Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Unicode-pitanja obrade u Perl-u i kako se nositi sa tim

Gomila perldoc manpages-a ocrtavaju i objašnjavaju podršku Perl-ovog unicode-a. perluniintro, perlunicode, Encode module, binmode() funkcija. A spisak nije završen. Glavni problem sa ovom dokumentacijom je obim. Većina programera čak ne moraju sve to ni da čitaju,jer,da biste počeli da radite sa Unicode-om,sam vam je potrebno da znate neke osnovne činjenice i pravila.

Ja sam iskusio nekoliko vrsta teškoća sa Unicode-om u Perl-u, u nekoliko projekata. Dva glavna problema koje sam video su:

  • UTF-8 podaci postaju duplo kodirani ili drugi podaci kodiranja postaju raskomadani
  • Upozorenje “Wide character in print” (“Široki karakter u štampi”)

Ova dva problema su blisko povezani i često se rešavaju sličnim potezima.

Čitanje ili bar pretraživanje kroz related manpages (odnosne man stranice) je i dalje dobar način za razumevanje i rešavanje vaših problema sa Unicode-om. Ako sada za to nemate vremena,nastavite da čitate.

Problem showcase (“izlog”): primer

Zamislite dve proste varijable sa Unicode tekstom u tome. I štamapate te varijable na standardni output. Šta bi moglo biti lakše?..

    #!/usr/bin/perl
    
    my $ustring1 = "Hello \x{263A}!\n";  
    my $ustring2 = <DATA>;
    
    print "$ustring1$ustring2";
    __DATA__
    Hello ☺!
    

    source

Obe varijable ovde sadrže iste podatke: string(niz) "Hello " kojeg prati Unicode karakter WHITE SMILING FACE (BELO NASMEJANO LICE) U+263A, znak uzvika i karakter novog reda. __DATA__ part ($ustring2) je UTF-8 kodiran.

Ali,kada to štampamo, prvi izlazi dobro,a drugi je izopačen. Ovo je iz razloga što Perl zna da je prvi string Unicode string i interno je uskladišten u UTF-8. Ali on ne zna kodiranje drugog. Kada gradi veći string za štampanje, on re-kodira drugi u UTF-8, pogrešno.

Dodatno tome,štampa upozorenje: Wide character in print (Široki karakter u štampi) na unitest1.pl line 6, <DATA> line 1. Pogledaćemo to kasnije, nakon što sredimo naš output.

Očigledno biste mogli da sredite stvari izbegavanjem konkatenacije:

    #!/usr/bin/perl
    
    my $ustring1 = "Hello \x{263A}!\n";  
    my $ustring2 = <DATA>;
    
    print $ustring1, $ustring2;
    __DATA__
    Hello ☺!
    

    source

Ali ovo nije rešenje. Ponekad prosto ne možete izbeći konkatenaciju; to je tako osnovna operacija. Pored toga, podložna je greškama i nije sigurna za budućnost.

Zašto se dešava problem

Prvo,neke osnovne činjenice.

Postoji razlika između byte-ova i karaktera. Karakteri su Unicode karakteri. Jedan karakter može biti predstavljen sa nekoliko byte-ova, kada se skladišti, štampa ili šalje preko mreže. To zavisi od određenog kodiranja koje se koristi. UTF-8 je samo jedan od načina za predstavljanje Unicode podataka.

Perl ima “utf8” oznaku (flag) za svaku skalarnu vrednost,što može biti “on” (“uključeno”) ili “off” (“isključeno”). Stanje “on” oznake govori perl-u da vrednost tretira kao string Unicode karaktera.

Ako uzmete string sa utf8 oznakom off i ulanačate ga sa string-om koji ima utf8 oznaku on, perl prvi konvertuje na Unicode.

Ovo možda zvuči o.k. i očigledno. Ali onda pomislite: Kako? Perl će morati da zna kodiranje string podataka pre nego što ga konvertuje. I perl će pokušati da ga pogodi. I ovo je uobičajeni izvor problema.

Algoritam koji perl koristi prilikom pogađanja je dokumentovan (koristi neke defaults i možda proverava vaše lokalne),ali moja čvrsta sugestija je: nikada ne dopustite da perl to radi. Sa druge strane,postoji VELIKA šansa da ćete dobiti duplo kodirane UTF-8 string-ove, ili pak raskomadane podatke.

Rešenje: uvek radite kodiranje podataka eksplicitno, podjednako za vaš input i output.

Rešenje #1: Konvertujte string u Unicode

Jedno rešenje bi moglo biti da se kaže perl-u da $ustring2 sadrži Unicode podatke u UTF-8 kodiranju. Postoji par načina da se to uradi; ortodoksan način je kroz Encode’s decode_utf8() funkciju:

    #!/usr/bin/perl
    
    use Encode;
    my $ustring1 = "Hello \x{263A}!\n";  
    my $ustring2 = <DATA>;
    $ustring2 = decode_utf8( $ustring2 );
    
    print "$ustring1$ustring2";
    __DATA__
    Hello ☺!
    

    source

U ovom prostom slučaju bi oba načina obavila posao, ali može biti prilično dosadno, ako vaših import-a ima u izobilju. A i dalje štampa upozorenje “Wide character”.

Ali ovo je ono što bi trebalo uvek da radite za internacionalne podatke koje dobijate od drugih modula,kao iz baza podataka.

Ipak ne bi trebalo da zaboravite da nije svaka sekvenca byte-ova validni UTF-8. Stoga decode_utf8() operacija možda ne uspe. Pogledajte Encode perldoc za detalje o postupanju sa greškama.

Još jedan način da dopustite da perl prihvati UTF-8 podatke kao takve je sa pack “U0C*”, unpack “C*” hack.

Ako dobijete podatke u drugom kodiranju (ne UTF-8), eksplicitno ih konvertujte u Unicode. Ponovo, Encode module, decode() funkcija:

    require Encode;
    my $ustring = Encode::decode( 'iso-8859-1', $input );
    

Još jedan primer: UTF-8 podaci iz CGI-a

U ACIS-u mi proizvodimo HTML stranice u UTF-8. Očekujemo da će i input u HTML formi biti UTF-8. Radi manipulacije,kažemo perl-u za kodiranje:

    require Encode;
    require CGI;
    my $query = CGI ->new;
    my $form_input = {};  
    foreach my $name ( $query ->param ) {
      my @val = $query ->param( $name );
      foreach ( @val ) {
        $_ = Encode::decode_utf8( $_ );
      }
      $name = Encode::decode_utf8( $name );
      if ( scalar @val == 1 ) {   
        $form_input ->{$name} = $val[0];
      } else {                      
        $form_input ->{$name} = \@val;  # save value as an array ref
      }
    }
    

Ovo gradi spreman- i bezbedan-za-upotrebu hash (mešavina) input parametara.

Rešenje #2: Specifikujte IO slojeve kodiranja za vaše filehandles (rukovanja fajlovima)

U Perl-u 5.8 filehandle može imati kodiranje specifikovano za njega. Perl će zatim konvertovati sav input iz fajla automatski u njegovo interno Unicode kodiranje. Markiraće vrednosti pročitane iz njega u skladu sa utf8 oznakom. Isto tako,perl može konvertovati output u specifično kodiranje za filehandle. Pored toga,perl proverava da li su podaci koje iznosite (output) validni za kodiranje filehandle-a.

Dakle,ako čitate podatke iz fajla ili drugog ulaznog toka (input stream), i tamo očekujete UTF-8 podatke,upozorite perl:

    if ( open( FILE, "<:utf8", $fname ) ) {
      . . . 
    }
    

ili,u slučaju našeg jednostavnog testa,

    #!/usr/bin/perl
    
    my $ustring1 = "Hello \x{263A}!\n";  
    binmode DATA, ":utf8";
    my $ustring2 = <DATA>;
    
    print "$ustring1$ustring2";
    __DATA__
    Hello ☺!
    

    source

Ovo bi trebalo da odštampa dva jednaka reda i ne stvori dosadno upozorenje .

Slično tome,ako otvorite fajl kao:

    open FILE, "<:encoding(iso-8859-7)", $filename;
    

da će se za njegov sadržaj pertpostavljati da je iso-8859-7 kodiranje. Perl će to upotrebiti da ispravno interpretira podatke fajla,to jest da ih konvertuje u interni UTF-8.

Rešenje #3: Globalno Unicode podešavanje u Perl-u

A tu je još jedan način da pristupite vašim problemima šifrovanja/kodiranja. to approach your coding/encoding problems. Na perl-u je da komanduje da se sav vaš programski input i output tretira kao UTF-8 po default-u. -C je prebacivanje perl-a koje vam dopušta da to radite. Samo postavite -CS na perl komandnu liniju.

Alternativno, koristite PERL_UNICODE varijablu okruženja. Ona mora biti podešena u okruženju gde izvršavate perl,na primer:

    god@world:~$ PERL_UNICODE=S perl script.pl
    

Da komanda perl peruzme UTF-8 u svim input i output filehandle-ovima u vašem skriptu i upotrebljenim modulima,po default-u. (Na žalost i suprotno mojim očekivanjima,ovo nema uticaja na specijalni filehandle PODATAKA. Dakle,ovo nije rešenje za naš problem showcase (izlog) skripta.)

Možete takođ specifikovati UTF-8 samo za vaš stdin ili samo stdout ili samo stderr. Pročitajte odeljak o -C-u u perlrun za pune detalje.

Upozorenje: Wide character in print (široki karakter u štampi)

Upozorenje se dešava kada iznosite (output) Unicode string u non-unicode filehandle. Šta je "non-unicode filehandle?", pitaćete. To je onaj bez unicode-kompatibilnog IO sloja na njemu (pogledajte Rešenje #2 odeljak gore.)

Pravi način da se ovo sredi je da se eksplicitno specifikuje kodiranje output-a,sa binmode() funkcijom na vašem open() call-u (otvoreni poziv). Na primer,otvorite vaš fajl ovako:

    open FILE, ">:utf8", $filename;
    

Da odštampate UTF-8 na standardni output (ili standardnu grešku), kao u našem slučaju, mi činimo sledeće:

    #!/usr/bin/perl
    
    my $ustring1 = "Hello \x{263A}!\n";  
    binmode DATA, ":utf8";
    my $ustring2 = <DATA>;
    binmode STDOUT, ":utf8";
    print "$ustring1$ustring2";
    __DATA__
    Hello ☺!
    

    source

Pogrešan način da izbegnete upozorenje je da isključite utf8 oznaku na vašim podacima koji su za štampanje. Onda će se karakteri pretvoriti u byte-ove,a perl će ih blago pogurati prema bytes-filehandle-u. Ali to vam ne treba,zaista.

Sa druge strane,ako otvorite fajl kao:

    open FILE, ">:encoding(iso-8859-7)", $filename;

stvari koje štampate će biti output u iso-8859-7 kodiranju, transkodiran automatski. ISO-8859-7 nije Unicode-kompatibilan charset (skup karaktera), tako da nećete moći da iznesete (output) Unicode karaktere na njega bez upozorenja.

Prava strategija: rezime

Ako možete,koristite Unicode kodiranje (kao UTF-8) da skladištite i obrađujete vaše podatke. Uvek se uverite da perl zna koje kodiranje vaši podaci unose i iznose. Uverite se da svi vaši skalari koji sadrže Unicode imaju na sebi oznaku utf8. Onda možete bezbedno ulančavati string-ove. Onda možete koristiti regularne izraze vezane za Unicode,što vam daje veliku snagu za obrađivanje internacionalnih (više-jezičnih) tekstova.

Da biste to postigli,možda će vam biti potrebno da znate sve načine na koje podaci dospevaju u vaš program. Čim dobijete neki input,markirajte ga kao Unicode ili ga konvertujte u Unicode i mirno spavajte.

Ponekad podaci u vaš program dolaze već u Unicode-u i ne treba da brinete. Na primer, XML parser-i (raščlanjivači) vraćaju vrednosti vašeg string-a sa “uključenom” utf8 oznakom. (Osim ako ne uradite nešto čudno, kao da ga dobijete u originalnoj formi od parser-a,što svakako ne bi trebalo da učinite.) U primeru gore mi eksplicitno uključujemo unicode karakter u string ($ustring1) i perl zna njegovo kodiranje.

Ali kada čitate podatke iz input tokova (input streams), iz baze podataka ili iz varijabli okruženja (kao parametri u CGI-u), treba da kažete perl-u njihovo kodiranje.

Koristite PERL_UNICODE varijablu okruženja da naterate UTF-8 IO slojeve na vaše input i/ili output filehandle-ove.





Published (Last edited): 19-03-2013 , source: http://ahinea.com/en/tech/perl-unicode-struggle.html