Le traitement du texte Unicode se fait en deux étapes. La première est "comment puis-je l'entrer et le sortir sans perdre d'informations". La seconde est "comment traiter le texte selon les conventions de la langue locale".
Le post de tchrist couvre les deux, mais la deuxième partie est celle d'où provient 99% du texte de son post. La plupart des programmes ne gèrent même pas correctement les E/S, il est donc important de comprendre cela avant même de commencer à se préoccuper de la normalisation et de la collation.
Ce billet vise à résoudre ce premier problème
Lorsque vous lisez des données dans Perl, le codage n'a pas d'importance. Il alloue de la mémoire et y stocke les octets. Si vous dites print $str
il transmet simplement ces octets à votre terminal, qui est probablement configuré pour supposer que tout ce qui lui est écrit est UTF-8, et votre texte s'affiche.
Merveilleux.
Sauf que ça ne l'est pas. Si vous essayez de traiter les données comme du texte, vous verrez que quelque chose de mauvais se passe. Vous n'avez pas besoin d'aller plus loin que length
pour voir que ce que Perl pense de votre chaîne et ce que vous pensez de votre chaîne sont en désaccord. Ecrivez une phrase 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 pour qu'il vous donne la bonne réponse.
C'est assez facile ; le module Encode possède les fonctions pour le faire. Le point d'entrée générique est Encode::decode
(o use Encode qw(decode)
bien sûr). Cette fonction prend une chaîne de caractères du monde extérieur (ce que nous appellerons des "octets", une façon fantaisiste 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 second argument est la chaîne de caractères. La valeur de retour est le scalaire Perl contenant le texte.
(Il existe également Encode::decode_utf8
qui suppose que l'encodage est UTF-8).
Si on réécrit notre phrase d'introduction :
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
On tape 文字化け et on obtient "4" comme résultat. Succès.
C'est la solution à 99 % des problèmes d'Unicode en Perl.
La clé est que, dès qu'un texte entre dans votre programme, vous devez le décoder. L'Internet ne peut pas transmettre de caractères. Les fichiers ne peuvent pas stocker de caractères. Il n'y a pas de caractères 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 codés en caractères Perl avec le module Encode.
L'autre moitié du problème consiste à faire sortir les données de votre programme. C'est facile, il suffit de dire 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. encode($encoding, $data)
au lieu de simplement sortir $data
.
Cette opération convertit les caractères de Perl, sur lesquels votre programme fonctionne, en octets utilisables par le monde extérieur. Ce serait beaucoup plus simple si nous pouvions simplement envoyer des caractères sur Internet ou sur nos terminaux, mais ce n'est pas possible : ce sont des octets. Nous devons donc convertir les caractères en octets, sinon les résultats sont indéfinis.
Pour résumer : coder toutes les sorties et décoder toutes les entrées.
Nous allons maintenant parler de trois problèmes qui rendent la chose un peu difficile. Le premier est celui des bibliothèques. Traitent-elles le texte correctement ? La réponse est... elles essaient. Si vous téléchargez une page web, LWP vous rendra le 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 c'est la méthode decoded_content
no content
qui est juste le flux d'octets qu'il a reçu du serveur). Les pilotes de base de données peuvent être instables ; si vous utilisez DBD::SQLite avec seulement Perl, cela fonctionnera, mais si un autre outil a stocké du texte dans un encodage autre que UTF-8 dans votre base de données... eh bien... il 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 "wide character in print", alors vous savez que vous vous êtes trompé dans l'encodage quelque part. Cet avertissement signifie "hé, vous essayez de faire fuir des caractères Perl vers le monde extérieur et cela n'a aucun sens". Votre programme semble fonctionner (parce que l'autre extrémité traite généralement les caractères Perl bruts correctement), mais il est très cassé et peut cesser de fonctionner à tout moment. Corrigez-le avec un Encode::encode
!
Le deuxième problème est le code source codé en UTF-8. À moins que vous ne disiez 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 de manière horrible. Vous n'êtes pas obligé d'"utiliser utf8", mais si vous ne le faites pas, vous doit n'utilisez pas de caractères non ASCII dans votre programme.
Le troisième problème est la façon dont Perl gère le passé. Il y a longtemps, l'Unicode n'existait pas, et Perl partait du principe que tout était du texte en Latin-1 ou du 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 avons obtenu 12. Perl a supposé que nous opérions sur la chaîne Latin-1 "æåå" (qui compte 12 caractères, dont certains ne sont pas imprimables).
Ceci est appelé 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 en 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 correcte, et que certaines sont encore binaires. Perl interprétera la partie encore binaire comme s'il s'agissait d'un texte en Latin-1, puis la combinera avec les données de caractères correctes. Cela donnera l'impression que la gestion correcte de vos caractères a cassé 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 codé en UTF-8, vous y ajoutez un code Unicode. PILE OF POO
à chaque ligne, et vous l'imprimez. Tu l'écris comme ça :
while(<>){
chomp;
say "$_
4 votes
Bonjour à tous - Ces commentaires soulèvent quelques questions. Ce que j'ai fait, c'est prendre un instantané des commentaires ici et les déposer dans ce salon de discussion pour qu'ils puissent être lus. chat.stackover
16 votes
Je suis désolé mais je suis d'accord avec @tchrist -- UTF-8 est extrêmement difficile. Il n'existe pas de cadre ou d'outil qui permette d'actionner un interrupteur et de le gérer correctement. C'est quelque chose auquel vous devez penser directement lors de la conception de votre application - et non quelque chose qu'un framework ou un langage peut gérer pour vous. Si rakudo a fonctionné pour vous, c'est que vous n'avez pas été assez aventureux avec vos cas de test -- car il prendra plusieurs des exemples de cas de test.
12 votes
Qu'attendez-vous exactement de Moose ou de Modern::Perl ? Créer magiquement des données de caractères codées de manière aléatoire dans les fichiers et les bases de données.
2 votes
@Billy ONeal : en parcourant la liste de @tchrist, il n'y a pas un seul et unique remède. Je suis d'accord. Mais existe-t-il un niveau commun de gestion de l'UTF-8, qui soit enfichable et qui aide les développeurs à entrer dans le jeu ? Je pense que le savoir
utf8::all
est un très bon début. S'il (ouperluniintro
suggérer en tant que démarrage rapide, serait très apprécié0 votes
@jrockway : quel est le but de Modern::Perl ? Réduire le nombre d'erreurs et introduire les meilleures pratiques des technologies disponibles aujourd'hui en Perl. L'inclusion de la gestion de l'UTF-8 convient très bien à cet objectif, IMHO. C'est la même chose pour Moose : c'est un système d'objets moderne pour Perl. Alors, pourquoi ne pas faire un pas de plus et inclure l'UTF-8 comme de
15 votes
Qu'est-ce que cela signifie ? Moose n'a rien à voir avec la manipulation de texte. Pourquoi devrait-il connaître l'encodage des caractères, et encore moins choisir un encodage par défaut pour vous ? (Quoi qu'il en soit, la raison pour laquelle les pragmas que vous avez listés ne touchent pas à l'encodage est que la convention est pour l comportement. Supposer que le monde entier, y compris les autres modules, est en UTF-8 est tout simplement une erreur. [ ]
9 votes
(Par ailleurs... "la plupart des applications Perl modernes" ne fonctionnent pas avec UTF-8 ? Je n'ai certainement jamais écrit d'application, Perl o
15 votes
N t (Tom Christiansen) a posté formation.p Tom Christiansen's Materials for OSCON 2011] à propos d'Unicode. Celui intitulé "Unicode Support Shootout : The Good, The Bad, & the (mostly) Ugly" parle de la prise en charge de l'Unicode dans différents langages de programmation. Seuls Google Go et Perl5 supportent l'Unicode complet, seuls Google Go buil
0 votes
Votre question porte-t-elle spécifiquement sur un système d'exploitation en particulier ? La réponse la plus votée semble être spécifique à Linux. Ou du moins spécifique à Unices
0 votes
@hippietrail : je travaille principalement sur Linux mais j'ai vu beaucoup de questions sur Perl liées à UTF-8 sur Win également. J'ai trop peu de connaissances sur MacOS X, mais d'après ce que j'ai compris, les mêmes questions devraient être actuelles sur Mac également. Si ce n'est pas le cas, j'en suis ravi et je recherche
6 votes
Si je suis sur un système POSIX et que
ENV['LC_ALL']
à "en_US.UTF-8", il s'agit d'une déclaration d'intention explicite que Perl doit respecter en supposant que son entrée standard est encodée en UTF-8, et en encodant sa sortie standard de la même manière. Si mon code se casse parce qu'il ne gère pas certaines des nombreuses subtilités de l'Unicode, peut-être que je ne devrais pas l'exécuter en mode b Unicode. Je ne comprends pas pourquoi Perl devrait ignorer les paramètres linguistiques en faveur de ce que th0 votes
Je ne me suis pas beaucoup penché sur la question, mais utf8::all semble répondre à mes besoins de base. A titre d'information, je pense que la simplicité (publique) de l'utilisation de l'utf-8 en Java est quelque chose de très important.
1 votes
Je sais, c'est un peu hors sujet et trollesque, mais pourquoi ne pas se débarrasser des langages anachroniques comme Perl et PHP et n'utiliser que Python et avoir l'unicode par défaut. T
'string'.encode('utf-8')
(b'string'
) et de reconvertir cette chaîne binaire en unicodb'string'.decode('utf-8')
('string'
). Vous pouvez maintenant arrêter d'y penser. Ce serait ma façon de faire les choses en 2019. Être vieux signifie généralement être stable, mais cela signifie souvent aussi ne pas se débarrasser des façons laides de faire1 votes
@Nils Parce que si vous devez vous préoccuper de l'encodage et du décodage de modèles de bits binaires, c'est que vous vous y prenez mal. UTF-8 n'est rien d'autre qu'un encodage, et vous ne devriez jamais avoir à penser à ses unités de code individuelles, constitutives, de la taille d'un octet. Vous devriez tout au plus penser à des points de code abstraits - et non pas à la question de savoir s'il s'agit de petits ou de grands points de code. [ ] l de vos soucis lorsqu'il s'agit
2 votes
@tchrist Je ne suis pas sûr de comprendre ce que vous voulez dire. Python utilise l'unicode en interne partout et il n'y a pas besoin de se préoccuper des bits et des octets. len('aou') == len('äöü') == len(''). Si un module n'a pas de déclaration d'encodage, python suppose que c'est utf-8 et le décode en unicode. L'encodage du système de fichiers et de la console Windows a été modifié en UTF-8 dans la version 3.6. Toutes les bibliothèques pertinentes de python 3 encodent en utf-8 et utilisent en interne l'unicode. Ce n'est que lorsque open() ing des fichiers en mode texte sans le paramètre d'encodage (qui n'est pas li
3 votes
Il changera .