Jedním z nejjednodušších elektronických dokumentů je prostý textový soubor. I když to tak nemusí na první pohled vypadat, i prosté textové soubory skrývají některé záludnosti, pokud je chceme editovat a přenášet mezi různými platformami. Jelikož jsou i webové stránky a TeXové zdrojové texty čistě textové soubory, je dobré být si těchto úskalí vědom.
Přejít: navigace | na začátek stránky | na konec stránky
Znak konce řádků
Znak, který v textovém souboru signalizuje konec řádku (často označováno EOL), je na různých platformách různý, z těch nejrozšířenějších:
- Windows: dvojice znaků CR+LF
- Unix (tedy včetně Apple MacOS od verze X): znak LF
- Apple MacOS do verze 9 včetně: znak CR
Znaky CR a LF mají kořeny v technologii dálnopisných terminálů:
-
Znak LF (Line Feed; ASCII kód 0x0A,
v programovacích jazycích často reprezentován sekvencí
\n
) byl interpretován jako posun papíru o řádek vzhůru. -
Znak CR (Carriage Return; ASCII kód 0x0D,
v programovacích jazycích často reprezentován sekvencí
\r
) byl interpretován jako návrat vozíku s papírem nebo tiskovou hlavou.
Přejít: navigace | na začátek stránky | na konec stránky
Praktické úkoly k procvičení
Vytvořte si v libovolném textovém editoru několikařádkový textový soubor. Ověřte, jaký znak konce řádků je použit:
-
Linux:
$ cat plaintext.txt
Z výpisu nástroje hexdump je vidět, že soubor byl pravděpodobně vytvořen na unixovém systému, protože konce řádků jsou reprezentovány bajty s hodnotou 0a (ve výpisu zvýrazněné).
prvni radek souboru
druhy radek souboru
$ hexdump -C plaintext.txt
00000000 70 72 76 6e 69 20 72 61 64 65 6b 20 73 6f 75 62 |prvni radek soub|
00000010 6f 72 75 0a 64 72 75 68 79 20 72 61 64 65 6b 20 |oru.druhy radek |
00000020 73 6f 75 62 6f 72 75 0a |souboru.|
00000028 -
Ve Windows je možné nad souborem plaintext.txt použít
například integrovaný prohlížeč správce souborů
Altap Salamander:
Nabídka Start → All Programs → Altap Salamander
vybrat soubor plaintext.txt
Soubory → Zobrazit (F3)
Zobrazit → Šestnáctkově (Ctrl + H)
Následně soubor konvertujte do podoby obvyklé na ostatních platformách.
- Linux: použijte konverzí programy dos2unix, mac2unix, unix2dos a unix2mac.
-
Windows: použijte Altap Salamander:
Nabídka Start → All Programs → Altap Salamander
vybrat soubor plaintext.txt
Soubory → Převést (Ctrl + K)
Vyzkoušejte si, jaké problémy může přinést použití chybného znaku konce řádku:
-
V libovolném textovém editoru vytvořte jednoduchý skript pro
unixový shell Bash:
#!/bin/bash
echo 'Hello World!'
exit 0 - Uložte skript v kódování UTF-8 s Windows reprezentací konců řádků znaky CR+LF do souboru skript.sh.
-
Na unixovém systému nastavte souboru práva pro spuštění
a pokuste se skript spustit:
$ chmod u+x skript.sh
Proč došlo k uvedené chybě?
$ ./skript.sh
bash: ./skript.sh: /bin/bash^M: chybný interpretr: Adresář nebo soubor neexistuje
TeX se k znakům konce řádku staví rozumně a CR+LF, LF i CR jsou během čtení souboru nahrazeny znakem, který se nachází v registru \endlinechar (implicitně CR). HTML je k použitému znaku konce řádku rovněž lhostejný.
Přejít: navigace | na začátek stránky | na konec stránky
Kódování textu
Daleko větší problémy než různé znaky ukončení řádků však přináší rozdíly v kódování textu, tzn. interpretaci významu bajtů v textovém souboru.
Přejít: navigace | na začátek stránky | na konec stránky
Jednobajtová kódování
V současné době je nejzákladnějším standardem pro kódování textu na nejrozšířenějších platformách kód ASCII. Ten však nepokrývá potřeby většiny jazyků krom angličtiny, má jen velmi omezené pokrytí speciálních znaků apod.
Tip: Pomocí ASCII escape sekvencí ve zdrojovém textu umožňuje TeX sázet i různé cizokrajné znaky. V případě nedostupnosti vhodného znaku v použitém fontu je však výsledek vytvořen složením samotatného znaku písmene (např. d) a znaku akcentu (např. ˇ). Výsledek strojové kombinace obou znaků však často nemusí dávat optimální optický výsledek v porovnání s ručně optimalizovanou kresbou znaku (nevhodné zarovnání na osu znaku, potřeba zcela odlišné kresby háčku při kombinaci s písmenem d než při kombinaci s písmenem c apod.). K tomu řešení se tedy přistupuje jen v nouzi.
Historicky se pro potřeby neanglických jazyků začala nejdříve používat různá jednobajtová kódování. Vznikly různé standardní kódové stránky, které specifikovaly, jak interpretovat který bajt v textovém souboru. Pro češtinu se mimo jiné používalo kódování
- CP1250 na Windows,
- ISO-8859-2 na unixových systémech,
- Mac CE na starších verzích Mac OS.
Problém je, že jednobajtové kódování má vždy k dispozici pro jeden znak právě jeden bajt. Není tedy možné v jedné kódové stránce mít více než 256 různých znaků, což je pro některé jazyky značně omezující (extrémním příkladem může být například tradiční čínština používající tisíce různých znaků).
Informace o použité kódové stránce navíc není v čistě textovém souboru nikde uvedena. Zpracovatel tedy musí vědět, která kódová stránka byla použita, jinak dojde k chybné interpretaci textu.
Tip:
Existují nástroje, které mohou zkusit kódování textu
odhadnout. Jedním z nich je např. program
enca.
Jinou možností pro detekci kódování je např.
file -bi.
Kvůli chybějící indikaci není ani možné používat více kódových stránek v jednom souboru (např. pro zakódování textu ve dvou různých jazycích, kdy každý z nich vyžaduje jiné jednobajtové kódování).
Přejít: navigace | na začátek stránky | na konec stránky
Unicode
Problémy jednobajtových kódování řeší standard Unicode. Zjednodušeně si jej můžeme představit jako seznam dvojic číslo:znak, kde je každému znaku přiřazeno nějaké jednoznačné identifikační („katalogové“) číslo. Například znak š má přiděleno číslo 353, znak – (pomlčka) číslo 8211 atd.
Tip: Velmi užitečnou aplikací pro vyhledávání informací o Unicode znacích a hledání znaků samotných je webová aplikace UniView.
Povšimněte si, že počet znaků, které Unicode pokrývá, mnohonásobně převyšuje 256 možností zakódovatelných do jediného bajtu – například ona pomlčka (–) už je spolehlivě mimo tento rozsah. Musí tedy být definován i nějaký způsob, jak vysoká „katalogová“ čísla znaků uložit jako posloupnost bajtů do souboru (k zakódování čísel větších než 255 už nám nestačí jeden bajt, takové číslo musí být uloženo v několika bajtech). Existuje několik způsobů Unicode kódování:
-
Nejjednodušší přístup je vyhradit pro každý znak pevný
a dostatečně velký počet bajtů, aby tak bylo možné uložit i velká
„katalogová“ čísla znaků. Tento postup reprezentuje např.
Unicode
Transformation Format UTF-32, kde je každý znak reprezentován
právě čtyřmi bajty (tzn. 32 bity, odtud název UTF-32).
- Tento postup však značně plýtvá místem, i nízká „katalogová“ čísla znaků jsou uložena na celých čtyřech bajtech, z nich většina je ale nulová.
- Je problém s různou endianitou různých platforem – protože je číslo uloženo ve více bajtech, musí být možné nějak zjistit, na které straně čtveřice bajtů v souboru je uložen nejvíce/nejméně významný bajt čísla. Tento problém se řeší přidáváním BOM na začátek souboru, což ale může vést k problémům při zpracování souboru programy, které na to nejsou připravené.
Příklad – endianita: Mějmě (desítkově zapsaná) čísla (mezery ve všech zápisech v příkladu jsou přidány jen pro přehlednost):
- 1 245 391 901
- 379 976 145
Šestnácková reprezentace čísel pak je:
- 4A 3B 2C 1D
- 16 A5 F9 D1
Binární reprezentace čísel pak je:
- 0100 1010 0011 1011 0010 1100 0001 1101
- 0001 0110 1010 0101 1111 1001 1101 0001
Little-endian posloupnost bajtů v souboru při zápisu obou čísel za sebou:
- 1D 2C 3B 4A D1 F9 A5 16
Big-endian posloupnost bajtů v souboru při zápisu obou čísel za sebou:
- 4A 3B 2C 1D 16 A5 F9 D1
-
Propracovaným, a asi nejrozšířenějším Unicode kódováním
textových souborů je kódování
UTF-8.
-
Šetří místo – jednotlivé znaky jsou ukládány na jednom
až čtyřech bajtech tak, aby nebyly zbytečně ukládány
vedoucí nulové bajty. Počet bajtů použitých pro kódování
jednoho znaku je signalizován několikabitovým prefixem
ve všech bajtech použitých pro kódování daného znaku
(viz např.
příklad na Wikipedii).
- Takový postup navíc pomáhá s hledáním hranic jednotlivých znaků při náhodném přístupu doprostřed textového souboru, při ztrátě části obsahu souboru (vinou chyby při přenosu po síti, chyby při čtení z disku, …) apod.
- Díky tomu je UTF-8 také neutrální z hlediska endianity, čímž odpadají problémy s BOM. (Pozor, zejména na Windows některé programy vkládají BOM značky i do UTF-8 kódovaných souborů, což často způsobuje problémy, protože BOM u UTF-8 nemá žádný praktický význam a mnoho programů zpracovávajících UTF-8 soubory s BOM nepočítá.)
- Prvních 128 znaků v UTF-8 binárně jedna ku jedné odpovídá základnímu ASCII, což je výhodné z hlediska kompatibility – různé jednoduché textové soubory (např. konfigurační soubory), které si vystačí se základním ASCII, jsou „zdarma“ korektně kódovány v UTF-8 a naopak.
-
Šetří místo – jednotlivé znaky jsou ukládány na jednom
až čtyřech bajtech tak, aby nebyly zbytečně ukládány
vedoucí nulové bajty. Počet bajtů použitých pro kódování
jednoho znaku je signalizován několikabitovým prefixem
ve všech bajtech použitých pro kódování daného znaku
(viz např.
příklad na Wikipedii).
Příklad – kódování UTF-8: Mějme následující Unicode znak:
Tento znak má několik možných reprezentací. Jedna možná reprezentace sestává ze tří samostatných grafémů:
- A – Latin Capital Letter A (číslo grafému: 65)
- ◌̊ – Combining Ring Above (číslo grafému: 778)
- ◌́ – Combining Acute Accent (číslo grafému: 769)
Binární reprezentace čísel jednotlivých grafémů je následující:
- 100 0001
- 11 0000 1010
- 11 0000 0001
Kódování UTF-8 nám jednoznačné zadává, jak binární reprezentaci grafémů zakódovat:
- 0100 0001
- 1100 1100 1000 1010
- 1100 1100 1000 0001
Znak Ǻ tedy zakódujeme následující posloupností pěti bajtů:
- 41 CC 8A CC 81
Jak jsme zmínili, existuje několik reprezentací znaku Ǻ. Namísto výše uvedené trojice grafémů můžeme např. přímo použít grafém Latin Capital Letter A with Ring Above and Acute (grafém číslo 506), který zakódujeme pomocí dvou bajtů C7 BA.
Toto nám činí problémy např. tehdy, když chceme v textu najít
konkrétní slovo. Prohlížeče a editory textových dokumentů totiž
většinou pracují na úrovni grafémů, ne znaků, a jinak
reprezentované slovo nemusí najít. Abychom se tomuto problému
vyhnuli, můžeme reprezentaci znaků v dokumentu sjednotit pomocí tzv.
Unicode
normalizace implementované např. nástrojem
uconv:
$ printf '\x41\xCC\x8A\xCC\x81' | hexdump -C
00000000 41 cc 8a cc 81 |A....|
00000005
$ printf '\x41\xCC\x8A\xCC\x81' | uconv -x any-nfc | hexdump -C
00000000 c7 ba |..|
00000002
Přejít: navigace | na začátek stránky | na konec stránky
Praktické úkoly k procvičení
Prohlédněte si odkazované články o jednobajtových kódováních a porovnejte rozdíly mezi kódovými stránkami.
Vyzkoušejte si, jak vypadá chybná interpretace textu při chybně zvoleném kódování v programu text zpracovávajícím.
-
Vytvořte si v libovolném textovém editoru (plain textovém
editoru, tzn. programy typu Poznámkový blok, Vim, Visual Studio Code
apod., nikoliv v textovém procesoru typu MS Word, LibreOffice
Writer apod., které text ukládají ve složitém binárním formátu) soubor
plaintext-cp1250.txt s jediným řádkem obsahujícím text
Šíleně žluťoučký kůň úpěl ďábelské ódy.
a uložte jen v kódování Windows CP1250.-
Visual Studio Code pod Windows:
Soubor → Nový textový soubor
V pravém dolním rohu: UTF-8 → Central European (Windows 1250)
Soubor → Uložit jako → plaintext-cp1250.txt -
Vim na libovolné platformě:
:w ++enc=cp1250
-
Visual Studio Code pod Windows:
- Otevřete si takto vytvořený soubor ve webovém prohlížeči, kde je snadné ručně zvolit kódování zpracovávaného souboru. Např. v prohlížeči Google Chrome si za tímto účelem můžete nainstalovat rozšíření Set Character Encoding. Vyberte nejdříve kódování CP1250 (další možné názvy zahrnují „Windows 1250“ a „Central European“). Text by měl být zobrazen správně. Následně vyberte jiná kódování a podívejte se, jak je obsah souboru prohlížečem interpretován.
Převeďte soubor plaintext-cp1250.txt do Unicode v kódování UTF-8.
- Prohlédněte si, jak je vypadá binární reprezentace českého textu v plaintext-cp1250.txt v kódování CP1250. Viz výše.
-
Převeďte soubor do UTF-8.
-
Visual Studio Code pod Windows:
Soubor → Otevřít soubor… → plaintext-cp1250.txt
V pravém dolním rohu: UTF-8 → Znovu otevřít s kódováním → Central European (Windows 1250)
V pravém dolním rohu: Central European (Windows 1250) → Uložit s kódováním → UTF-8
Soubor → Uložit jako → plaintext-utf8.txt -
Vim na libovolné platformě:
:e ++enc=cp1250
:w ++enc=utf8 -
Linux:
iconv -f cp1250 -t utf8 --output plaintext-utf8.txt plaintext-cp1250.txt
-
macOS:
iconv -f CP1250 -t UTF-8 < plaintext-cp1250.txt > plaintext-utf8.txt
-
Visual Studio Code pod Windows:
- Prohlédněte si, jak vypadá binární reprezentace českého textu v plaintext-utf8.txt po převodu do UTF-8. Viz výše.
- Vyzkoušejte také další jednobajtová/Unicode kódování a prohlédněte si výslednou binární reprezentaci souboru.
- Vyzkoušejte chování různých konverzních programů při pokusu o konverzi textu obsahujícího znaky, které v cílovém (jednobajtovém) kódování nemají reprezentaci (cílová kódová stránka nemá pro daný znak žádnou reprezentaci).
-
Pokud pracujete na Unixu, vyzkoušejte si, jaké problémy může
přinést použití BOM
u textového souboru v kódování UTF-8.
-
Soubor skript.sh
z předchozího
cvičení uložte s unixovou reprezentací znaků konce
řádků, ale s BOM.
-
Vim na libovolné platformě:
:set fileformat=unix
:set bomb
:w ++enc=utf8
-
Vim na libovolné platformě:
-
Na unixovém systému nastavte souboru práva pro spuštění
a pokuste se skript spustit:
$ chmod u+x skript.sh
Proč došlo k uvedené chybě?
$ ./skript.sh
./skript.sh: řádek 1: #!/bin/bash: Adresář nebo soubor neexistuje
Hello World!
-
Soubor skript.sh
z předchozího
cvičení uložte s unixovou reprezentací znaků konce
řádků, ale s BOM.