is_utf8 - perl utf8 encode string




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

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

Je comprends qu'il existe de nombreux problèmes hérités pour les scripts Perl principaux, où ils peuvent casser des choses. Mais, de mon point de vue, au XXIe siècle, de gros nouveaux projets (ou projets avec une grande perspective) devraient rendre leur logiciel UTF-8 à l'épreuve. Je ne vois toujours pas ce qui se passe. Par exemple, Moose permet des avertissements stricts, mais pas Unicode . Modern::Perl réduit aussi la plaque froide, mais pas la manipulation UTF-8.

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

Commentant @tchrist est devenu trop long, donc je l'ajoute ici.

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

tchrist et je vois la situation de manière similaire, mais nos conclusions sont complètement à des fins opposées. Je suis d'accord, la situation avec Unicode est compliquée, mais c'est pourquoi nous (utilisateurs et codeurs Perl) avons besoin d'une couche (ou d'un pragma) qui rend la manipulation UTF-8 aussi facile qu'elle doit l'être aujourd'hui.

tchrist a souligné de nombreux aspects à couvrir, je vais lire et y penser pendant des jours ou même des semaines. Pourtant, ce n'est pas mon point. tchrist essaie de prouver qu'il n'y a pas un seul moyen "d'activer UTF-8". Je n'ai pas tellement de connaissances pour argumenter avec ça. Donc, je m'en tiens à des exemples vivants.

J'ai joué avec Rakudo et UTF-8 était juste là comme j'en avais besoin . Je n'ai eu aucun problème, ça a juste marché. Peut-être y a-t-il des limites plus profondes, mais au début, tout ce que j'ai testé a fonctionné comme je m'y attendais.

Est-ce que ça ne devrait pas être un objectif du Perl 5 moderne aussi? Je le souligne plus: je ne suggère pas UTF-8 comme le jeu de caractères par défaut pour le noyau Perl, je suggère la possibilité de le déclencher en un clin d' oeil 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é des frameworks web, mais je les ai simplement jetés parce que "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 faire face au même problème avec Mason 2: Comment rendre Mason2 UTF-8 propre? . Donc, c'est un framework assez nouveau, mais l'utiliser avec UTF-8 nécessite une connaissance approfondie de ses internes. C'est comme un grand panneau rouge: STOP, ne m'utilise pas!

J'aime vraiment Perl. Mais traiter avec Unicode est douloureux. Je me retrouve toujours à courir contre les murs. D'une certaine manière tchrist a raison et répond à mes questions: les nouveaux projets n'attirent pas l'UTF-8 parce que c'est trop compliqué dans Perl 5.


🌴 🐪🐫🐪🐫🐪 🌞 𝕿𝖍𝖔𝖚 𝖆𝖓𝖉 𝕷𝖎𝖐𝖊𝖜𝖎𝖘𝖊 🌞 🐪🐫🐪 🐁

𝓔𝓭𝓲𝓽: 𝙎𝙞𝙢𝙥𝙡𝙚𝙨𝙩 : 𝟕 𝘿𝙞𝙨𝙘𝙧𝙚𝙩𝙚 𝙍𝙚𝙘𝙤𝙢𝙢𝙚𝙣𝙙𝙖𝙩𝙞𝙤𝙣𝙨

  1. Définissez votre PERL_UNICODE envariable à AS . Cela fait que tous les scripts Perl décodent @ARGV tant que @ARGV UTF-8 et définissent le codage de tous les trois de stdin, stdout et stderr sur UTF-8. Les deux sont des effets globaux, pas lexicaux.

  2. En haut de votre fichier source (programme, module, bibliothèque, hickey), affirmez de manière évidente que vous exécutez perl version 5.12 ou supérieure via:

    use v5.12; # minimal for unicode string feature

    use v5.14; # optimal for unicode string feature

  3. Activer les avertissements, car la déclaration précédente n'autorise que les restrictions et les fonctionnalités, pas les avertissements. Je suggère également de promouvoir les avertissements Unicode en exceptions, donc utilisez ces deux lignes, pas seulement l'une d'entre elles. Notez cependant que sous la version v5.14, la classe d'avertissement utf8 comprend trois autres sous-groupes qui peuvent tous être activés séparément: nonchar , surrogate et non_unicode . Ceux-ci, vous pouvez souhaiter exercer un plus grand contrôle sur.

    use warnings;

    use warnings qw( FATAL utf8 );

  4. Déclarez que cette unité source est codée en UTF-8. Bien qu'il fût une fois que ce pragma ait fait d'autres choses, il sert maintenant ce but unique et unique:

    use utf8;

  5. Déclarez que tout ce qui ouvre un descripteur de fichier dans cette portée lexicale mais pas ailleurs est supposer que ce flux est codé en UTF-8 à moins que vous ne le disiez autrement. De cette façon, vous n'affectez pas le code d'autres modules ou d'autres programmes.

    use open qw( :encoding(UTF-8) :std );

  6. Activer les caractères nommés via \N{CHARNAME} .

    use charnames qw( :full :short );

  7. Si vous avez un handle DATA , vous devez définir explicitement son encodage. Si vous voulez que ce soit UTF-8, alors dites:

    binmode(DATA, ":encoding(UTF-8)");

Bien sûr, il n'y a pas de fin à d'autres sujets avec lesquels vous pourriez éventuellement vous sentir concerné, mais cela suffira pour se rapprocher de l'objectif de l'Etat de "tout faire fonctionner avec UTF-8", bien que ces termes soient quelque peu affaiblis.

Un autre pragma, bien qu'il ne soit pas lié à Unicode, est:

      use autodie;

C'est fortement recommandé.

🎅 𝕹 𝖔 𝖆 𝖌 𝖎 𝖈 𝕭 𝕭 𝖑 𝖊 𝖊 𝖙 🎅

Dire que "Perl devrait [en quelque sorte! ] Activer Unicode par défaut "ne commence même pas à commencer à penser à en dire assez pour être même marginalement utile dans une sorte de cas rare et isolé. Unicode est beaucoup plus qu'un simple répertoire de personnages plus grand. c'est aussi la façon dont ces personnages interagissent de nombreuses façons.

Même les mesures minimales simples que (certaines) personnes semblent croire vouloir mettre en échec lamentablement des millions de lignes de code, un code qui n'a aucune chance de se "mettre à jour" dans votre nouvelle modernité.

C'est bien plus compliqué que les gens ne le prétendent. J'ai beaucoup réfléchi à tout cela au cours des dernières années. J'aimerais montrer que j'ai tort. Mais je ne pense pas que je le suis. Unicode est fondamentalement plus complexe que le modèle que vous voudriez lui imposer, et il y a une complexité ici que vous ne pouvez jamais balayer sous le tapis. Si vous essayez, vous casserez votre propre code ou celui de quelqu'un d'autre. À un certain moment, vous devez simplement décomposer et apprendre ce qu'est Unicode. Vous ne pouvez pas prétendre que c'est quelque chose que ce n'est pas.

🐪 fait de son mieux pour rendre Unicode facile, bien plus que toute autre chose que j'ai jamais utilisé. Si vous pensez que c'est mauvais, essayez quelque chose d'autre pendant un moment. Puis revenons à 🐪: soit vous serez revenu dans un monde meilleur, soit vous apporterez la même connaissance avec vous afin que nous puissions utiliser vos nouvelles connaissances pour améliorer at ces choses.

💡 𝕴𝖉𝖊𝖆𝖘 𝖋𝖔𝖗 𝖀𝖓𝖎𝖈𝖔𝖉𝖊 ⸗ 🐪 𝕷𝖆𝖚𝖓𝖉𝖗𝖞 𝕷𝖎𝖘𝖙 💡

Au minimum, voici certaines choses qui sembleraient nécessaires pour que 🐪 "active Unicode par défaut", comme vous le dites:

  1. Tout le code source doit être en UTF-8 par défaut. Vous pouvez l'obtenir avec use utf8 ou export PERL5OPTS=-Mutf8 .

  2. La poignée 🐪 DATA doit être UTF-8. Vous devrez le faire sur une base par paquet, comme dans binmode(DATA, ":encoding(UTF-8)") .

  3. Les arguments du programme pour les scripts should doivent être compris comme étant UTF-8 par défaut. export PERL_UNICODE=A , ou perl -CA , ou export PERL5OPTS=-CA .

  4. Les flux d'entrée, de sortie et d'erreur standard doivent par défaut être UTF-8. export PERL_UNICODE=S pour tous, ou I , O et / ou E pour certains d'entre eux. C'est comme perl -CS .

  5. Toutes les autres poignées ouvertes par 🐪 doivent être considérées comme UTF-8, sauf indication contraire; export PERL_UNICODE=D ou avec i et o pour certains d'entre eux; export PERL5OPTS=-CD fonctionnerait. Cela rend -CSAD pour tous.

  6. Couvrez les deux bases plus tous les flux que vous ouvrez avec l' export PERL5OPTS=-Mopen=:utf8,:std . Voir uniquote .

  7. Vous ne voulez pas manquer les erreurs d'encodage UTF-8. Essayez l' export PERL5OPTS=-Mwarnings=FATAL,utf8 . Et assurez-vous que vos flux d'entrée sont toujours binmode d :encoding(UTF-8) , et pas seulement :utf8 .

  8. Les points de code compris entre 128 et 255 doivent être compris par 🐪 comme étant les points de code Unicode correspondants, et pas seulement comme des valeurs binaires non exploitées. use feature "unicode_strings" ou export PERL5OPTS=-Mfeature=unicode_strings . Cela rendra uc("\xDF") eq "SS" et "\xE9" =~ /\w/ . Une export PERL5OPTS=-Mv5.12 simple export PERL5OPTS=-Mv5.12 ou mieux obtiendra également cela.

  9. Les caractères Unicode nommés ne sont pas activés par défaut, donc ajoutez l' export PERL5OPTS=-Mcharnames=:full,:short,latin,greek ou autre. Voir uninames et tcgrep .

  10. Vous avez presque toujours besoin d'accéder aux fonctions à partir du module standard Unicode::Normalize différents types de décompositions. export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD , puis exécutez toujours les éléments entrants via NFD et les éléments sortants de NFC. Il n'y a pas encore de couche d'E / S pour cela, mais voir nfc , nfd , nfkd et nfkc .

  11. Les comparaisons de chaînes dans 🐪 en utilisant eq , ne , lc , cmp , sort , & c & cc sont toujours fausses. Donc, au lieu de @a = sort @b , vous avez besoin de @a = Unicode::Collate->new->sort(@b) . export PERL5OPTS=-MUnicode::Collate ajouter cela à votre export PERL5OPTS=-MUnicode::Collate . Vous pouvez mettre en cache la clé pour les comparaisons binaires.

  12. 🐪 built-ins comme printf et write faire la mauvaise chose avec des données Unicode. Vous devez utiliser le module Unicode::GCString pour le premier, et aussi bien le module Unicode::LineBreak que pour le second. Voir uwc et unifmt .

  13. Si vous voulez qu'ils soient considérés comme des entiers, alors vous devrez exécuter vos captures \d+ via la fonction Unicode::UCD::num car atoi (3) n'est pas assez intelligent pour le moment.

  14. Vous allez avoir des problèmes de système de fichiers sur les systèmes de fichiers. Certains systèmes de fichiers imposent silencieusement une conversion à NFC; d'autres imposent silencieusement une conversion à NFD. Et d'autres font encore autre chose. Certains ignorent même complètement la question, ce qui entraîne des problèmes encore plus importants. Donc, vous devez faire votre propre manipulation NFC / NFD pour rester sain d'esprit.

  15. Tout votre code involving impliquant az ou AZ et tel doit être modifié , y compris m// , s/// , et tr/// . Il devrait ressortir comme un drapeau rouge hurlant que votre code est brisé. Mais on ne sait pas comment cela doit changer. Obtenir les bonnes propriétés, et comprendre leurs cas, est plus difficile que vous ne le pensez. J'utilise unichars et uniprops tous les jours.

  16. Le code qui utilise \p{Lu} est presque aussi faux que le code qui utilise [A-Za-z] . Vous devez utiliser \p{Upper} place, et connaître la raison pour laquelle. Oui, \p{Lowercase} et \p{Lower} sont différents de \p{Ll} et \p{Lowercase_Letter} .

  17. Le code qui utilise [a-zA-Z] est encore pire. Et il ne peut pas utiliser \pL ou \p{Letter} ; il doit utiliser \p{Alphabetic} . Toutes les lettres ne sont pas des lettres, vous savez!

  18. Si vous cherchez des variables avec /[\$\@\%]\w+/ , alors vous avez un problème. Vous devez rechercher /[\$\@\%]\p{IDS}\p{IDC}*/ , et même cela ne pense pas aux variables de ponctuation ou aux variables du paquet.

  19. Si vous vérifiez les espaces, vous devez choisir entre \h et \v , selon. Et vous ne devriez jamais utiliser \s , car cela ne signifie pas [\h\v] , contrairement à la croyance populaire.

  20. Si vous utilisez \n pour une limite de ligne, ou même \r\n , vous vous trompez. Vous devez utiliser \R , ce qui n'est pas la même chose!

  21. Si vous ne savez pas quand et si appeler Unicode::Stringprep , alors vous feriez mieux d'apprendre.

  22. Les comparaisons insensibles à la casse doivent vérifier si deux choses sont les mêmes lettres, peu importe leurs signes diacritiques et autres. La façon la plus simple de le faire est avec le module standard Unicode :: Collate . Unicode::Collate->new(level => 1)->cmp($a, $b) . Il y a aussi des méthodes eq et autres, et vous devriez probablement en apprendre davantage sur les méthodes match et substr . Ceux-ci ont des avantages distincts sur les 🐪 built-ins.

  23. Parfois, cela ne suffit toujours pas, et vous avez besoin du module Unicode :: Collate :: Locale à la place, comme dans Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b) place. Considérons que Unicode::Collate::->new(level => 1)->eq("d", "ð") est vrai, mais Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð") est faux. De même, "ae" et "æ" sont eq si vous n'utilisez pas les locales, ou si vous utilisez l'anglais, mais ils sont différents dans la langue islandaise. Maintenant quoi? C'est dur, je te le dis. Vous pouvez jouer avec unifmt pour tester certaines de ces choses.

  24. Considérez comment faire correspondre le motif CVCV (consonsant, voyelle, consonne, voyelle) dans la chaîne " niño ". Son formulaire NFD - que vous aviez bien mieux décrit - est devenu "nin \ x {303} o". Maintenant que vas-tu faire? Même en prétendant qu'une voyelle est [aeiou] (ce qui est faux, en passant), vous ne pourrez pas faire quelque chose comme (?=[aeiou])\X) non plus, parce que même dans NFD un point de code comme ' ø ' ne se décompose pas ! Cependant, il va tester égal à un «o» en utilisant la comparaison UCA que je viens de vous montrer. Vous ne pouvez pas compter sur NFD, vous devez compter sur UCA.

💩 𝔸 𝕤 𝕦 𝕞 𝕖 𝔹 𝕣 𝕜 𝕜 𝕖 𝕖 𝕖 𝕤 𝕤 𝕤 💩

Et ce n'est pas tout. Il y a des millions d'hypothèses brisées que les gens font à propos d'Unicode. Jusqu'à ce qu'ils comprennent ces choses, leur code will sera brisé.

  1. Le code qui suppose qu'il peut ouvrir un fichier texte sans spécifier que l'encodage est cassé.

  2. Le code qui suppose que l'encodage par défaut est une sorte de codage de plate-forme native est cassé.

  3. Le code qui suppose que les pages Web en japonais ou en chinois occupent moins d'espace en UTF-16 qu'en UTF-8 est faux.

  4. Le code qui suppose que Perl utilise UTF-8 en interne est incorrect.

  5. Le code qui suppose que les erreurs de codage provoquent toujours une exception est incorrect.

  6. Le code qui suppose que les points de code Perl sont limités à 0x10_FFFF est incorrect.

  7. Le code qui suppose que vous pouvez définir $/ sur quelque chose qui fonctionnera avec n'importe quel séparateur de ligne valide est faux.

  8. Le code qui suppose une égalité de roundtrip sur le pli, comme lc(uc($s)) eq $s ou uc(lc($s)) eq $s , est complètement cassé et faux. Considérons que uc("σ") et uc("ς") sont tous deux "Σ" , mais que lc("Σ") ne peut pas retourner les deux.

  9. Le code qui suppose que chaque point de code minuscule a une majuscule distincte, ou vice versa, est rompu. Par exemple, "ª" est une lettre minuscule sans majuscule; alors que "ᵃ" et "ᴬ" sont des lettres, mais ce ne sont pas des minuscules; cependant, ils sont tous deux des points de code en minuscules sans les versions majuscules correspondantes. Obtenu que? Ils ne sont pas \p{Lowercase_Letter} , bien qu'ils soient à la fois \p{Letter} et \p{Lowercase} .

  10. Le code qui suppose que le changement de cas ne change pas la longueur de la chaîne est cassé.

  11. Le code qui suppose qu'il n'y a que deux cas est cassé. Il y a aussi titlecase.

  12. Code qui suppose que seules les lettres sont cassées. Au-delà des lettres, il s'avère que les chiffres, les symboles et même les marques ont un cas. En fait, changer le cas peut même faire changer quelque chose à sa catégorie générale principale, comme un \p{Mark} devenant un \p{Letter} . Cela peut aussi le faire passer d'un script à un autre.

  13. Le code qui suppose que le cas n'est jamais dépendant des paramètres régionaux est rompu.

  14. Le code qui suppose que Unicode donne une image sur les locales de POSIX est cassé.

  15. Le code qui suppose que vous pouvez supprimer les signes diacritiques pour obtenir les lettres ASCII de base est mauvais, immobile, brisé, endommagé par le cerveau, incorrect et justifiant la peine capitale.

  16. Code qui suppose que les signes diacritiques \p{Diacritic} et les marques \p{Mark} sont la même chose est cassée.

  17. Le code qui suppose que \p{GC=Dash_Punctuation} couvre autant que \p{Dash} est cassé.

  18. Le code qui suppose que les tirets, les traits d'union et les points négatifs sont la même chose entre eux, ou qu'il n'y en a qu'un seul de chaque, est cassé et faux.

  19. Code qui suppose que chaque point de code n'occupe pas plus d'une colonne d'impression est brisée.

  20. Le code qui suppose que tous les caractères \p{Mark} prennent zéro colonnes d'impression est rompu.

  21. Le code qui suppose que les caractères qui se ressemblent sont semblables est cassé.

  22. Le code qui suppose que les caractères qui ne se ressemblent pas ne sont pas identiques.

  23. Le code qui suppose qu'il y a une limite au nombre de points de code dans une ligne qu'un seul \X peut correspondre est faux.

  24. Le code qui suppose que \X ne peut jamais commencer avec un caractère \p{Mark} est faux.

  25. Le code qui suppose que \X ne peut jamais contenir deux caractères non \p{Mark} est faux.

  26. Le code qui suppose qu'il ne peut pas utiliser "\x{FFFF}" est incorrect.

  27. Le code qui suppose qu'un point de code non-BMP nécessitant deux unités de code UTF-16 (de substitution) codera deux caractères UTF-8 distincts, un par unité de code, est erroné. Ce n'est pas le cas: il code pour un point de code unique.

  28. Le code qui transcode depuis UTF-16 ou UTF-32 avec les principales nomenclatures en UTF-8 est rompu s'il place une nomenclature au début de l'UTF-8 résultant. C'est tellement stupide que l'ingénieur devrait se faire enlever les paupières.

  29. Le code qui suppose que le CESU-8 est un codage UTF valide est incorrect. De même, le code qui pense encoder U + 0000 comme "\xC0\x80" est UTF-8 est cassé et faux. Ces gars méritent aussi le traitement des paupières.

  30. Code qui suppose que les caractères comme > pointe toujours vers la droite et < toujours les points vers la gauche sont faux - parce qu'ils ne le font pas.

  31. Code qui suppose que si vous produisez d'abord le caractère X et ensuite le caractère Y , ceux-ci apparaîtront comme XY est faux. Parfois, ils ne le font pas.

  32. Le code qui suppose que ASCII est assez bon pour écrire correctement l'anglais est stupide, myope, illettré, brisé, mal, et faux. Enlevez leur têtes! Si cela semble trop extrême, nous pouvons faire des compromis: dorénavant, ils ne peuvent taper qu'avec leur gros orteil à partir d'un pied (le reste étant encore ducktapé).

  33. Le code qui suppose que tous les points de code \p{Math} sont des caractères visibles est incorrect.

  34. Le code supposant que \w contient uniquement des lettres, des chiffres et des traits de soulignement est incorrect.

  35. Le code qui suppose que ^ et ~ sont des signes de ponctuation est incorrect.

  36. Le code qui suppose que ü a un tréma est faux.

  37. Un code qui croit que des choses comme contiennent des lettres est incorrect.

  38. Le code qui croit que \p{InLatin} est le même que \p{Latin} est abîmé.

  39. Le code qui croit que \p{InLatin} est presque toujours utile est presque certainement faux.

  40. Code qui croit que $FIRST_LETTER étant la première lettre d'un alphabet et $LAST_LETTER la dernière lettre du même alphabet, que [${FIRST_LETTER}-${LAST_LETTER}] a une signification presque toujours complète et erronée. sans signification.

  41. Le code qui croit que le nom de quelqu'un ne peut contenir que certains caractères est stupide, offensant et mauvais.

  42. Le code qui tente de réduire Unicode en ASCII n'est pas simplement faux, son auteur ne devrait jamais être autorisé à travailler à nouveau en programmation. Période. Je ne suis même pas certain qu'ils devraient même être autorisés à revoir, car cela ne leur a pas vraiment fait du bien jusqu'ici.

  43. Le code qui croit qu'il existe un moyen de prétendre que les encodages de textfile n'existent pas est cassé et dangereux. Peut-être aussi bien piquer l'autre œil, aussi.

  44. Code qui convertit les caractères inconnus en ? est cassé, stupide, braindead, et va à l'encontre de la recommandation standard, qui dit de ne pas faire ça! RTFM pour pourquoi pas.

  45. Un code qui croit pouvoir deviner de façon fiable l'encodage d'un fichier texte non marqué est coupable d'un mélange fatal d'hubris et de naïveté que seul un éclair de Zeus va corriger.

  46. Code qui croit que vous pouvez utiliser 🐪 largeurs de printf pour tamponner et justifier les données Unicode est cassé et faux.

  47. Code qui croit qu'une fois que vous avez réussi à créer un fichier avec un nom donné, lorsque vous lancez ls ou readdir dans son répertoire englobant, vous trouverez ce fichier avec le nom que vous avez créé sous buggy, broken et wrong. Cessez d'être surpris par ça!

  48. Code qui croit UTF-16 est un encodage à largeur fixe est stupide, cassé, et faux. Révoquer leur licence de programmation.

  49. Le code qui traite différemment les points de code d'un plan différent de ceux de tout autre plan est ipso facto cassé et erroné. Retourne à l'école.

  50. Code qui croit que des choses comme /s/i peuvent seulement correspondre à "S" ou "s" est cassé et faux. Tu serais surpris.

  51. Le code qui utilise \PM\pM* pour trouver des grappes de graphèmes au lieu d'utiliser \X est cassé et incorrect.

  52. Les gens qui veulent retourner au monde ASCII devraient être encouragés de tout coeur et, en l'honneur de leur glorieuse mise à jour, ils devraient recevoir gratuitement une machine à écrire pré-électrique pour tous leurs besoins d'entrée de données. Les messages qui leur sont envoyés doivent être envoyés par un télégraphe ats à 40 caractères par ligne et remis en mains propres par un courrier. ARRÊTEZ.

🎁 🐪 𝕭𝖔𝖎𝖑𝖊𝖗⸗𝖕𝖑𝖆𝖙𝖊 𝖀𝖓𝖎𝖈𝖔𝖉𝖊⸗𝕬𝖜𝖆𝖗𝖊 𝕮𝖔𝖉𝖊 🐪 🎁

Mon propre passe-temps de nos jours a tendance à ressembler à ceci:

use 5.014;

use utf8;
use strict;
use autodie;
use warnings; 
use warnings    qw< FATAL  utf8     >;
use open        qw< :std  :utf8     >;
use charnames   qw< :full >;
use feature     qw< unicode_strings >;

use File::Basename      qw< basename >;
use Carp                qw< carp croak confess cluck >;
use Encode              qw< encode decode >;
use Unicode::Normalize  qw< NFD NFC >;

END { close STDOUT }

if (grep /\P{ASCII}/ => @ARGV) { 
   @ARGV = map { decode("UTF-8", $_) } @ARGV;
}

$0 = basename($0);  # shorter messages
$| = 1;

binmode(DATA, ":utf8");

# give a full stack dump on any untrapped exceptions
local $SIG{__DIE__} = sub {
    confess "Uncaught exception: @_" unless $^S;
};

# now promote run-time warnings into stackdumped exceptions
#   *unless* we're in an try block, in which 
#   case just generate a clucking stackdump instead
local $SIG{__WARN__} = sub {
    if ($^S) { cluck   "Trapped warning: @_" } 
    else     { confess "Deadly warning: @_"  }
};

while (<>)  {
    chomp;
    $_ = NFD($_);
    ...
} continue {
    say NFC($_);
}

__END__

😱 𝕾 𝖀 𝕸 𝕬 𝕽 𝖄 😱

Je ne sais pas combien plus "Unicode par défaut" vous pouvez obtenir que ce que j'ai écrit. Eh bien, oui je le fais: vous devriez aussi utiliser Unicode::Collate et Unicode::LineBreak . Et probablement plus.

Comme vous le voyez, il y a beaucoup trop de choses Unicode que vous avez vraiment à vous inquiéter car il existe toujours quelque chose comme "default to Unicode".

Ce que vous allez découvrir, comme nous l'avons fait en 5.8, c'est simplement impossible d'imposer toutes ces choses sur un code qui n'a pas été conçu dès le départ pour en tenir compte. Votre égoïsme bien intentionné a juste brisé le monde entier.

Et même une fois que vous le faites, il y a encore des problèmes critiques qui nécessitent beaucoup de réflexion pour bien faire. Il n'y a pas d'interrupteur que vous pouvez retourner. Rien que du cerveau, et je veux dire du vrai cerveau , suffira ici. Il y a beaucoup de choses à apprendre. Modulo la retraite à la machine à écrire manuelle, vous ne pouvez tout simplement pas espérer se faufiler par ignorance. C'est le 21ème siècle, et vous ne pouvez pas désirer Unicode par ignorance volontaire.

Vous devez l'apprendre. Période. Il ne sera jamais si facile que «tout fonctionne bien», car cela garantira que beaucoup de choses ne fonctionnent pas - ce qui invalide l'hypothèse selon laquelle il est toujours possible de «tout faire fonctionner».

Vous pourriez être en mesure d'obtenir quelques défauts raisonnables pour un très petit nombre d'opérations et très limitées, mais pas sans penser à des choses beaucoup plus que je pense que vous avez.

Pour ne citer qu'un exemple, l'ordre canonique va causer de véritables maux de tête. 😭 "\x{F5}" 'õ' , "o\x{303}" 'õ' , "o\x{303}\x{304}" 'ȭ' , et "o\x{304}\x{303}" 'ō" devrait correspondre à "õ" , mais comment allez-vous faire cela dans le monde? C'est plus difficile que ça en a l'air, mais c'est quelque chose dont vous devez tenir compte. 💣

S'il y a une chose que je sais à propos de Perl, c'est ce que ses bits Unicode font et ne font pas, et ce que je vous promets: "ᴛʜᴇʀᴇ ɪs ɴᴏ Uɴɪᴄᴏᴅᴇ ᴍᴀɢɪᴄ ʙᴜʟʟᴇᴛ" 😞

Vous ne pouvez pas simplement changer certaines valeurs par défaut et obtenir une navigation fluide. C'est vrai que je cours 🐪 avec PERL_UNICODE mis à "SA" , mais c'est tout, et même cela est principalement pour les choses en ligne de commande. Pour un vrai travail, je passe par toutes les étapes décrites ci-dessus, et je le fais très, très ** avec soin.

😈 ƨdləɥ ƨᴉɥʇ ədoɥ puɐ'ɐɐp əɔᴉu ɐ əʌɐɥ'ʞɔnl poo⅁ 😈


Le traitement du 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 un texte selon les conventions linguistiques locales".

Le message de tchrist couvre les deux, mais la deuxième partie est d'où vient 99% du texte de son message. 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 s'inquiéter de la normalisation et du classement.

Ce post vise à résoudre ce premier problème

Lorsque vous lisez des données dans Perl, il ne se soucie pas de l'encodage. Il alloue de la mémoire et cache les octets là-bas. Si vous dites print $str , cela bloque simplement ces octets vers votre terminal, qui est probablement supposé supposer que tout ce qui y est écrit est UTF-8, et votre texte apparaît.

Merveilleux.

Sauf, ce n'est pas. Si vous essayez de traiter les données sous forme de texte, vous verrez que quelque chose de mal se passe. Vous ne devez pas aller plus loin que la length pour voir ce que Perl pense de votre chaîne et ce que vous pensez de votre chaîne n'est pas d'accord. Écrivez un one-liner comme: perl -E 'while(<>){ chomp; say length }' 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 dire que c'est un texte avant qu'il ne vous donne 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 bien use Encode qw(decode) , bien sûr). Cette fonction prend une certaine chaîne du monde extérieur (ce que nous appellerons "octets", une fantaisie de dire "octets de 8 bits"), et le 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.

Voilà, 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 caractère 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 à extraire des données de votre programme. C'est facile à faire vous dites simplement use Encode qw(encode) , décider 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 afficher le résultat de l' encode($encoding, $data) au lieu de simplement sortir $data .

Cette opération convertit les caractères de Perl, sur lesquels fonctionne votre programme, en octets pouvant être utilisés par le monde extérieur. Ce serait beaucoup plus simple si nous pouvions simplement envoyer des caractères sur Internet ou à nos terminaux, mais nous ne pouvons pas: octets seulement. Nous devons donc convertir les caractères en octets, sinon les résultats ne sont pas définis.

Pour récapituler: encoder toutes les sorties et décoder toutes les entrées.

Maintenant, nous allons parler de trois questions qui rendent un peu difficile. Le premier est les bibliothèques. Traitent-ils le texte correctement? La réponse est ... ils essaient. Si vous téléchargez une page Web, LWP vous remettra le résultat sous forme de texte. Si vous appelez la bonne méthode sur le résultat, c'est (et cela arrive à decoded_content , pas content , qui est juste le flux d'octets qu'il a reçu 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é comme 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 de données est généralement plus facile, mais si vous voyez "caractères larges dans l'impression", alors vous savez que vous êtes en train de bousiller l'encodage quelque part. Cet avertissement signifie "hé, vous essayez de fuir les caractères Perl vers le monde extérieur et cela n'a aucun sens". Votre programme semble fonctionner (car l'autre extrémité gère normalement les caractères Perl bruts correctement), mais il est très brisé et peut cesser de fonctionner à tout moment. Fixez-le avec un Encode::encode explicite!

Le deuxième problème est le code source codé UTF-8. Sauf si vous 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 ordures dans votre programme qui va tout casser complètement horriblement. Vous n'avez pas besoin d'utiliser "utf8", mais si vous ne le faites pas, vous ne devez utiliser aucun caractère non-ASCII dans votre programme.

Le troisième problème est de savoir comment Perl gère le passé. Il y a longtemps, il n'y avait pas d'Unicode, et Perl supposait que tout était en texte latin ou binaire. Ainsi, lorsque les données entrent 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, quand nous avons demandé la longueur de "文字 化 け", nous avons eu 12. Perl supposait que nous fonctionnions sur la chaîne "1" (qui est composée de 12 caractères, dont certains ne sont pas imprimables).

C'est ce qu'on appelle une "mise à jour 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 les entrées: si vous ne le faites pas, Perl le fera, et cela pourrait le faire de travers.

Les gens rencontrent des problèmes où la moitié de leurs données est une chaîne de caractères correcte, et certains sont encore binaires. Perl interprétera la partie qui est toujours binaire comme s'il s'agissait d'un texte Latin-1, puis la combiner avec les données de caractères correctes. Cela donnera l'impression que la manipulation de vos personnages a brisé votre programme correctement, mais en réalité, vous ne l'avez pas suffisamment réparé.

Voici un exemple: vous avez un programme qui lit un fichier texte encodé en UTF-8, vous PILE OF POO sur un PILE OF POO Unicode de PILE OF POO à chaque ligne, et vous l'imprimez. Vous l'écrivez comme:

while(<>){
    chomp;
    say "$_ 💩";
}

Et puis exécutez sur certaines données codé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 à partir du fichier, en supprimant un \n avec chomp, puis en cliquant sur les octets dans la représentation UTF-8 du caractère PILE OF POO . Lorsque vous modifiez votre programme pour décoder les données du fichier et encoder la sortie, vous remarquerez que vous obtenez des ordures ("ð ©") à la place du caca. Cela vous amènera à croire que le décodage du fichier d'entrée est la mauvaise chose à faire. Ce n'est pas.

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

(C'est le problème numéro un que je vois en aidant les gens avec Unicode.Ils ont fait partie droite et qui a brisé leur programme.C'est ce qui est triste de résultats indéfinis: vous pouvez avoir un programme de travail pendant une longue période, 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'elles se cassent, cela signifie simplement que vous avez encore du travail à faire. 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, il a le meilleur support Unicode parmi tous les langages de programmation populaires. Si vous supposez que cela va magiquement savoir quel type de texte vous l'alimentez, cependant, vous allez détruire vos données irrévocablement. Juste parce que votre programme fonctionne aujourd'hui sur votre terminal UTF-8 ne signifie pas qu'il fonctionnera demain sur un fichier encodé en UTF-16. Alors protégez-vous maintenant, et gardez-vous le mal de tête de vider les données de vos utilisateurs!

La partie la plus simple de la manipulation d'Unicode est la sortie de codage et l'entrée de décodage. La partie la plus difficile est de trouver toutes vos entrées et sorties, et de déterminer quel est le codage. Mais c'est pourquoi vous obtenez les gros dollars :)


I think you misunderstand Unicode and its relationship to Perl. No matter which way you store data, Unicode, ISO-8859-1 , or many other things, your program has to know how to interpret the bytes it gets as input (decoding) and how to represent the information it wants to output (encoding). Get that interpretation wrong and you garble the data. There isn't some magic default setup inside your program that's going to tell the stuff outside your program how to act.

You think it's hard, most likely, because you are used to everything being ASCII. Everything you should have been thinking about was simply ignored by the programming language and all of the things it had to interact with. If everything used nothing but UTF-8 and you had no choice, then UTF-8 would be just as easy. But not everything does use UTF-8. For instance, you don't want your input handle to think that it's getting UTF-8 octets unless it actually is, and you don't want your output handles to be UTF-8 if the thing reading from them can handle UTF-8. Perl has no way to know those things. That's why you are the programmer.

I don't think Unicode in Perl 5 is too complicated. I think it's scary and people avoid it. There's a difference. To that end, I've put Unicode in Learning Perl, 6th Edition , and there's a lot of Unicode stuff in Effective Perl Programming . You have to spend the time to learn and understand Unicode and how it works. You're not going to be able to use it effectively otherwise.


There's a truly horrifying amount of ancient code out there in the wild, much of it in the form of common CPAN modules. I've found I have to be fairly careful enabling Unicode if I use external modules that might be affected by it, and am still trying to identify and fix some Unicode-related failures in several Perl scripts I use regularly (in particular, iTiVo fails badly on anything that's not 7-bit ASCII due to transcoding issues).





utf-8