/ / perl binmode de utf-8 está funcionando apenas com codificação x {codepoint} e não x para codificação de três bytes - perl, unicode, codificação, utf-8

perl binmode do utf-8 está trabalhando somente com x {codepoint} e não x codificando para codificação de três bytes - perl, unicode, codificação, utf-8

o Caractere euro é 0xe282ac em utf-8

Estou tentando usar uma string em perl com a saída de caracteres utf-8 para STDOUT.

Então, configurei meu script para utf-8 com "use utf8;"

E eu configurei meu STDOUT para estar no utf-8 com "binmode".

Um script de exemplo é:

use utf8;
binmode STDOUT, ":utf8";
print "I owe you 160x{20ac}n";
print "I owe you 80xe2x82xacn";  # utf-8 encoding?

O x {codepoint} funciona bem, mas a codificação do utf-8 me dá um erro:

I owe you 160€
I owe you 80â¬

Respostas:

5 para resposta № 1

Se você deseja uma sequência que consiste nos três bytes E2 82 AC, você pode declarar assim:

my $bytes = "xE2x82xAC";

o xXX O formulário em uma cadeia de caracteres entre aspas duplas usa dois dígitos hexadecimais (e sempre dois) para representar um byte.

A sequência acima contém 3 bytes. Se passarmos a string para o length função retornará 3:

say "Length of $bytes is: " . length($bytes);    # 3

Perl não tem como saber se esses trêsbytes destinam-se a representar o símbolo do Euro. Eles também podem ser uma sequência de três bytes de dentro de um arquivo JPEG, ZIP ou arquivo de dados TCP codificado em SSL que atravessa uma rede. O Perl não sabe nem se importa - são apenas três bytes.

Se você realmente deseja uma sequência de caracteres(em vez de bytes), é necessário fornecer os dados dos caracteres de forma a permitir que o Perl use sua representação interna de caracteres Unicode para armazená-los na memória. Uma maneira é fornecer os caracteres não ASCII no formato UTF8 no código-fonte. Se você estiver fazendo isso, precisará dizer use utf8 na parte superior do seu script, diga ao interpretador Perl para tratar literais de seqüência de caracteres não ASCII como utf8:

use utf8;

my $euro_1 = "€";

Como alternativa, você pode usar o formulário x {X ...} com 1-5 caracteres hexadecimais, representando o número do ponto de código Unicode. Isso declarará uma sequência idêntica:

my $euro_2 = "x{20ac}";

Cada uma dessas cadeias contém uma representação de vários bytes do caractere euro na codificação interna do Perl. Perl sabe que as cadeias são cadeias de caracteres, portanto, o length A função retornará 1 (para 1 caractere) em cada caso:

say "Length of $euro_1 is: " . length($euro_1);    # 1
say "Length of $euro_2 is: " . length($euro_2);    # 1

O recurso definidor da representação interna de cadeias de caracteres do Perl é que ele é para uso dentro Perl. Se quiser gravar os dados em um arquivo ou soquete, você precisará codificar a string de caracteres em uma sequência de bytes:

use Encode qw(encode);

say encode("utf-8", $euro_1);

Também é possível usar binmode ou um argumento para open para dizer que qualquer string gravada em um determinado filehandle deve ser codificado para uma codificação específica.

binmode(STDOUT, ":encoding(utf-8)");

say $euro_1;

Isso só funcionará corretamente para cadeias de caracteres. Se pegássemos nossa string original de 3 bytes $bytes e usado também encode ou camadas de E / S, acabaríamos com lixo, porque o Perl pegaria cada byte e o converteria em UTF8. Então xE2 seria a saída como xC3xA2, x82 seria a saída como xC2x82 e assim por diante.

No entanto, podemos usar o Encode::Decode função para converter a string de $ bytes de 3 bytes em uma string de caractere único na representação de caractere interno do Perl:

use Encode qw(decode);

my $bytes = "xE2x82xAC";
my $euro_3 = decode($bytes);

say "Length of $euro_3 is " . length($euro_3);    # 1

Um pequeno detalhe: em sua pergunta original, você afirmou que 20AC é a Representação UTF-16 do símbolo do euro. Na verdade, existem duas representações UTF-16 diferentes: UTF16BE e UTF16LE, com a última usando a ordem oposta: AC20.


3 para resposta № 2

Enquanto o fileformat.info página que você vincula a descrita, o caractere Unicode EURO SIGN está no ponto de código 20AC e pode ser referido como U+20AC. Em utf-8, que é codificado como os três bytes 0xE2 0x82 0xAC

Para adicionar o caractere Unicode a uma string, você pode escrever

"I owe you x{20ac}160n"

ou

"I owe you N{EURO SIGN}160n"

ou

"I owe you N{U+20AC}160n"

ou, se você use utf8 no topo do seu programa, você pode adicionar o caractere literal com o mesmo efeito

"I owe you €160n"

cada um deles adicionará um único personagem para a string com o ponto de código necessário

Se você usar

"I owe you 80xe2x82xacn"

então você criou uma string com três caracteres que correspondem ao caractere EURO SIGN codificado em utf-8, o que é uma coisa muito diferente. Você pode usar decode_utf8 de Encode módulo para converter esses bytes em um único caractere, mas caso contrário, você tem uma string codificada em utf-8, que é diferente de uma string de caracteres

Aqui está um programa de exemplo

use strict;
use warnings "all";

use open qw/ :std :encoding(utf-8) /;

use Encode qw/ decode_utf8 :fallbacks /;

for my $s (
"I owe you x{20ac}160n",
"I owe you N{EURO SIGN}160n",
"I owe you N{U+20AC}160n",
do { use utf8; "I owe you €160n" },
decode_utf8(my $ss = "I owe you xe2x82xac160n") ) {

print $s;
}

saída

I owe you €160
I owe you €160
I owe you €160
I owe you €160
I owe you €160

Observe que não há necessidade de use utf8 a menos que você esteja usando caracteres não ASCII no código-fonte, como . Você pode acessar os caracteres por seus nomes Unicode (que estão sempre em ASCII), conforme mostrado acima

Se eu redirecionar para um arquivo, posso ver que écodificando o primeiro símbolo do Euro conforme o esperado, 0xe282ac, mas o segundo está se tornando 0xc3a2c20x82c2ac, então de alguma forma está ficando truncado, como se estivesse sendo codificado duas vezes.

isto é sendo codificado duas vezes. Você mesmo codifica o caractere na primeira vez, fornecendo a codificação utf-8 "xe2x82xac" para o caractere, e binmode em seu identificador de arquivo de saída codifica cada um desses caracteres uma segunda vez, dando C3 A2 para E2, C2 82 para 82 e C2 AC para AC


3 para resposta № 3

Você está construindo duas strings diferentes, portanto, obter resultados diferentes não deve ser surpreendente.

Você está executando o que é chamado de "codificação dupla". Você tinha uma string que já estava codificada usando utf-8 e perguntou ao Perl (usando binmode e print) para codificá-lo uma segunda vez. Isso foi um bug da sua parte.


O literal da string "x{20ac}" produz uma string de um caractere (0x20ac).

$ perl -E"say length("x{20ac}")"
1

Quando você imprime em uma alça com o :utf8 identificador, você está instruindo Perl a tratar esses caracteres como pontos de código Unicode e codificá-los usando utf-8.

Conforme solicitado, Perl imprime o seguinte codificado usando utf-8:
SINAL DO EURO U + 020AC (€).

$ perl -E"binmode STDOUT, ":utf8"; print "x{20ac}"" | od -t x1
0000000 e2 82 ac
0000003

$ perl -E"binmode STDOUT, ":utf8"; say "x{20ac}""
€

O literal da string "xe2x82xac" produz uma string de três caracteres (0xe2, 0x82, 0xac).

$ perl -E"say length("xe2x82xac")"
3

("xe2x82xac" é a mesma coisa que "x{e2}x{82}x{ac}".)

Quando você imprime em uma alça com o :utf8 identificador, você está instruindo Perl a tratar esses caracteres como pontos de código Unicode e codificá-los usando utf-8.

Conforme solicitado, Perl imprime o seguinte codificado usando utf-8:
U + 000E2 PEQUENA LETRA A LATINA COM CIRCUMFLEX (â),
U + 00082 INTERVALO PERMITIDO AQUI e
U + 000AC NÃO SINAL (¬).

$ perl -E"binmode STDOUT, ":utf8"; print "xe2x82xac"" | od -t x1
0000000 c3 a2 c2 82 c2 ac
0000006

$ perl -E"binmode STDOUT, ":utf8"; say "xe2x82xac""
�