Zapisz jako PDF
Transkrypt
Zapisz jako PDF
TI:WTBD/Ćwiczenia 1 Do zabawy używaliśmy m. in. plików Plik:PanTadeusz1.txt i Plik:PanTadeusz1 Latin2.txt. Dla rozgrzewki i jako narzędzie do dalszej zabawy napisaliśmy moduł z funkcjami: konwertującą liczbę (int) na "bity" (jej zapis dwójkowy jako napis składający się z zer i jedynek), i funkcję do niej odwrotną. Oto pełny tekst kodu modułu: #! /usr/bin/python # coding: utf-8 # util.py def tobits(N): s = list() while N: N, r = N / 2, N % 2 s.append('%i' % r) s.reverse() return ''.join(s) def frombits(bits): res = for c in bits: if c not in '01': raise ValueError('not bits!') res *= 2 res += int(c) return res Funkcji tych użyliśmy do przyjrzenia się, jak wyglądają sekwencje wielobajtowe reprezentujące znaki spoza zakresu ASCII w kodowaniu UTF-8. Informacje o zasadzie kodowania UTF-8 można znaleźć np. w podręczniku systemowym (man utf-8). Oczywiście tak naprawdę nie było potrzeby pisania tych funkcji samemu. Proszę znaleźć sposób uzyskania tych konwersji wprost za pomocą wbudowanych funkcji/metod. Napisaliśmy dwie funkcje odtwarzające zasadniczą funkcjonalność standardowego polecenia wc, różnią się one sposobem wykonania operacji wejścia. Obie nic nie wiedzą o kodowaniach tekstu, traktując zawartość pliku jako strumień bajtów. def wc0(plik): '''zwraca (linie, słowa, znaki); ta wersja wc nic nie wie o kodowaniach, po prostu liczy bajty. ''' try: s = plik.read() except AttributeError: s = open(plik).read() #return len(s.split('\n')), len(s.split()), len(s) # tradycyjne wc liczy wystąpienia '\n', co często nie zgadzałoby się o 1 return s.count('\n'), len(s.split()), len(s) def wc1(plik): '''na sposób linijka-po-linijce''' try: f = open(plik) except TypeError: f = plik l, w, c = , , for line in f: l += 1 w += len(line.split()) c += len(line) return l, w, c Warto czasami sprawdzić, czy "ulepszona" implementacja nie daje innych wyników, niż oryginalna. Zwłaszcza, że z komentarza w pierwszej wersji wc wynika, że czasami wyniki tych funkcji mogą różnić się o 1 (dla liczby linijek) -- w jakich okolicznościach, i jak to poprawić? def test(plik): if wc0(plik) == wc1(plik): print 'OK' else: print 'ERROR!' Kolejna wersja zakłada, że plik jest zapisem tekstu w kodowaniu UTF-8. def wc(plik, enc='utf-8'): '''ta wersja zakłada że plik jest w kodowaniu utf-8 ''' try: s = plik.read() except AttributeError: s = open(plik).read() s = s.decode(enc) return s.count('\n'), len(s.split()), len(s) Ta wersja cierpi na tę słabość, że rzuci wyjątkiem jeśli treść pliku nie jest legalnym strumieniem UTF-8. Jeżeli chcemy stworzyć program bezpośrednio wykonywalny, to potrzebny jest jeszcze blok mówiący o tym, co ma się stać w wyniku jego wywołania. Do tego musimy oczywiście dołączyć jeszcze w treści pliku programu którąś z wersji definicji funkcji wc. if __name__ == '__main__': from sys import argv, stdin args = argv[1:] if args: reslines = list(wc(plik) + (plik,) for plik in args) else: reslines = [wc(stdin) + ('-',)] if len(reslines) > 1: reslines.append(tuple(sum(t) for t in zip(*reslines)[:3]) + ('RAZEM',)) #print reslines # jeszcze trzeba to umiejętnie sformatować reslines = list(tuple(str(x) for x in line) for line in reslines) maxl = tuple(max(len(s) for s in col) + 1 for col in zip(*reslines)) fmt = '%%%ds%%%ds%%%ds %%s' % maxl[:3] for line in reslines: print fmt % line Co tu się dzieje? 1. Dobieramy się do listy argumentów z linii poleceń (pomijając zerowy, będący nazwą samego programu) i traktujemy te argumenty jako nazwy plików. 2. Iterujemy po elementach listy argumentów jeśli jest niepusta. WPP czytamy zawartość stdin i pracujemy na niej. Uwaga, iteracja jest wykonana nie za pomocą pętli for, a generatora. 3. W przypadku gdy lista wynikowa ma długość > 1, dołączamy jeszcze jeden wiersz z podsumowaniem. 4. Formatujemy wynik w zgrabną tabelkę o wyrównanych kolumnach. W tym celu obliczamy, jaką szerokość trzeba zarezerwować na każdą z 3 pierwszych kolumn, i na tej podstawie tworzymy napis formatujący fmt, służący do sformatowania linijek.