Anatomia pliku tekstowego — część I (wstęp)
Plik tekstowy? Cóż może być prostszego niż plik tekstowy?
Ala ma kota.
Widziałem ludzi walących głową w monitor. Widziałem zmarnowaną prace i zmarnowane tysiące dolarów. Widziałem całe pokoje pracowników kręcących się w kółko.
Wszystko dlatego że ktoś założył bezmyślnie, że plik tekstowy to najprostsza rzecz na świecie.
W tym cyklu wpisów odkryjemy tajemnice plików tekstowych i poznamy pułapki, jakie czyhają między wierszami.
Czy jesteś programistą, czy nie, ręczę, że informacje tutaj zawarte będą dla Ciebie, drogi Czytelniku, przydatne. Nadejdzie dzień, kiedy poślesz mi bombonierkę w podziękowaniu za to, że przeczytane tutaj informacje uratowały Ci czas i pieniądze, a może i życie.
Zera i jedynki
Tak naprawdę, komputer nie zna liter, ani nawet — w przeciwieństwie do nas, dziesięciopalczastych ssaków — dziesięciu cyfr. Podstawą działania komputera jest elementarna dystynkcja, binarna opozycja: zero i jeden, prawda i fałsz, tak — tak, nie — nie, jin i jang, kobieta i mężczyzna, byt i niebyt. (Prawdę mówiąc, tak naprawdę komputer — jak każde urządzenie fizyczne — operuje na ciągłych wartościach sygnału, dopiero w naszych głowach istnieją progi, powyżej których sygnał interpretujemy jako jedynkowy, a poniżej — jako zerowy, ma to przerażające konsekwencje filozoficzne, dla naszego spokoju nie będziemy się w to wgłębiać).
Wszystko w komputerze ostatecznie zapisywane jest jako bardzo długi ciąg zer i jedynek: IX symfonia Beethovena, film z wakacji, program księgowy, zdjęcie gnijącego liścia. Również plik tekstowy.
A w jaki sposób zakodować za pomocą zer i jedynek litery składające się na plik tekstowy? Może tak: weźmy kształt litery i rozłóżmy go w rastrze.

Jeśli każde zaczernione pole będziemy kodować jedynką, a puste — zerem, otrzymamy kod 01101001100111111001 (powiedzmy, że idziemy wierszami od góry do dołu).
Nie, to nie jest dobry pomysł. Przecież chcemy — w prostym pliku tekstowym — abstrahować od kształtu liter. Nie chcemy, żeby binarne zakodowanie znaków zależało od tego, czy w edytorze mamy ustawiany taki czy inny font. To, czego potrzebujemy, to arbitralne kodowanie. Po prostu przypiszemy znakom arbitralne kody — arbitralne, czyli równie dobre, jak każde inne, nic w świecie fizycznym (ani nawet Bóg) nie nakazuje nam takiego czy innego kodowania, ważne, żebyśmy wszyscy razem je konsekwentnie stosowali. (Podobnie jak arbitralnie samogłoskę otwartą przednią niezaokrągloną oznaczamy kształtem A, spółgłoskę zaś zwartą dwuwargową dźwięczną — kształtem B).
Bity i bajty
Długie ciągi zer i jedynek są trochę nieporęczne. Dlatego sekwencje zer i jedynek (bitów) układa się w większe paczki po 8 sztuk, czyli bajty (właściwie należałoby mówić o oktetach, bo historycznie bajty nie zawsze były 8-bitowe, ale nie bądźmy drobiazgowi). Oto przykładowy bajt:
01001110
Taki bajt można traktować jako liczbę zapisaną dwójkowo (binarnie). Nie jest to oczywiście liczba „milion tysiąc sto dziesięć” (nie interpretujemy, broń Boże, 0 i 1 jako cyfr dziesiętnych), lecz inna. Jaka? Musimy oprzeć się na potęgach dwójki (zamiast dziesiątki):
0 * 27 + 1 * 26 + 0 * 25 + 0 * 24 + 1 * 23 + 1 * 22 + 1 * 21 + 0 * 20 =
= 0 * 2*2*2*2*2*2*2 + 1 * 2*2*2*2*2*2 + 0 * 2*2*2*2*2 + 0 * 2*2*2*2 + 1 * 2*2*2 + 1 * 2*2 + 1 * 2 + 0 * 1 =
= 0 * 128 + 1 * 64 + 0 * 32 + 0 * 16 + 1 * 8 + 1 * 4 + 1 * 2 + 0 * 1 =
= 64 + 8 + 4 + 2 =
= 78
więc 01001110 dla komputera to 78 dla małp człekokształtnych. Informatycy to trochę osobny gatunek — wolą zapis szesnastkowy niż dziesiętny (dlaczego? bo 16 jest potęgą dwójki, jedna cyfra szesnastkowa to dokładnie 4 bity, a jeden bajt to dokładnie dwie cyfry szesnastkowe). W zapisie szesnastkowym (heksadecymalnym, jak kto woli) używamy 10 cyfr arabskich plus 6 liter (A, B, C, D, E, F).
Nasz bajt zapisany szesnastkowo to:
4E
Dlaczego? Bo 4 * 16 + 14 * 1 = 78. (Skąd tutaj 14? A reprezentuje 10, B — 11, C — 12, D — 13, E — 14!).
Ile różnych liczb może reprezentować 8 bitów? „Ostatni” bajt to: 11111111, czyli dziesiętnie 255 (szesnastkowo: FF), a zatem mamy 256 (nie 255 — zaczynamy od zera!) możliwości. (Inaczej: bo 28 = 2*2*2*2*2*2*2*2 = 256, podobnie jak w Księdze przemian — też opartej na kodzie binarnym — mamy dla 6 bitów 26=64 możliwości).
Kod ASCII
Wróćmy do liter.
Ponieważ bajt jest podstawową (większą niż bit) jednostką, na której działa komputer, to jest rzeczą rozsądną reprezentować litery (i inne znaki) jako pojedyncze bajty. 256 możliwości wystarczy z naddatkiem dla małych i wielkich liter alfabetu łacińskiego (właściwie angielskiego — Rzymianie nie znali przecież J i W oraz nie odróżniali U i V).
Notabene, rozróżniamy tutaj małe i wielkie litery — małe „a” to inny znak niż duże „A” (a zatem znakom tym przypiszemy inne kody). Innymi słowy, „wielkość” (ładniej: kasztowość — zecer wyciągał małe litery z dolnej kaszty, wielkie zaś z górnej) liter ma znaczenie.
No dobrze, ale jakie konkretnie ośmiobitowe kody przypiszemy literom? Najpowszechniej stosowane obecnie kodowanie znaków to ASCII (American Standard Code for Information Interchange, czyli Amerykański Standardowy Kod na potrzeby Wymiany Informacji). Dawno temu istniały zupełnie inne standardy kodowania znaków (np. EBCDIC), ale odeszły już do lamusa.
W starych dobrych czasach (lata 80. i 90. ubiegłego wieku) umieszczanie na końcu tabelki kodów ASCII było rutynową metodą powiększania książki informatycznej (więcej stron, wyższa cena, więcej pieniędzy dla autora!). Ja też nie mogę się powstrzymać, by nie przytoczyć w całości tabeli kodów ASCII:
binarnie | dziesiętnie | szesnastkowo | znak |
---|---|---|---|
00000000 | 0 | 00 | znak specjalny |
00000001 | 1 | 01 | znak specjalny |
00000010 | 2 | 02 | znak specjalny |
00000011 | 3 | 03 | znak specjalny |
00000100 | 4 | 04 | znak specjalny |
00000101 | 5 | 05 | znak specjalny |
00000110 | 6 | 06 | znak specjalny |
00000111 | 7 | 07 | znak specjalny |
00001000 | 8 | 08 | znak specjalny |
00001001 | 9 | 09 | tabulacja |
00001010 | 10 | 0A | przejście do nowego wiersza |
00001011 | 11 | 0B | znak specjalny |
00001100 | 12 | 0C | znak specjalny |
00001101 | 13 | 0D | powrót karetki |
00001110 | 14 | 0E | znak specjalny |
00001111 | 15 | 0F | znak specjalny |
00010000 | 16 | 10 | znak specjalny |
00010001 | 17 | 11 | znak specjalny |
00010010 | 18 | 12 | znak specjalny |
00010011 | 19 | 13 | znak specjalny |
00010100 | 20 | 14 | znak specjalny |
00010101 | 21 | 15 | znak specjalny |
00010110 | 22 | 16 | znak specjalny |
00010111 | 23 | 17 | znak specjalny |
00011000 | 24 | 18 | znak specjalny |
00011001 | 25 | 19 | znak specjalny |
00011010 | 26 | 1A | znak specjalny |
00011011 | 27 | 1B | znak specjalny |
00011100 | 28 | 1C | znak specjalny |
00011101 | 29 | 1D | znak specjalny |
00011110 | 30 | 1E | znak specjalny |
00011111 | 31 | 1F | znak specjalny |
00100000 | 32 | 20 | spacja |
00100001 | 33 | 21 | ! |
00100010 | 34 | 22 | “ |
00100011 | 35 | 23 | # |
00100100 | 36 | 24 | $ |
00100101 | 37 | 25 | % |
00100110 | 38 | 26 | & |
00100111 | 39 | 27 | ’ |
00101000 | 40 | 28 | ( |
00101001 | 41 | 29 | ) |
00101010 | 42 | 2A | * |
00101011 | 43 | 2B | + |
00101100 | 44 | 2C | , |
00101101 | 45 | 2D | - |
00101110 | 46 | 2E | . |
00101111 | 47 | 2F | / |
00110000 | 48 | 30 | 0 |
00110001 | 49 | 31 | 1 |
00110010 | 50 | 32 | 2 |
00110011 | 51 | 33 | 3 |
00110100 | 52 | 34 | 4 |
00110101 | 53 | 35 | 5 |
00110110 | 54 | 36 | 6 |
00110111 | 55 | 37 | 7 |
00111000 | 56 | 38 | 8 |
00111001 | 57 | 39 | 9 |
00111010 | 58 | 3A | : |
00111011 | 59 | 3B | ; |
00111100 | 60 | 3C | < |
00111101 | 61 | 3D | = |
00111110 | 62 | 3E | > |
00111111 | 63 | 3F | ? |
01000000 | 64 | 40 | @ |
01000001 | 65 | 41 | A |
01000010 | 66 | 42 | B |
01000011 | 67 | 43 | C |
01000100 | 68 | 44 | D |
01000101 | 69 | 45 | E |
01000110 | 70 | 46 | F |
01000111 | 71 | 47 | G |
01001000 | 72 | 48 | H |
01001001 | 73 | 49 | I |
01001010 | 74 | 4A | J |
01001011 | 75 | 4B | K |
01001100 | 76 | 4C | L |
01001101 | 77 | 4D | M |
01001110 | 78 | 4E | N |
01001111 | 79 | 4F | O |
01010000 | 80 | 50 | P |
01010001 | 81 | 51 | Q |
01010010 | 82 | 52 | R |
01010011 | 83 | 53 | S |
01010100 | 84 | 54 | T |
01010101 | 85 | 55 | U |
01010110 | 86 | 56 | V |
01010111 | 87 | 57 | W |
01011000 | 88 | 58 | X |
01011001 | 89 | 59 | Y |
01011010 | 90 | 5A | Z |
01011011 | 91 | 5B | [ |
01011100 | 92 | 5C | \ |
01011101 | 93 | 5D | ] |
01011110 | 94 | 5E | ^ |
01011111 | 95 | 5F | _ |
01100000 | 96 | 60 | ` |
01100001 | 97 | 61 | a |
01100010 | 98 | 62 | b |
01100011 | 99 | 63 | c |
01100100 | 100 | 64 | d |
01100101 | 101 | 65 | e |
01100110 | 102 | 66 | f |
01100111 | 103 | 67 | g |
01101000 | 104 | 68 | h |
01101001 | 105 | 69 | i |
01101010 | 106 | 6A | j |
01101011 | 107 | 6B | k |
01101100 | 108 | 6C | l |
01101101 | 109 | 6D | m |
01101110 | 110 | 6E | n |
01101111 | 111 | 6F | o |
01110000 | 112 | 70 | p |
01110001 | 113 | 71 | q |
01110010 | 114 | 72 | r |
01110011 | 115 | 73 | s |
01110100 | 116 | 74 | t |
01110101 | 117 | 75 | u |
01110110 | 118 | 76 | v |
01110111 | 119 | 77 | w |
01111000 | 120 | 78 | x |
01111001 | 121 | 79 | y |
01111010 | 122 | 7A | z |
01111011 | 123 | 7B | { |
01111100 | 124 | 7C | | |
01111101 | 125 | 7D | } |
01111110 | 126 | 7E | ~ |
01111111 | 127 | 7F | znak specjalny |
(Znaki specjalne to niedrukowalne znaki sterujące, nie musimy się nimi w tej chwili przejmować).
Jeszcze raz powtórzmy, kod jest arbitralny — to, że A ma kod 65, to wynik przypadkowych decyzji amerykańskich standaryzatorów w latach 60.
Nasuwają się dwa pytania: dlaczego wykorzystano tylko 128 możliwości spośród 256 oferowanych przez 8 bitów? i co z polskimi znakami?
128 możliwości, bo ASCII jest kodem 7-bitowym (nie 8-bitowym!). Po prostu w zamierzchłych czasach, gdy powstawał kod ASCII, istniały jeszcze urządzenia 7-bitowe, więc twórcy tego standardu postanowili się ograniczyć do 7 bitów. Ludzkość miała od tego czasu różne pomysły, jak wykorzystać te pozostałe 128 kodów (od 128 do 255) i często te 8-bitowe kodowania określa się mianem ASCII, ale to nadużycie (bo precyzyjniej były to rozszerzenia ASCII).
A polskie znaki (ą, ć, ę, ł, ń, ó, ś, ź, ż i ich odpowiedniki z górnej kaszty)? Niestety, informatyka rozwinęła się w USA, gdzie nikt nie przejmował się polskimi diakrytykami (nie obrażajmy się — ani grażdanką, ani hangulem, ani nawet hebrajszczyzną). Gdyby II wojna światowa wybuchła parę lat później niż wybuchła, Rejewski, Różycki, Zygalski, Ulam, Tarski i polscy radioinżynierowie skonstruowaliby pierwszy komputer i mielibyśmy standard z polskimi znakami, niestety losy potoczyły się inaczej.
Łącząc obie kwestie, naturalny pomysł to upchać polskie znaki wśród kodów od 128 do 255. To rzeczywiście uczyniono (na dwadzieścia parę sposobów, niech żyje tradycyjna polska anarchia!). Poznamy jednak lepszy standard, dzięki któremu możemy pisać teksty nie tylko z ogonkami, lecz także z (prawie) wszystkimi znakami świata. Wrócimy do sprawy w III odcinku naszego cyklu.
Weźmy prosty plik tekstowy bez ogonków:
Ala ma kota.
Jak komputer widzi ten plik? A tak:
010000010110110001100001001000000110110101100001001000000110101101101111011101000110000100001010
Mało czytelnie? Zaznaczmy teraz chociaż granice bajtów:
01000001|01101100|01100001|00100000|01101101|01100001|00100000|01101011|01101111|01110100|01100001|00001010
Albo zapiszmy bajty dziesiętnie:
65, 108, 97, 32, 109, 97, 32, 107, 111, 116, 97, 10
i szesnastkowo:
41, 6C, 61, 20, 6D, 61, 20, 6B, 6F, 74, 61, 0A
Zgadza się? Proszę po kolei skonfrontować liczby z tabelką — powinno się zgadzać.
Zauważmy, że spacja też jest traktowany jako osobny znak (tak jak litera) i ma swój kod (32 dziesiętnie). Na końcu naszego pliku jest jeszcze tajemniczy znak o kodzie 10 — to znak końca wiersza. Z końcem wiersza sprawa jest bardziej skomplikowana, poświecimy temu osobny odcinek naszego cyklu.
Szesnastkowy zrzut
Jak poprosić komputer, żeby sprawdził, jak dokładnie zakodowany jest plik tekstowy? (Jest to pierwsza rzecz, jaką powinniśmy zrobić, jeśli mamy jakiś tajemniczy problem z plikiem tekstowym — być może komputer widzi go inaczej niż my). W systemie Linux służy do tego…
Tu dygresja: ludzie, którzy profesjonalnie przetwarzają tekst, pracują pod Linuksem. Po prostu przetwarzanie tekstu w Windowsie to udręka, to jak wykuwanie miecza w kaftanie bezpieczeństwa. Jeśli nie masz Linuksa pod ręką, czytaj sobie spokojnie dalej, ale warto pomyśleć w wolnej chwili o zainstalowaniu tego systemu operacyjnego.
… więc w Linuksie do zrzucania zawartości pliku w postaci szesnastkowej służy polecenie hexdump
(„hex” — „heksadecymalnie”, „dump” — „zrzut”), najlepiej z opcją -C
, która wyświetli szesnastkowe kody bajt po bajcie. Oto przykładowe komendy wydane z wiersza poleceń:
# tworzymy na szybko plik tekstowy
$ echo 'Ala ma kota' > plik.txt
# zrzucamy szesnastkowo
$ hexdump -C plik.txt
00000000 41 6c 61 20 6d 61 20 6b 6f 74 61 0a |Ala ma kota.|
0000000c
W pierwszej kolumnie podano adres (też zapisany szesnastkowo), ale potem zgadza się z tym, co podałem wyżej.
Plik tekstowy w zapisie szesnastkowym (który szybko można przełożyć na zapis binarny) może pokazać każdy przyzwoity edytor tekstu. Na przykład w moim ulubionym edytorze Emacs służy do tego polecenie hexl-mode
. Wygląda to tak (pierwszy wiersz zawiera numery bajtów zapisane szesnastkowo):

W następnym odcinku: piekło końca wiersza.