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


Je me demande pourquoi la plupart des solutions modernes construites en utilisant Perl ne permettent 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, dans le 21st siècle, les grands nouveaux projets (ou projets avec une grande perspective) devraient rendre leur logiciel UTF-8 preuve de zéro. Je ne vois toujours pas ce qui se passe. Par exemple, élan permet strict et avertissements, mais pas Unicode. Moderne :: Perl réduit aussi la plaque chauffante, mais pas de 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 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'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 Nouveau 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 il y avait une prime pour faire face au même problème avec le maçon 2: Comment nettoyer Mason2 UTF-8?. 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é en Perl 5.


527
2018-05-28 15:12


origine


Réponses:



𝕲𝖔 𝕿𝖍𝖔𝖚 𝖆𝖓𝖉 𝕯𝖔 𝕷𝖎𝖐𝖊𝖜𝖎𝖘𝖊


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

  1. Définissez votre PERL_UNICODE envariable à AS. Cela fait décoder tous les scripts Perl @ARGV en tant que chaînes UTF-8 et définit le codage des trois 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, dohickey), affirmer de manière évidente que vous exécutez perl version 5.12 ou mieux 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 v5.14, le utf8 La classe d'avertissement 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éclarer que tout ce qui ouvre une poignée de fichier dans cette portée lexicale mais pas ailleurs est de 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 tu as un DATA handle, 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é.


 𝕹 𝖔 𝕸 𝖌 𝖎 𝖈 𝖈 𝖑 𝖑 𝖊 𝖙


En disant 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 large; 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 "mettre à jour" votre nouveau Brave Nouveau Monde la 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 revenez à: soit vous serez retourné à un monde meilleur, soit vous apporterez la même connaissance avec vous afin que nous puissions utiliser vos nouvelles connaissances pour améliorer ces choses.


𝕴𝖉𝖊𝖆𝖘 𝖋𝖔𝖗 𝖆 ⸗ 𝕬𝖜𝖆𝖗𝖊 𝕷𝖆𝖚𝖓𝖉𝖗𝖞 𝕷𝖎𝖘𝖙


Au minimum, voici quelques éléments qui sembleraient nécessaires pour "activer Unicode par défaut", comme vous l'avez dit:

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

  2. le DATA Le handle devrait être UTF-8. Vous devrez le faire sur une base par paquet, comme dans binmode(DATA, ":encoding(UTF-8)").

  3. Les arguments du programme aux scripts 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, Oet / 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 UTF-8, sauf indication contraire; export PERL_UNICODE=D ou avec i et o pour certains d'entre eux; export PERL5OPTS=-CD travaillerait. Qui fait -CSAD pour chacun d'eux.

  6. Couvrez les deux bases ainsi que tous les cours d'eau que vous ouvrez export PERL5OPTS=-Mopen=:utf8,:std. Voir uniquote.

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

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

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

  10. Vous avez presque toujours besoin d'accéder aux fonctions de le standard Unicode::Normalize module divers types de décompositions. export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD, puis exécutez toujours des trucs entrants à travers NFD et des trucs sortants de NFC. Il n'y a pas encore de couche d'E / S pour ça, mais je vois nfc, nfd, nfkd, et nfkc.

  11. Comparaisons de chaînes en utilisant eq, ne, lc, cmp, sort, & c & cc ont toujours tort. Donc, au lieu de @a = sort @b, vous avez besoin @a = Unicode::Collate->new->sort(@b). Pourriez-vous 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 la Unicode::GCString module pour le premier, et à la fois et aussi la Unicode::LineBreak module aussi bien pour ce dernier. Voir uwc et unifmt.

  13. Si vous voulez qu'ils comptent comme entiers, alors vous devrez exécuter votre \d+ capture à travers la Unicode::UCD::num fonction parce que c'est intégré atoi(3) n'est pas assez intelligent actuellement.

  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 conduit à des problèmes encore plus grands. Donc, vous devez faire votre propre manipulation NFC / NFD pour rester sain d'esprit.

  15. Tout votre code impliquant a-z ou A-Z et autres choses de ce genre DOIT ÊTRE CHANGÉ, comprenant 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 chaque jour.

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

  17. 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 package.

  19. Si vous vérifiez les espaces, vous devez choisir entre \h et \v, en fonction, dépendemment. Et vous ne devriez jamais utiliser \s, puisqu'il NE SIGNIFIE PAS  [\h\v], contrairement aux croyances populaires.

  20. Si vous utilisez \n pour une limite de ligne, ou même \r\n, alors vous le faites mal. Vous devez utiliser \R, ce qui n'est pas pareil!

  21. Si vous ne savez pas quand et si appeler Unicode :: Stringprep, alors tu ferais 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 de standard Unicode :: Collate module. Unicode::Collate->new(level => 1)->cmp($a, $b). Il y a aussi eq méthodes et autres, et vous devriez probablement en apprendre davantage sur la match et substr méthodes, aussi. Ceux-ci ont des avantages distincts sur les built-ins.

  23. Parfois, ce n'est toujours pas suffisant, et vous avez besoin l'Unicode :: Collate :: Locale module à la place, comme dans Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b) au lieu. Considérez que Unicode::Collate::->new(level => 1)->eq("d", "ð") est vrai, mais Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð") c'est faux. De même, "ae" et "æ" sont eq si vous n'utilisez pas les paramètres régionaux, 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 ucsort 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, d'ailleurs), vous ne serez pas capable de faire quelque chose comme (?=[aeiou])\X) soit, 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 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 déclencheront toujours une exception est incorrect.

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

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

  8. Code qui suppose l'égalité de roundtrip sur le casse, comme lc(uc($s)) eq $s ou uc(lc($s)) eq $s, est complètement cassé et faux. Considérez que le uc("σ") et uc("ς")  sont les deux "Σ", mais lc("Σ") ne peut probablement 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 les deux "ᵃ" et "ᴬ" sont des lettres, mais ce ne sont pas des lettres minuscules; Cependant, ils sont tous deux des points de code minuscules sans les versions majuscules correspondantes. Obtenu que? Elles sont ne pas  \p{Lowercase_Letter}, en dépit d'être à la fois \p{Letter} et \p{Lowercase}.

  10. Code qui suppose que la modification de la casse ne change pas la longueur de la chaîne est cassée.

  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 principale catégorie générale, comme un \p{Mark} se transformer en \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 qu'Unicode donne une image des environnements 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. Code qui suppose \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 est brisé et erroné.

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

  20. Code qui suppose que tout \p{Mark} les caractères prennent zéro colonnes d'impression est cassé.

  21. Code qui suppose que les caractères qui se ressemblent sont semblable est cassé.

  22. Code qui suppose que les caractères qui font ne pas se ressemblent sont ne pas semblable est cassé.

  23. 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. Code qui suppose \X ne peut jamais commencer avec un \p{Mark} le caractère est faux.

  25. Code qui suppose que \X ne peut jamais tenir deux non\p{Mark} les caractères sont faux.

  26. Code qui suppose qu'il ne peut pas utiliser "\x{FFFF}" est faux.

  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 avoir les paupières enlevées.

  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 des caractères comme > pointe toujours vers la droite et < les points toujours à gauche sont faux - parce qu’ils ne le sont pas.

  31. Code qui suppose que le premier caractère est généré X puis le personnage Y, que 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. Code qui suppose que tout \p{Math} les points de code sont des caractères visibles est faux.

  34. Code qui suppose \w ne contient que des lettres, des chiffres et des traits de soulignement est faux.

  35. Code qui suppose que ^ et ~ Les signes de ponctuation sont-ils faux?

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

  37. Code qui croit aux choses comme  contenir des lettres en eux est faux.

  38. Code qui croit \p{InLatin} est le même que \p{Latin} est cassé

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

  40. Code qui croit que donné $FIRST_LETTER comme la première lettre dans un alphabet et $LAST_LETTER comme la dernière lettre dans ce même alphabet, [${FIRST_LETTER}-${LAST_LETTER}] Quel que soit son sens, il est presque toujours complet, brisé et erroné.

  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 piquer l'autre œil.

  44. Code qui convertit les caractères inconnus en ? est cassé, stupide, braindead, et va à l'encontre de la recommandation standard, qui dit 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 printf Les largeurs de tampons et de justifications des données Unicode sont erronées.

  47. Code qui croit qu'une fois que vous avez réussi à créer un fichier avec un nom donné, que lorsque vous exécutez ls ou readdir Dans son répertoire, vous trouverez ce fichier avec le nom que vous avez créé sous buggy, broken et wrong. Arrêtez 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. Code qui traite les points de code d'un plan différent de celui de tout autre plan ipso facto cassé et faux. Retourne à l'école.

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

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

  52. Les gens qui veulent retourner dans le monde ASCII devraient être encouragés de tout coeur et, en l'honneur de leur glorieuse mise à jour, ils devraient être fournis gratuitement avec une machine à écrire manuelle 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 "default Unicode in" vous pouvez obtenir que ce que j'ai écrit. Eh bien, oui je le fais: vous devriez utiliser Unicode::Collate et Unicode::LineBreak, aussi. Et probablement plus.

Comme vous le voyez, il y a beaucoup trop de choses Unicode que vous avez vraiment faire avoir à vous soucier de là pour déjà existe une chose telle que "default to Unicode".

Ce que vous allez découvrir, tout 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 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", car cela garantira beaucoup de choses ne pas travail - qui invalide l'hypothèse selon laquelle il peut toujours y avoir un moyen de «faire en sorte que tout fonctionne».

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 tous 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 surtout 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⅁


1089
2018-05-28 17:09



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 tu le dis print $str, il bloque juste ces octets sur 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 n'avez pas besoin d'aller plus loin que 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. Ecrire un one-liner 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 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 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 tu dis juste use Encode qw(encode), décidez du codage de vos données (UTF-8 pour les terminaux qui comprennent UTF-8, UTF-16 pour les fichiers sous Windows, etc.), puis affichez le résultat de 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 à être decoded_content, ne pas content, qui est juste 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é 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. Corrigez-le avec un explicit Encode::encode!

Le deuxième problème est le code source codé UTF-8. Sauf si tu dis 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 complètement casser tout horriblement. Vous n'êtes pas obligé d'utiliser "utf8", mais si vous ne le faites pas, vous doit n'utilisez 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 a supposé que tout était en texte Latin-1 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 collé sur un Unicode 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 \n avec chomp, puis virant sur les octets dans la représentation UTF-8 de la PILE OF POO personnage. Lorsque vous réviser votre programme pour décoder les données du fichier et encoder la sortie, vous remarquerez que vous obtenez des ordures ("ð") au lieu de la 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, alors il va fonctionner à 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 :)


92
2018-05-31 18:48



Nous sommes tous d'accord pour dire que c'est un problème difficile pour plusieurs raisons, mais c'est précisément la raison pour essayer de le rendre plus facile pour tout le monde.

Il existe un module récent sur CPAN, utf8 :: tous, qui tente de "activer Unicode.

Comme cela a été souligné, vous ne pouvez pas faire comme par magie tout le système (en dehors des programmes, des requêtes web externes, etc.), mais nous pouvons travailler ensemble pour créer des outils pratiques qui facilitent les problèmes communs. C'est la raison pour laquelle nous sommes programmeurs.

Si utf8 :: tout ne fait pas quelque chose que vous pensez devoir, améliorons-le pour le rendre meilleur. Ou faisons d'autres outils qui, ensemble, peuvent répondre aux besoins variés des gens aussi bien que possible.

`


46
2018-05-29 18:59



Je pense que vous vous méprenez sur Unicode et sa relation avec Perl. Peu importe la façon dont vous stockez les données, Unicode, ISO-8859-1ou bien d'autres choses, votre programme doit savoir comment interpréter les octets qu'il reçoit en entrée (décodage) et comment représenter les informations qu'il veut produire (encodage). Obtenez cette interprétation erronée et vous déformez les données. Il n'y a pas de configuration magique par défaut dans votre programme qui va dire à l'extérieur de votre programme comment agir.

Vous pensez que c'est difficile, très probablement, parce que vous êtes habitué à tout ASCII. Tout ce à quoi vous deviez penser était simplement ignoré par le langage de programmation et toutes les choses avec lesquelles il devait interagir. Si tout n'utilisait que UTF-8 et que vous n'aviez pas le choix, alors UTF-8 serait aussi facile. Mais tout n'utilise pas UTF-8. Par exemple, vous ne voulez pas que votre handle d'entrée pense qu'il obtient des octets UTF-8 à moins qu'il ne le soit réellement, et vous ne voulez pas que vos handles de sortie soient UTF-8 si la chose qui les lit peut 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 le temps d'apprendre et de comprendre Unicode et comment cela fonctionne. Vous ne serez pas en mesure de l'utiliser efficacement autrement.


34
2018-05-29 17:51



En lisant ce fil, j'ai souvent l'impression que les gens utilisent "UTF-8"comme synonyme de"UnicodeVeuillez faire une distinction entre les "Code-Points" 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, UTF-16 et UTF-32 sont les actuels et quelques autres sont obsolètes.

S'il vous plaît, UTF-8 (ainsi que tous les autres les encodages) existe et n'a de sens qu'en entrée ou en sortie. En interne, depuis Perl 5.8.1, toutes les chaînes sont conservées en tant que "points de code" Unicode. Il est vrai que vous devez activer certaines fonctionnalités admirablement couvertes précédemment.


27
2018-05-30 09:41



Il y a une quantité effroyable de code ancien dans la nature, en grande partie sous la forme de modules CPAN communs. J'ai trouvé que je devais être assez prudent en activant Unicode si j'utilise des modules externes qui pourraient être affectés par celui-ci, et j'essaie toujours d'identifier et de corriger certains échecs liés à 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).


9
2018-05-28 15:19



Vous devez activer la fonctionnalité des chaînes Unicode, et ceci est la valeur par défaut si vous utilisez la version v5.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 raison. Voir par exemple http://perl11.org/blog/unicode-identifiers.html

En ce qui concerne utf8 pour vos handles de fichiers / flux: Vous devez décider vous-même de l'encodage de vos données externes. Une bibliothèque ne peut pas le savoir, et puisque même la libc ne supporte pas utf8, les données utf8 sont rares. Il y a plus de wtf8, l'aberration des fenêtres de utf8 autour.

BTW: Moose n'est pas vraiment "Perl moderne", ils ont juste détourné le nom. Moose est parfait Perl postmoderne style Larry Wall mélangé avec Bjarne Stroustrup-style tout va, avec une aberration éclectique de la syntaxe Perl6 appropriée, par ex. 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 vrais perls modernes, où la forme suit la fonction, et la mise en œuvre est réduite et optimisée.


1
2018-05-14 11:59