Le traitement du texte Unicode se déroule en deux étapes. Le premier est "comment puis-je le saisir et le sortir sans perdre d'informations". La seconde est "comment traiter le texte selon les conventions linguistiques locales".
Le message de tchrist couvre les deux, mais la deuxième partie est d'où proviennent 99% du texte de son message. La plupart des programmes ne gèrent même pas les E / S correctement, il est donc important de comprendre cela avant même de commencer à vous soucier de la normalisation et du classement.
Ce message vise à résoudre ce premier problème
Lorsque vous lisez des données dans Perl, peu importe leur encodage. Il alloue de la mémoire et y stocke les octets. Si vous dites print $str
, il transfère simplement ces octets à votre terminal, qui est probablement configuré pour supposer que tout ce qui y est écrit est UTF-8, et votre texte apparaît.
Merveilleux.
Sauf que non. Si vous essayez de traiter les données comme du texte, vous verrez que quelque chose de mauvais se produit. Vous n'avez pas besoin d'aller plus loin que length
de voir que ce que Perl pense de votre chaîne et ce que vous pensez de votre chaîne ne sont pas d'accord. Écrivez une ligne comme: perl -E 'while(<>){ chomp; say length }'
et tapez 文字化け
et vous obtenez 12 ... pas la bonne réponse, 4.
C'est parce que Perl suppose que votre chaîne n'est pas du texte. Vous devez lui dire que c'est du texte avant qu'il ne vous donne la bonne réponse.
C'est assez simple; le module Encode a les fonctions pour le faire. Le point d'entrée générique estEncode::decode
(ouuse Encode qw(decode)
bien sûr). Cette fonction prend une chaîne du monde extérieur (ce que nous appellerons "octets", une façon de dire "octets 8 bits"), et la transforme en un texte que Perl comprendra. Le premier argument est un nom de codage de caractères, comme "UTF-8" ou "ASCII" ou "EUC-JP". Le deuxième argument est la chaîne. La valeur de retour est le scalaire Perl contenant le texte.
(Il y a aussi Encode::decode_utf8
, qui suppose UTF-8 pour l'encodage.)
Si nous réécrivons notre one-liner:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Nous tapons 文字 化 け et obtenons "4" comme résultat. Succès.
C'est là, la solution à 99% des problèmes Unicode en Perl.
La clé est, chaque fois qu'un texte entre dans votre programme, vous devez le décoder. Internet ne peut pas transmettre de caractères. Les fichiers ne peuvent pas stocker de caractères. Il n'y a aucun personnage dans votre base de données. Il n'y a que des octets et vous ne pouvez pas traiter les octets comme des caractères en Perl. Vous devez décoder les octets encodés en caractères Perl avec le module Encode.
L'autre moitié du problème consiste à extraire des données de votre programme. C'est facile à; vous dites simplement use Encode qw(encode)
, décidez de l'encodage de vos données (UTF-8 pour les terminaux qui comprennent UTF-8, UTF-16 pour les fichiers sous Windows, etc.), puis encode($encoding, $data)
sortez le résultat au lieu de simplement le sortir $data
.
Cette opération convertit les caractères de Perl, sur lesquels votre programme fonctionne, en octets pouvant être utilisés par le monde extérieur. Ce serait beaucoup plus facile si nous pouvions simplement envoyer des caractères sur Internet ou à nos terminaux, mais nous ne pouvons pas: octets uniquement. Nous devons donc convertir les caractères en octets, sinon les résultats ne sont pas définis.
Pour résumer: encoder toutes les sorties et décoder toutes les entrées.
Nous allons maintenant parler de trois problèmes qui rendent cela un peu difficile. Le premier est les bibliothèques. Gèrent-ils correctement le texte? La réponse est ... ils essaient. Si vous téléchargez une page Web, LWP vous rendra votre résultat sous forme de texte. Si vous appelez la bonne méthode sur le résultat, c'est-à-dire (et il se trouve que decoded_content
non content
, qui n'est que le flux d'octets qu'il a obtenu du serveur.) Les pilotes de base de données peuvent être floconneux; si vous utilisez DBD :: SQLite avec seulement Perl, cela fonctionnera, mais si un autre outil a mis du texte stocké sous forme d'encodage autre que UTF-8 dans votre base de données ... eh bien ... ça ne sera pas géré correctement jusqu'à ce que vous écriviez du code pour le gérer correctement.
La sortie des données est généralement plus facile, mais si vous voyez "caractère large en caractères d'imprimerie", alors vous savez que vous gâchez l'encodage quelque part. Cet avertissement signifie "hé, vous essayez de divulguer des caractères Perl au monde extérieur et cela n'a aucun sens". Votre programme semble fonctionner (car l'autre extrémité gère généralement correctement les caractères Perl bruts), mais il est très endommagé et peut cesser de fonctionner à tout moment. Fixez-le avec un explicite Encode::encode
!
Le deuxième problème est le code source codé UTF-8. Sauf si vous le dites use utf8
en haut de chaque fichier, Perl ne supposera pas que votre code source est UTF-8. Cela signifie que chaque fois que vous dites quelque chose comme my $var = 'ほげ'
, vous injectez des déchets dans votre programme qui vont tout casser horriblement. Vous n'avez pas à "utiliser utf8", mais si vous ne le faites pas, vous ne devez pas utiliser de caractères non ASCII dans votre programme.
Le troisième problème est de savoir comment Perl gère le passé. Il y a longtemps, Unicode n'existait pas et Perl supposait que tout était du texte latin-1 ou binaire. Ainsi, lorsque des données arrivent dans votre programme et que vous commencez à les traiter comme du texte, Perl traite chaque octet comme un caractère Latin-1. C'est pourquoi, lorsque nous avons demandé la longueur de "文字 化 け", nous en avons obtenu 12. Perl a supposé que nous fonctionnions sur la chaîne latine-1 "æååã" (qui est de 12 caractères, dont certains ne sont pas imprimés).
C'est ce qu'on appelle une «mise à niveau implicite», et c'est une chose parfaitement raisonnable à faire, mais ce n'est pas ce que vous voulez si votre texte n'est pas Latin-1. C'est pourquoi il est essentiel de décoder explicitement l'entrée: si vous ne le faites pas, Perl le fera, et il pourrait le faire mal.
Les gens rencontrent des problèmes lorsque la moitié de leurs données est une chaîne de caractères appropriée et que certaines sont encore binaires. Perl interprétera la partie qui est encore binaire comme s'il s'agissait de texte Latin-1, puis la combinera avec les données de caractères correctes. Cela donnera l'impression que la gestion correcte de vos personnages a interrompu votre programme, mais en réalité, vous ne l'avez pas suffisamment corrigé.
Voici un exemple: vous avez un programme qui lit un fichier texte encodé en UTF-8, vous clouez un Unicode PILE OF POO
sur chaque ligne et vous l'imprimez. Vous l'écrivez comme:
while(<>){
chomp;
say "$_ 💩";
}
Et puis exécutez sur certaines données encodées UTF-8, comme:
perl poo.pl input-data.txt
Il imprime les données UTF-8 avec un caca à la fin de chaque ligne. Parfait, mon programme fonctionne!
Mais non, vous faites juste une concaténation binaire. Vous lisez des octets du fichier, supprimez un \n
avec chomp, puis clouez sur les octets dans la représentation UTF-8 du PILE OF POO
personnage. Lorsque vous révisez votre programme pour décoder les données du fichier et encoder la sortie, vous remarquerez que vous obtenez des ordures ("ð ©") au lieu du caca. Cela vous amènera à croire que le décodage du fichier d'entrée n'est pas la bonne chose à faire. Ce n'est pas.
Le problème est que le caca est implicitement mis à niveau en latin-1. Si vous use utf8
faites le texte littéral au lieu de binaire, alors cela fonctionnera à nouveau!
(C'est le problème numéro un que je vois en aidant les gens avec Unicode. Ils se sont bien débrouillés et cela a cassé leur programme. C'est ce qui est triste avec des résultats indéfinis: vous pouvez avoir un programme de travail pendant longtemps, mais quand vous commencez à le réparer, ne vous inquiétez pas; si vous ajoutez des instructions d'encodage / décodage à votre programme et qu'il se casse, cela signifie simplement que vous avez plus de travail à faire. La prochaine fois, lorsque vous concevrez avec Unicode à l'esprit depuis le début, ce sera beaucoup plus facile!)
C'est vraiment tout ce que vous devez savoir sur Perl et Unicode. Si vous dites à Perl quelles sont vos données, elles ont le meilleur support Unicode parmi tous les langages de programmation populaires. Si vous supposez qu'il saura comme par magie quel type de texte vous l'alimentez, alors vous allez détruire vos données de manière irrévocable. Ce n'est pas parce que votre programme fonctionne aujourd'hui sur votre terminal UTF-8 qu'il fonctionnera demain sur un fichier encodé UTF-16. Alors sécurisez-le maintenant et évitez le casse-tête de mettre à la poubelle les données de vos utilisateurs!
La partie facile de la gestion d'Unicode est l'encodage de la sortie et le décodage de l'entrée. La partie difficile consiste à trouver toutes vos entrées et sorties, et à déterminer de quel encodage il s'agit. Mais c'est pourquoi vous obtenez le gros lot :)