utf8 && locale

Perl má podporu pre UNICODE a rôzne znakové sady ale napísať kód ktorý pracuje správne s UTF-8 ešte nieje veľmy primočiare a jednoduché. Stále prichádzajú znova a znova otázky ohľadne tejto témy. Jan poslal otázku na mailing list, nesôr som čítal podobnú otázku diskutovanú na CZ/SK Debian mailing liste. Tak sa na to pozrime na príklade:

#!/usr/bin/perl 

my @pole=qw(šiška marek ucho čaj žička);

@pole=sort(@pole);           # utriediť pole

foreach my $a (@pole) {
    $a =~ s/\W//g;           # vymazať všetky "neslovné" znaky
    print "$a\n";
}

open my $handle, ">file.txt" or die "Can't write to file.txt: $!";
print $handle "\Uěščřžabcd\E\n";     # vypíš znaky ako veľké písmená
close $handle;

Nám vypíše:

marek 
ucho 
aj 
ika
ika

ěščřžABCD

Veľa chybyčiek krásy a výsledok zrovna nieje to čo sme čakali. čaj, šiška and žička sú na konci a neboli utriedené dobre, ěščřž zostalo ako bolo a písmená s diakritikou neboli považované za znaky slov.

Naučme Perl abecedu

Najskôr, Perl nepracuje správne so stringom keďže triedenie a pattern matching nepoužilo všetky znaky ako malo. To je preto, lebo nie všetky lazyky používajú tú istú abecedu. Kým niektoré jazyky môžu mať (a majú samozrejme) tie isté znaky, triedenie môže byť iné. Perl nevie v ako jazyku je string napísaný takže ich nevie správne utriediť. Aby sme mu to "ukázali" musíme mu povedať aby používal špecifickú "locale" (abecedu), potom bude vedieť ktoré znaky sú písmená a aké majú poradie. Toto sa udeje pomocout use locale;, keď to pridáme výstup vyzerá následovne:

aj 
marek 
ika 
ucho 
ika 
ěščřžABCD

Parsovanie zdrojového kódu v rôznych jazykoch

V predchádzajúcom odstavci sme si ukázali ako naučiť Perl abecedu. Problémom je, že Perl stále nevie ako prečítať náš text. Vie prečítať string zo súborov a STDIN ale nie zo svojho zdrojového kódu. To je preto, že Perl počíta s tým že zdrojový kód je v default encoding ktoré NIEJE UTF-8 (asi ASCII alebo Latin-1, niesom si istý). Aby sme to opravili musíme mu povedať v akom kódovaní je zdrojový kód. Toto sa dá spraviť troma spôsobmi:

Pridaním use utf8;. Perl prečíta zdrojový kód ako UTF-8 súbor, takže "prijme" všetky znaky s diakritikou a ostatné znaky definované v UTF-8 znakovej sade.

Pridaním use encoding 'utf8';. Táto pragma má výhodu tohom že sa dá špecifikovať kódovanie aj iné ako utf8.

Používaním escape sequences pre všetky non ASCII znaky. Napríklad, na napísanie Á sa dá použiť "\x{C1}" čo korešponduje s UNICODE znakom pre LATIN CAPITAL LETTER A WITH ACUTE. Toto je celkom univerzálne, zdroják je celý v ASCII ale vcelku sa to zle číta a udržiava.

Keď je zdrojový súbor načítaný v správnom kódovaní tak máme dobrý výsledok aj na výstupe.

Wide character in print at ./utf8-test.pl line 15.
čaj 
marek 
Wide character in print at ./utf8-test.pl line 15.
šiška 
ucho 
Wide character in print at ./utf8-test.pl line 15.
žička 
Wide character in print at ./utf8-test.pl line 18.
ĚŠČŘŽABCD

I/O v UTF-8

Teraz je text vypísaný ako sme očakávali. Ale stále máme problém s výpisom UTF-8 znakov, STDOUT neočakáva UTF-8. To sa dá ošetriť rôznymi spôsobmi:

Prvé riešenie je pridať use encoding 'utf8';, to povie Perl-u, že zdrojový kód je v UTF-8 a zároveň nastaví PerlIO layers pre STDIN a STDOUT na UTF-8. V podstate sú use encoding 'utf8'; a use utf8; skoro rovnaké okrem toho že ten prvý nastavý kódovanie pre STDIN a STDOUT (STDERR zostáva nezmenený).

Druhá alternatíva je manuálne nastaviť kódovanie pre STDOUT pomocou binmode(STDOUT => ':encoding(utf8)'). To sa dá uplatniť na všetky file handle and nieje limitované pre STDOUT. Je to asi najlepšie riešenie, lebo všetky ostatné PerlIO zostanú nezmenené aj keď sa použije use encoding.

A nakoniec, kódovanie pre PerlIO layer sa dá nastaviť priamo pri otváraní súboru pomocou open. Napríklad nasledujúci kód open my $handle, "<:utf8", "file" or die "Can't read file: $!"; otvorí súbor v UTF-8.

Keď povieme Perl-u aby robil I/O v UTF-8 všetko je v poriadku:

čaj 
marek 
šiška 
ucho 
žička 
ĚŠČŘŽABCD

Nuž a pre úplnosť tu je finálny test script:

#!/usr/bin/perl 

use warnings;
use strict;

# Utrieď string podľa aktuálneho kódovania a používanej abecedy
use locale;

# načítaj zdrojovy kód ak UTF-8 a nastav STDOUT a STDIN na UTF-8
use encoding 'utf8';


# môžeme písať UTF-8 znaky v kóde vďaka "use encoding"
my @pole=qw(šiška marek ucho čaj žička);

# sort rozpoznáva všetk znaky abecedy vďaka "use locale"
@pole=sort(@pole);           # sort array

foreach my $a (@pole) {
    # pattern matching funguje vďaka "use locale"
    $a =~ s/\W//g;           # vymazať všetky "neslovné" znaky
    print "$a\n";            # vypíš prvok poľa
}

# zapíš UTF-8 do súboru
open my $handle, ">:utf8", "file.txt" or die "Can't write to file.txt: $!";
# vieme zneniť znaky na veľké písmená vďaka "use locale"
print $handle "\Uěščřžabcd\E\n";     # vypíš znaky ako veľe písmená
close $handle;

Poznánky

Keď chceme písať znaky iné ako Latin-1 do Pod dokumentácie treba nastaviť kódovanie nasledovne:

=encoding utf-8

Linky: utf8 - Perl pragma to enable/disable UTF-8 (or UTF-EBCDIC) in source code, locale - Perl pragma to use and avoid POSIX locales for built-in operations, perlunitut - Perl Unicode Tutorial, perluniintro - Perl Unicode introduction, perlunicode - Unicode support in Perl

29. Jun 2008
Jozef

03. Aug 2008
Emmanuel