web-dev-qa-db-fra.com

Pourquoi Perl moderne évite-t-il UTF-8 par défaut?

Je me demande pourquoi la plupart des solutions modernes construites avec Perl n'activent pas TF-8 par défaut.

Je comprends qu’il existe de nombreux problèmes hérités pour les scripts Perl de base, où cela peut casser des choses. Mais, de mon point de vue, dans le 21st siècle, les grands nouveaux projets (ou les projets dans une grande perspective) devraient rendre leur logiciel UTF-8 totalement transparent. Je ne vois toujours pas ce qui se passe. Par exemple, Moose active les avertissements stricts, mais pas nicode . Modern :: Perl réduit également la valeur standard, mais pas de traitement UTF-8.

Pourquoi? Y a-t-il des raisons d'éviter l'UTF-8 dans les projets Perl modernes en 2011?


Commenter @tchrist a pris trop de temps, alors je l’ajoute ici.

Il semble que je ne me suis pas fait comprendre. Laissez-moi essayer d'ajouter certaines choses.

tchrist et je vois la situation de la même façon, mais nos conclusions sont complètement opposées. Je conviens que la situation avec Unicode est compliquée, mais c’est la raison pour laquelle nous (utilisateurs de Perl et codeurs) avons besoin d’une couche (ou pragma) qui facilite la gestion UTF-8 aussi facilement qu’elle doit être aujourd’hui.

tchrist a souligné de nombreux aspects à couvrir, je vais les lire et y réfléchir pendant des jours, voire des semaines. Pourtant, ce n'est pas ce que je veux dire. tchrist essaie de prouver qu'il n'y a pas un seul moyen "d'activer UTF-8". Je n'ai pas beaucoup de connaissances pour discuter avec cela. Donc, je m'en tiens à des exemples concrets.

J'ai joué avec Rakudo et UTF-8 était juste là comme j'en avais besoin . Je n'ai eu aucun problème, cela a juste fonctionné. Il y a peut-être une limite plus profonde, mais au début, tout ce que j'ai testé a fonctionné comme prévu.

Cela ne devrait-il pas être un objectif de Perl 5 moderne également? Je le souligne davantage: je ne suggère pas UTF-8 en tant que jeu de caractères par défaut pour Perl, je suggère la possibilité de le déclencher en un clin d'œil pour ceux qui développent de nouveaux projets.

Un autre exemple, mais avec un ton plus négatif. Les cadres devraient faciliter le développement. Il y a quelques années, j'ai essayé les frameworks Web, mais je les ai simplement jetés parce qu'activer "UTF-8" était si obscur. Je n'ai pas trouvé comment et où accrocher le support Unicode. Cela prenait tellement de temps que je trouvais plus facile de revenir à l’ancienne. Maintenant, j'ai vu ici qu'il y avait une prime pour traiter le même problème avec Mason 2: Comment rendre Mason2 UTF-8 propre?. Il s’agit donc d’un nouveau framework, mais son utilisation avec UTF-8 nécessite une connaissance approfondie de ses composants internes. C'est comme un grand panneau rouge: STOP, ne m'utilise pas!

J'aime beaucoup Perl. Mais traiter avec Unicode est pénible. Je me retrouve encore à courir contre les murs. D'une certaine manière tchrist est correct et répond à mes questions: les nouveaux projets n'attirent pas UTF-8 car c'est trop compliqué dans Perl 5.

553
w.k

Le traitement de texte Unicode comporte deux étapes. Le premier est "comment puis-je l’entrer et le sortir sans perdre d’informations". La seconde est "comment traiter le texte conformément aux conventions de la langue locale".

le post de tchrist couvre les deux, mais la deuxième partie est l’origine de 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 vous préoccuper de la normalisation et du classement.

Cet article a pour but de résoudre ce premier problème

Lorsque vous lisez des données dans Perl, peu importe le codage. Il alloue de la mémoire et stocke les octets. Si vous dites print $str, les octets sont simplement supprimés par votre terminal, qui est probablement configuré pour supposer que tout ce qui y est écrit est en UTF-8, et votre texte s'affiche.

Merveilleux.

Sauf que ce n'est pas. Si vous essayez de traiter les données sous forme de texte, vous verrez qu'il se passe quelque chose de grave. 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. Écrivez une ligne comme celle-ci: Perl -E 'while(<>){ chomp; say length }' et saisissez 文字化け pour obtenir 12 ... pas la bonne réponse, 4.

En effet, Perl suppose que votre chaîne n'est pas du texte. Vous devez lui dire que c'est du texte avant de vous donner la bonne réponse.

C'est assez facile. le module Encode a les fonctions pour le faire. Le point d'entrée générique est Encode::decode (ou use Encode qw(decode), bien sûr). Cette fonction prend une chaîne du monde extérieur (ce que nous appellerons "octets", une manière élégante de dire "octets 8 bits") et la transformera en un texte que Perl comprendra. Le premier argument est un nom de codage de caractère, tel que "UTF-8", "ASCII" ou "EUC-JP". Le deuxième argument est la chaîne. La valeur de retour est le scalaire Perl contenant le texte.

(Il existe également Encode::decode_utf8, qui utilise le codage UTF-8.)

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.

Telle est la solution à 99% des problèmes Unicode en Perl.

La clé est que 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 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 dans Perl. Vous devez décoder les octets codé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 à; il vous suffit de dire use Encode qw(encode), de décider de l’encodage de vos données (UTF-8 aux terminaux qui comprennent UTF-8, UTF-16 pour les fichiers sous Windows, etc.), puis d’envoyer le résultat de 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 pouvant être utilisés par le monde extérieur. Ce serait beaucoup plus facile si nous pouvions simplement envoyer des caractères via Internet ou à nos terminaux, mais nous ne pouvons pas: uniquement des octets. Nous devons donc convertir les caractères en octets, sinon les résultats ne sont pas définis.

Pour résumer: encodez toutes les sorties et décodez toutes les entrées.

Nous allons maintenant parler de trois problèmes qui rendent la tâche un peu difficile. Le premier est les bibliothèques. Est-ce qu'ils traitent le texte correctement? La réponse est ... ils essaient. Si vous téléchargez une page Web, LWP vous renverra votre résultat sous forme de texte. Si vous appelez la bonne méthode sur le résultat, c’est (et il s’agit bien de decoded_content, et non de content, qui est simplement le flux d’octets reçu du serveur.) Les pilotes de base de données peuvent être erronés. ; si vous utilisez DBD :: SQLite avec uniquement Perl, cela fonctionnera, mais si un autre outil a mis du texte stocké sous un codage 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 "Caractère large en impression", alors vous saurez que vous allez gâcher le codage quelque part. Cet avertissement signifie "hé, vous essayez de faire sortir des caractères Perl du 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 ne plus fonctionner à tout moment. Corrigez-le avec un Encode::encode explicite!

Le deuxième problème est le code source codé en UTF-8. Sauf si vous indiquez 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 ordures dans votre programme qui vont tout casser horriblement. Vous n'avez pas à "utiliser utf8", mais si vous ne l'utilisez pas, vous ne devez pas utiliser de caractères non-ASCII dans votre programme.

Le troisième problème est la manière dont Perl gère The Past. Il y a longtemps, Unicode n'existait pas, et Perl supposait que tout était du texte latin-1 ou binaire. Ainsi, lorsque les 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 opérions sur la chaîne latine-1 "æååã" (qui est composée de 12 caractères, dont certains non imprimables).

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 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 se peut qu'il le fasse 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 d'un texte Latin-1, puis la combinera avec les données de caractères correctes. Cela donnera l’impression que manipuler vos personnages correctement a cassé votre programme, mais en réalité, vous ne l’avez tout simplement pas réparé suffisamment.

Voici un exemple: vous avez un programme qui lit un fichier texte codé en UTF-8, vous ajoutez un Unicode PILE OF POO à chaque ligne et vous l’imprimez. Vous é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 dans le fichier, supprimez un \n avec chomp, puis pointez sur les octets de la représentation UTF-8 du caractère PILE OF POO. 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 mènera à croire que le décodage du fichier d'entrée est une mauvaise chose à faire. Ce n'est pas.

Le problème est que le caca est implicitement mis à niveau en tant que latin-1. Si vous use utf8 pour rendre le texte littéral au lieu de binaire, alors cela fonctionnera à nouveau!

(C’est le principal problème que je vois quand j’aide les utilisateurs d’Unicode. Ils se sont bien débrouillés, ce qui a brisé leur programme. C’est ce qui est triste à propos des résultats non définis: vous pouvez avoir un programme fonctionnel pendant longtemps, mais lorsque vous commencez à le réparer, Ne vous inquiétez pas, si vous ajoutez des instructions encoder/décoder à votre programme et que cela se produit, cela signifie simplement que vous avez encore du travail à faire. La prochaine fois, lorsque vous concevez avec Unicode en tête dès le début, ce sera beaucoup plus facile!)

C'est tout ce que vous devez savoir sur Perl et Unicode. Si vous indiquez à Perl quelles sont vos données, il dispose du meilleur support Unicode parmi tous les langages de programmation courants. Si vous supposez qu'il saura, comme par magie, le type de texte que vous alimentez, vous allez alors 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 que cela fonctionnera demain sur un fichier encodé en UTF-16. Alors, sécurisez-le maintenant et évitez les tracas de la destruction des données de vos utilisateurs!

La partie facile de la manipulation Unicode est l’encodage de la sortie et le décodage de l’entrée. La partie difficile est de trouver toutes vos entrées et sorties et de déterminer de quel encodage il s’agit. Mais c'est pourquoi vous obtenez beaucoup d'argent :)

96
jrockway

Nous sommes tous d’accord pour dire que c’est un problème difficile pour de nombreuses raisons, mais c’est précisément la raison pour laquelle il est important de simplifier les choses pour tout le monde.

Il existe un module récent sur le réseau CPAN, tf8 :: all , qui tente "d'activer Unicode. Tout".

Comme il a été souligné, vous ne pouvez pas forcer tout le système (programmes extérieurs, requêtes Web externes, etc.) à utiliser également Unicode, mais nous pouvons travailler ensemble pour créer des outils judicieux facilitant la résolution des problèmes courants. C'est la raison pour laquelle nous sommes des programmeurs.

Si utf8 :: all ne fait pas ce que vous croyez devoir faire, améliorons-le pour le rendre meilleur. Ou créons des outils supplémentaires qui, ensemble, peuvent répondre au mieux aux besoins variés des personnes.

`

47
Randy Stauner

Je pense que vous comprenez mal l’Unicode et sa relation avec Perl. Quelle que soit la manière dont vous stockez les données, Unicode, ISO-8859-1 , ou bien d’autres choses, votre programme doit savoir interpréter les octets reçus en entrée (décodage) et représenter le informations qu’elle veut sortir (encodage). Si vous vous trompez d'interprétation, vous brouillez les données. Il n’existe pas de configuration magique par défaut dans votre programme qui dise ce qui se passe en dehors de celui-ci comment agir.

Vous pensez que c'est difficile, probablement, parce que vous êtes habitué à tout ce qui est ASCII. Tout ce à quoi vous auriez dû penser était simplement ignoré par le langage de programmation et tout ce avec quoi il devait interagir. Si tout n'utilisait que l'UTF-8 et que vous n'aviez pas le choix, alors l'UTF-8 serait tout aussi facile. Mais tout n’utilise pas UTF-8. Par exemple, vous ne voulez pas que votre descripteur d'entrée pense qu'il obtiendra des octets UTF-8 à moins que ce ne soit réellement le cas, et vous ne souhaitez pas que vos descripteurs de sortie soient UTF-8 si leur lecture permet de gérer UTF-8. . Perl n'a aucun moyen de savoir ces choses. C'est pourquoi vous êtes le programmeur.

Je ne pense pas que Unicode dans Perl 5 soit trop compliqué. Je pense que c'est effrayant et les gens l'évitent. Il y a une différence. À cette fin, j'ai mis Unicode dans Learning Perl, 6ème édition, et il y a beaucoup de choses Unicode dans Programmation Perl efficace. Vous devez passer du temps à apprendre et à comprendre Unicode et son fonctionnement. Vous ne pourrez pas l'utiliser efficacement autrement.

34
brian d foy

En lisant ce fil, j'ai souvent l'impression que les gens utilisent " TF-8 " comme synonyme de " nicode ". Faites une distinction entre les "points de code" d'Unicode, qui sont un parent agrandi du code ASCII, et les divers "encodages" d'Unicode. Et il y en a quelques-uns, dont UTF-8, TF-16 et TF-32 sont les plus actuels et un peu plus sont obsolètes.

S'il vous plaît, UTF-8 (ainsi que tous les autres encodages ) existent et ont une signification en entrée ou en sortie uniquement. En interne, depuis Perl 5.8.1, toutes les chaînes sont conservées sous forme de "points de code" Unicode. Certes, vous devez activer certaines fonctionnalités comme couvert par admiration précédemment.

28
MeirG

Il existe dans la nature une quantité vraiment horrible de code ancien, dont une grande partie se présente sous la forme de modules CPAN courants. J'ai constaté que je devais être assez prudent pour activer Unicode si j'utilisais des modules externes qui pourraient en être affectés, et j'essaie toujours d'identifier et de corriger certaines défaillances liées à Unicode dans plusieurs scripts Perl que j'utilise régulièrement (en particulier, - iTiVo échoue mal sur tout ce qui n'est pas ASCII 7 bits en raison de problèmes de transcodage).

10
geekosaur

Vous devez activer la fonctionnalité de chaînes unicode. Il s'agit de la valeur par défaut si vous utilisez la version 5.14.

Vous ne devriez pas vraiment utiliser les identifiants Unicode esp. pour le code étranger via utf8 car ils ne sont pas sûrs dans Perl5, seul cperl a eu ce droit. Voir par exemple http://Perl11.org/blog/unicode-identifiers.html

Concernant utf8 pour vos traitements de fichiers/flux: Vous devez décider vous-même du codage de vos données externes. Une bibliothèque ne peut pas le savoir, et comme même la libc ne supporte pas utf8, les données utf8 appropriées sont rares. Il n'y a plus de wtf8, l'aberration de la fenêtre de utf8 autour.

BTW: Moose n'est pas vraiment "Modern Perl", ils ont juste détourné le nom. Moose est un mélange parfait de Perl postmoderne de style Larry Wall mélangé à de style Bjarne Stroustrup. en utilisant des chaînes pour les noms de variables, une syntaxe de champs horrible et une implémentation naïve très immature qui est 10 fois plus lente qu'une implémentation correcte. cperl et Perl6 sont les véritables perles modernes, où la forme suit la fonction et où la mise en œuvre est réduite et optimisée.

1
rurban