Użycie „use utf8;” daje mi „szeroki charakter w druku”

86

Jeśli uruchomię następujący program w Perlu:

perl -e 'use utf8; print "鸡\n";'

Otrzymuję to ostrzeżenie:

Wide character in print at -e line 1.

Jeśli uruchomię ten program w Perlu:

perl -e 'print "鸡\n";'

Nie dostaję ostrzeżenia.

Pomyślałem, że use utf8konieczne jest użycie znaków UTF-8 w skrypcie Perla. Dlaczego to nie działa i jak mogę to naprawić? Używam Perla 5.16.2. Mam ten sam problem, jeśli jest to w pliku zamiast być pojedynczą linijką w wierszu poleceń.

Eric Johnson
źródło
3
„Dlaczego to nie działa?” To nie praca, ale to było moje doświadczenie z Unicode, że istnieje bardzo wiele połamanych programów, które obecnie nie wyglądają jak oni pracują. Kiedy naprawisz jedną rzecz, sprawiając, że kod będzie nieco mniej błędny, wyniki wydają się znacznie gorsze. Dopiero gdy naprawisz ostatnią część, wszystko znów wygląda dobrze.
hobbs

Odpowiedzi:

110

Bez use utf8Perla interpretuje twój ciąg jako sekwencję znaków jednobajtowych. Twój ciąg zawiera cztery bajty, jak widać z tego:

$ perl -E 'say join ":", map { ord } split //, "鸡\n";'
233:184:161:10

Pierwsze trzy bajty tworzą twoją postać, ostatni to przesunięcie o linię.

Wywołanie printwysyła te cztery znaki do STDOUT. Twoja konsola ustali, jak wyświetlić te znaki. Jeśli twoja konsola jest ustawiona na używanie UTF8, to zinterpretuje te trzy bajty jako twój pojedynczy znak i to jest to, co jest wyświetlane.

Jeśli dodamy do utf8modułu, sprawy mają się inaczej. W tym przypadku Perl interpretuje twój ciąg jako tylko dwa znaki.

$ perl -Mutf8 -E 'say join ":", map { ord } split //, "鸡\n";'
40481:10

Domyślnie warstwa IO Perla zakłada, że ​​pracuje ze znakami jednobajtowymi. Więc kiedy próbujesz wydrukować znak wielobajtowy, Perl myśli, że coś jest nie tak i wyświetla ostrzeżenie. Jak zawsze, możesz uzyskać więcej wyjaśnienia tego błędu, dołączając use diagnostics. Powie to:

(S utf8) Perl napotkał szeroki znak (> 255), kiedy go nie oczekiwał. To ostrzeżenie jest domyślnie włączone dla I / O (jak drukowanie). Najłatwiejszym sposobem wyciszenia tego ostrzeżenia jest po prostu dodanie warstwy: utf8 do wyjścia, np. Binmode STDOUT, ': utf8'. Innym sposobem na wyłączenie ostrzeżenia jest dodanie żadnych ostrzeżeń „utf8”; ale to często jest bliższe oszukiwaniu. Ogólnie rzecz biorąc, powinieneś jawnie oznaczyć uchwyt pliku za pomocą kodowania, zobacz open i perlfunc / binmode.

Jak zauważyli inni, musisz powiedzieć Perlowi, aby akceptował wielobajtowe wyjście. Można to zrobić na wiele sposobów ( kilka przykładów można znaleźć w samouczku Perl Unicode ). Jednym z najprostszych sposobów jest użycie -CSflagi wiersza poleceń - która mówi trzem standardowym uchwytom plików (STDIN, STDOUT i STDERR), aby poradziły sobie z UTF8.

$ perl -Mutf8 -e 'print "鸡\n";'
Wide character in print at -e line 1.
鸡

vs

$ perl -Mutf8 -CS -e 'print "鸡\n";'

Unicode to duży i złożony obszar. Jak widzieliście, wiele prostych programów wydaje się działać właściwie, ale z niewłaściwych powodów. Kiedy zaczynasz naprawiać część programu, sytuacja często się pogarsza, dopóki nie naprawisz całego programu.

Dave Cross
źródło
Jak przeliterować, -Mutf8jeśli nie w jednym liniowcu perl?
Lei Yang,
@LeiYang:use utf8;
Dave Cross
80

Wystarczy use utf8;powiedzieć Perlowi, że kod źródłowy jest zakodowany przy użyciu UTF-8. Musisz powiedzieć Perlowi, jak zakodować twój tekst:

use open ':std', ':encoding(UTF-8)';
ikegami
źródło
Dzięki, działa to dobrze w przypadku programów przechowywanych w plikach, w przeciwieństwie do jednowierszowych w wierszu poleceń, które obejmuje odpowiedź @ DaveCross.
vktec,
19

Zakoduj wszystkie standardowe wyjścia jako UTF-8:

binmode STDOUT, ":utf8";
Borys Iwanow
źródło
2
use open ':std', ':encoding(UTF-8)';zgodnie z propozycją innej odpowiedzi robi to dla STDOUT, ale również oznacza STDERR i STDIN jako UTF-8, więc otrzymujesz trzy za cenę jednego oświadczenia. Zobacz także stackoverflow.com/a/42194059
Stephen Ostermiller
Zgodzić się. To jest jeszcze lepsze.
Boris Ivanov
14

Możesz zbliżyć się do "po prostu wykonaj utf8 wszędzie" za pomocą modułu CPAN utf8::all.

perl -Mutf8::all -e 'print "鸡\n";'

Kiedy printotrzyma coś, czego nie może wydrukować (znak większy niż 255, gdy nie ma :encodingwarstwy), zakłada, że ​​zamierzałeś zakodować to za pomocą UTF-8. Robi to po ostrzeżeniu o problemie.

Joel Berger
źródło
5

Możesz tego użyć,

perl -CS filename.

To również zakończy ten błąd.

Karthikeyan.RS
źródło
tylko to pomogło
muenalan
0

W języku hiszpańskim możesz znaleźć ten błąd, gdy obok zaczniesz używać:

use utf8;

Twoje kodowanie edytora jest w innym kodowaniu. Więc to, co widzisz w edytorze, nie jest tym, co robi Perl. Aby rozwiązać ten błąd, po prostu zmień kodowanie edytora na Unicode / UTF-8 .

DiegoAr
źródło
1
Nie. To nie jest przyczyną błędu. Cały kod został poprawnie zakodowany jako UTF8, ale uchwyt pliku wyjściowego nie wiedział, że tak.
Dave Cross