为什么现代Perl默认会避免使用UTF-8?

w.k*_*w.k 555 unicode perl utf-8

我想知道为什么使用Perl构建的大多数现代解决方案默认情况下不启用UTF-8.

我知道核心Perl脚本存在许多遗留问题,可能会破坏它们.但是,从我的角度来看,在21 的世纪,新的大项目(或具有大的方面讲项目)应该从头开始他们的软件UTF-8的证明.我仍然没有看到它发生.例如,Moose启用严格和警告,但不启用Unicode.Modern :: Perl也减少了样板,但没有UTF-8处理.

为什么?是否有一些理由在2011年的现代Perl项目中避免使用UTF-8?


评论@tchrist太长了,所以我在这里添加它.

似乎我没有说清楚.让我尝试添加一些东西.

tchrist和我看到情况非常相似,但我们的结论完全是相反的.我同意,Unicode的情况很复杂,但这就是为什么我们(Perl用户和编码人员)需要一些层(或编译指示),这使得UTF-8处理变得像现在一样容易.

tchrist指出要涵盖的许多方面,我会阅读并思考它们几天甚至几周.不过,这不是我的观点.tchrist试图证明没有一种方法"启用UTF-8".我没有太多的知识可以与之争辩.所以,我坚持住实例.

我和Rakudo一起玩,UTF-8就在我需要的地方.我没有任何问题,它只是奏效了.也许在某些地方存在一些限制,但一开始,我测试的所有工作都按照我的预期进行.

这不应该是现代Perl 5的目标吗?我更强调一点:我不是建议将UTF-8作为核心Perl的默认字符集,我建议可以为那些开发项目的人快速触发它.

另一个例子,但更负面的语气.框架应该使开发更容易.几年前,我尝试过Web框架,但只是把它们扔掉了,因为"启用UTF-8"是如此模糊.我没有找到如何以及在何处挂钩Unicode支持.这是非常耗时的,我发现它更容易走老路.现在我看到这里有一个赏金来处理与梅森 2 相同的问题:如何让Mason2 UTF-8干净?.因此,它是一个非常新的框架,但使用UTF-8需要深入了解其内部.这就像一个大红色标志:停止,不要使用我!

我真的很喜欢Perl.但处理Unicode是痛苦的.我仍然发现自己在墙上奔跑.某种方式tchrist是正确的,并回答我的问题:新项目不吸引UTF-8,因为它在Perl 5中太复杂了.

tch*_*ist 1139


               


:    

  1. 将你的PERL_UNICODE变量设置为AS.这使得所有Perl脚本都解码@ARGV为UTF?8字符串,并将stdin,stdout和stderr三者的编码设置为UTF?8.这些都是全局效应,而不是词汇效应.

  2. 在源文件(程序,模块,库,dohickey)的顶部,突出显示您通过以下方式运行perl版本5.12或更高版本:

    utf8

    nonchar

  3. 启用警告,因为上一个声明仅启用限制和功能,而不是警告.我还建议将Unicode警告提升为异常,因此请使用这两行,而不仅仅是其中一行.但是请注意,在v5.14中,surrogate警告类包括都可以单独启用其他三个subwarnings: ,non_unicode,\N{CHARNAME}DATA.您可能希望对这些进行更大程度的控制.

    use utf8

    export PERL5OPTS=-Mutf8

  4. 声明此源单元编码为UTF?8.虽然很久以前这个pragma做了其他的事情,但它现在单独服务于这个单一的目的,而不是其他的:

    DATA

  5. 声明在这个词法范围内打开文件句柄而不在其他地方打开文件句柄的任何东西都是假设该流以UTF编码?8除非你另有说明.这样您就不会影响其他模块或其他程序的代码.

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

  6. 通过启用命名字符export PERL_UNICODE=A.

    perl -CA

  7. 如果您有export PERL5OPTS=-CA句柄,则必须显式设置其编码.如果你想要这是UTF?8,那么说:

    export PERL_UNICODE=S

当然,你可能最终会发现自己关心的其他问题没有尽头,但是这些足以接近国家目标"让一切都与UTF一起工作?8",尽管这些术语有点弱化.

另一个编译指示,虽然它与Unicode无关,但是:

use v5.12;  # minimal for unicode string feature
use v5.14;  # optimal for unicode string feature
Run Code Online (Sandbox Code Playgroud)

强烈建议.


                               


说"Perl应该[ 不知何故!]默认情况下启用Unicode"甚至没有开始考虑到在某种罕见和孤立的情况下说得足够甚至有用.Unicode不仅仅是一个更大的角色曲目; 它也是这些角色以多种方式进行互动的方式.

即使是那些(某些)人似乎认为他们想要的简单的最小措施,也可以保证惨败数百万行代码,这些代码没有机会"升级"到你漂亮的新勇敢的新世界现代性.

这是比人们假装更复杂的方式.在过去的几年里,我一直认为这是一个巨大的问题.我希望被证明我错了.但我不认为我.从根本上说,你想要对它施加的模型更加复杂,而且这里有一种复杂性,你永远无法扫清地毯.如果你尝试,你将破坏自己的代码或其他人的代码.在某些时候,您只需要分解并了解Unicode的含义.你不能假装它不是它.

尽力使Unicode变得简单,远远超过我用过的任何东西.如果您认为这很糟糕,请尝试其他一段时间.然后回过头来说:要么你会回到一个更美好的世界,否则你将带来与你相同的知识,这样我们就可以利用你的新知识在这些方面做得更好.


                 ?      


至少,以下是"默认情况下启用Unicode"似乎需要的一些内容,如下所示:

  1. 默认情况下,所有源代码都应为UTF-8.你可以用I或得到它O.

  2. E手柄应该是UTF-8.您必须在每个包装的基础上执行此操作,如perl -CS.

  3. Program arguments to scripts should be understood to be UTF-8 by default. export PERL_UNICODE=D, or i, or o.

  4. The standard input, output, and error streams should default to UTF-8. export PERL5OPTS=-CD for all of them, or -CSAD, export PERL5OPTS=-Mopen=:utf8,:std, and/or export PERL5OPTS=-Mwarnings=FATAL,utf8 for just some of them. This is like binmode.

  5. Any other handles opened by should be considered UTF-8 unless declared otherwise; :encoding(UTF-8) or with :utf8 and use feature "unicode_strings" for particular ones of these; export PERL5OPTS=-Mfeature=unicode_strings would work. That makes uc("\xDF") eq "SS" for all of them.

  6. Cover both bases plus all the streams you open with "\xE9" =~ /\w/. See uniquote.

  7. You don’t want to miss UTF-8 encoding errors. Try export PERL5OPTS=-Mv5.12. And make sure your input streams are always export PERL5OPTS=-Mcharnames=:full,:short,latin,greekd to Unicode::Normalize, not just to export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD.

  8. Code points between 128–255 should be understood by to be the corresponding Unicode code points, not just unpropertied binary values. eq or ne. That will make lc and cmp. A simple sort or better will also get that.

  9. Named Unicode characters are not by default enabled, so add @a = sort @b or some such. See uninames and tcgrep.

  10. You almost always need access to the functions from the standard @a = Unicode::Collate->new->sort(@b) module various types of decompositions. export PERL5OPTS=-MUnicode::Collate, and then always run incoming stuff through NFD and outbound stuff from NFC. There’s no I/O layer for these yet that I’m aware of, but see nfc, nfd, nfkd, and nfkc.

  11. String comparisons in using printf, write, Unicode::GCString, Unicode::LineBreak, \d+, &c&cc are always wrong. So instead of Unicode::UCD::num, you need a-z. Might as well add that to your A-Z. You can cache the key for binary comparisons.

  12. built-ins like m// and s/// do the wrong thing with Unicode data. You need to use the tr/// module for the former, and both that and also the \p{Lu} module as well for the latter. See uwc and unifmt.

  13. If you want them to count as integers, then you are going to have to run your [A-Za-z] captures through the \p{Upper} function because ’s built-in atoi(3) isn’t currently clever enough.

  14. You are going to have filesystem issues on filesystems. Some filesystems silently enforce a conversion to NFC; others silently enforce a conversion to NFD. And others do something else still. Some even ignore the matter altogether, which leads to even greater problems. So you have to do your own NFC/NFD handling to keep sane.

  15. All your code involving \p{Lowercase} or \p{Lower} and such MUST BE CHANGED, including \p{Ll}, \p{Lowercase_Letter}, and [a-zA-Z]. It’s should stand out as a screaming red flag that your code is broken. But it is not clear how it must change. Getting the right properties, and understanding their casefolds, is harder than you might think. I use unichars and uniprops every single day.

  16. Code that uses \pL is almost as wrong as code that uses \p{Letter}. You need to use \p{Alphabetic} instead, and know the reason why. Yes, /[\$\@\%]\w+/ and /[\$\@\%]\p{IDS}\p{IDC}*/ are different from \h and \v.

  17. Code that uses \s is even worse. And it can’t use [\h\v] or \n; it needs to use \r\n. Not all alphabetics are letters, you know!

  18. If you are looking for variables with \R, then you have a problem. You need to look for Unicode::Collate->new(level => 1)->cmp($a, $b), and even that isn’t thinking about the punctuation variables or package variables.

  19. If you are checking for whitespace, then you should choose between eq and match, depending. And you should never use substr, since it DOES NOT MEAN Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b), contrary to popular belief.

  20. If you are using Unicode::Collate::->new(level => 1)->eq("d", "ð") for a line boundary, or even Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð"), then you are doing it wrong. You have to use eq, which is not the same!

  21. If you don’t know when and whether to call Unicode::Stringprep, then you had better learn.

  22. Case-insensitive comparisons need to check for whether two things are the same letters no matter their diacritics and such. The easiest way to do that is with the standard Unicode::Collate module. [aeiou]. There are also (?=[aeiou])\X) methods and such, and you should probably learn about the $/ and lc(uc($s)) eq $s methods, too. These are have distinct advantages over the built-ins.

  23. Sometimes that’s still not enough, and you need the Unicode::Collate::Locale module instead, as in uc(lc($s)) eq $s instead. Consider that uc("?") is true, but uc("?") is false. Similarly, "ae" and "æ" are "?" if you don’t use locales, or if you use the English one, but they are different in the Icelandic locale. Now what? It’s tough, I tell you. You can play with ucsort to test some of these things out.

  24. Consider how to match the pattern CVCV (consonsant, vowel, consonant, vowel) in the string "niño". Its NFD form — which you had darned well better have remembered to put it in — becomes "nin\x{303}o". Now what are you going to do? Even pretending that a vowel is lc("?") (which is wrong, by the way), you won’t be able to do something like "ª" either, because even in NFD a code point like 'ø’ does not decompose! However, it will test equal to an 'o’ using the UCA comparison I just showed you. You can’t rely on NFD, you have to rely on UCA.


                          


And that’s not all. There are million broken assumptions that people make about Unicode. Until they understand these things, their code will be broken.

  1. Code that assumes it can open a text file without specifying the encoding is broken.

  2. Code that assumes the default encoding is some sort of native platform encoding is broken.

  3. Code that assumes that web pages in Japanese or Chinese take up less space in UTF?16 than in UTF?8 is wrong.

  4. Code that assumes Perl uses UTF?8 internally is wrong.

  5. Code that assumes that encoding errors will always raise an exception is wrong.

  6. Code that assumes Perl code points are limited to 0x10_FFFF is wrong.

  7. Code that assumes you can set "?" to something that will work with any valid line separator is wrong.

  8. Code that assumes roundtrip equality on casefolding, like "?" or \p{Lowercase_Letter}, is completely broken and wrong. Consider that the \p{Letter} and \p{Lowercase} are both \p{Mark}, but \p{Letter} cannot possibly return both of those.

  9. Code that assumes every lowercase code point has a distinct uppercase one, or vice versa, is broken. For example, \p{Diacritic} is a lowercase letter with no uppercase; whereas both \p{Mark} and \p{GC=Dash_Punctuation} are letters, but they are not lowercase letters; however, they are both lowercase code points without corresponding uppercase versions. Got that? They are not \p{Dash}, despite being both \p{Mark} and \X.

  10. Code that assumes changing the case doesn’t change the length of the string is broken.

  11. Code that assumes there are only two cases is broken. There’s also titlecase.

  12. Code that assumes only letters have case is broken. Beyond just letters, it turns out that numbers, symbols, and even marks have case. In fact, changing the case can even make something change its main general category, like a \X turning into a \p{Mark}. It can also make it switch from one script to another.

  13. Code that assumes that case is never locale-dependent is broken.

  14. Code that assumes Unicode gives a fig about POSIX locales is broken.

  15. Code that assumes you can remove diacritics to get at base ASCII letters is evil, still, broken, brain-damaged, wrong, and justification for capital punishment.

  16. Code that assumes that diacritics \X and marks \p{Mark} are the same thing is broken.

  17. Code that assumes "\x{FFFF}" covers as much as "\xC0\x80" is broken.

  18. Code that assumes dash, hyphens, and minuses are the same thing as each other, or that there is only one of each, is broken and wrong.

  19. Code that assumes every code point takes up no more than one print column is broken.

  20. Code that assumes that all > characters take up zero print columns is broken.

  21. Code that assumes that characters which look alike are alike is broken.

  22. Code that assumes that characters which do not look alike are not alike is broken.

  23. Code that assumes there is a limit to the number of code points in a row that just one < can match is wrong.

  24. Code that assumes X can never start with a Y character is wrong.

  25. Code that assumes that XY can never hold two non-\p{Math} characters is wrong.

  26. Code that assumes that it cannot use \w is wrong.

  27. Code that assumes a non-BMP code point that requires two UTF-16 (surrogate) code units will encode to two separate UTF-8 characters, one per code unit, is wrong. It doesn’t: it encodes to single code point.

  28. Code that transcodes from UTF?16 or UTF?32 with leading BOMs into UTF?8 is broken if it puts a BOM at the start of the resulting UTF-8. This is so stupid the engineer should have their eyelids removed.

  29. Code that assumes the CESU-8 is a valid UTF encoding is wrong. Likewise, code that thinks encoding U+0000 as ^ is UTF-8 is broken and wrong. These guys also deserve the eyelid treatment.

  30. Code that assumes characters like ~ always points to the right and ü always points to the left are wrong — because they in fact do not.

  31. Code that assumes if you first output character ? and then character \p{InLatin}, that those will show up as \p{Latin} is wrong. Sometimes they don’t.

  32. Code that assumes that ASCII is good enough for writing English properly is stupid, shortsighted, illiterate, broken, evil, and wrong. Off with their heads! If that seems too extreme, we can compromise: henceforth they may type only with their big toe from one foot (the rest still be ducktaped).

  33. Code that assumes that all \p{InLatin} code points are visible characters is wrong.

  34. Code that assumes $FIRST_LETTER contains only letters, digits, and underscores is wrong.

  35. Code that assumes that $LAST_LETTER and [${FIRST_LETTER}-${LAST_LETTER}] are punctuation marks is wrong.

  36. Code that assumes that ? has an umlaut is wrong.

  37. Code that believes things like printf contain any letters in them is wrong.

  38. Code that believes ls is the same as readdir is heinously broken.

  39. Code that believe that /s/i is almost ever useful is almost certainly wrong.

  40. Code that believes that given "S" as the first letter in some alphabet and "s" as the last letter in that same alphabet, that \PM\pM* has any meaning whatsoever is almost always complete broken and wrong and meaningless.

  41. Code that believes someone’s name can only contain certain characters is stupid, offensive, and wrong.

  42. Code that tries to reduce Unicode to ASCII is not merely wrong, its perpetrator should never be allowed to work in programming again. Period. I’m not even positive they should even be allowed to see again, since it obviously hasn’t done them much good so far.

  43. Code that believes there’s some way to pretend textfile encodings don’t exist is broken and dangerous. Might as well poke the other eye out, too.

  44. Code that converts unknown characters to \X is broken, stupid, braindead, and runs contrary to the standard recommendation, which says NOT TO DO THAT! RTFM for why not.

  45. Code that believes it can reliably guess the encoding of an unmarked textfile is guilty of a fatal mélange of hubris and naïveté that only a lightning bolt from Zeus will fix.

  46. Code that believes you can use Unicode::Collate widths to pad and justify Unicode data is broken and wrong.

  47. Code that believes once you successfully create a file by a given name, that when you run Unicode::LineBreak or "\x{F5}" on its enclosing directory, you’ll actually find that file with the name you created it under is buggy, broken, and wrong. Stop being surprised by this!

  48. Code that believes UTF-16 is a fixed-width encoding is stupid, broken, and wrong. Revoke their programming licence.

  49. Code that treats code points from one plane one whit differently than those from any other plane is ipso facto broken and wrong. Go back to school.

  50. Code that believes that stuff like "o\x{303}" can only match "o\x{303}\x{304}" or "o\x{304}\x{303}" is broken and wrong. You’d be surprised.

  51. Code that uses PERL_UNICODE to find grapheme clusters instead of using "SA" is broken and wrong.

  52. People who want to go back to the ASCII world should be whole-heartedly encouraged to do so, and in honor of their glorious upgrade they should be provided gratis with a pre-electric manual typewriter for all their data-entry needs. Messages sent to them should be send via an ??????s telegraph at 40 characters per line and hand-delivered by a courier. STOP.


           ?   ?   


My own boilerplate these days tends to look like this:

use warnings;
use warnings qw( FATAL utf8 );
Run Code Online (Sandbox Code Playgroud)

                                  


I don’t know how much more "default Unicode in " you can get than what I’ve written. Well, yes I do: you should be using PERL_UNICODE and __C

  • 我是唯一一个发现具有讽刺意味的人,因为tchrist的这篇文章在FF/Chrome/IE/Opera上呈现出如此大的不同,有时甚至难以辨认? (72认同)
  • 就像Sherm Pendley指出的那样:"全部!" 如果我今天写一些新内容,UTF-8应该是最简单的方法来完成任务.它不是.你的样板证明了它.不是每个人都有这样的知识可以把这么多的翻转者变成正确的位置.对不起,我度过了漫长而艰难的一天,所以我将在明天的主要参赛作品中加上一些例子. (55认同)
  • @xenoterracide不,我没有使用故意有问题的代码点; 这是一个让你安装[George Douros的超级令人敬畏的Symbola字体](http://users.teilar.gr/~g1951d/)的情节,它涵盖了Unicode 6.0.@depesz这里没有空间来解释为什么每次破碎的分裂都是错误的.@leonbloy**很多和很多**一般都适用于Unicode,而不仅仅是Perl.其中一些材料可能会出现在[Programming Perl,第4版](http://oreilly.com/catalog/9780596004927/)中,将于10月份发布.我还有一个月的时间来处理它,**Unicode就是ᴍᴇɢᴀ**; 正则表达式 (36认同)
  • 通过阅读上面的列表,一个结论应该是显而易见的:不要大小写.只是不要.永远.计算上昂贵且具有语义,这些语义主要依赖于"语言环境"尝试识别失败的任何内容. (17认同)
  • 虽然我一般喜欢这个帖子,并且做了upvote,但有一件事让我感到很难过.有很多"代码......被打破".虽然我不赞成这个陈述,但我认为展示破碎会很好.通过这种方式,它将从咆哮到教育遍历(这部分答案). (15认同)
  • 完美答案.但问题的主要内容仍然存在.在21世纪,应该更多,更多,更容易和更直观地使用unicode**.是的,明白比"这里没有灵丹妙药".但框架开发人员(如上面的Mason2)确实*应该关心它.是的,我理解这是一项志愿者工作,当我不喜欢这个框架时,很容易不使用它.但perl中的所有**unicode疯狂**真的是HURTS perl本身. (14认同)
  • @wk:像'perl -i.bak -pe'/ foo/bar'这样的代码打破了很酷吗?世界上有很多这样的东西.你想要什么样的比较'eq`?一个UCA3比较?`lc`会把它变成UCA1吗?你怎么知道的?你如何匹配部分和/或不连续的字形?现在所有带有8位数据的旧代码都无法编译,这样可以吗?Perl不再适用于二进制数据吗?得到不同的答案是否可以?没有他们的同意,可以从人们手中抽出'az`吗?分解字素是否可以?排序代码可以减少100倍吗?filesys怎么样? (13认同)
  • @Mark:不,这不是多余的.我不知道谁会决定他们不想一路走到5.14.如果他们退缩得足够远,严格就会消失,我绝不希望这种情况发生.因此,这不是多余的.此外,它是声明性的,因此很有用.同样,我喜欢将`unicode_strings`功能显式化,以便人们意识到它有效.这就像我经常将事物初始化为0,即使我不需要:我喜欢表达我的意图.我不喜欢秘密的副作用. (13认同)
  • 好奇但是如果由于缺乏字体支持而导致许多unicode字符不可读的意图. (9认同)
  • @tchrist:谢谢你的回答,这有助于看到一幅大图.我仍然相信,解决你指出的所有问题至少需要10年.为了有效地解决它们,我们需要每天使用Unicode.如果我们说"Unicode问题过于复杂,让我们先解决它们并制作理想的解决方案,那么就使用",我们无法继续前进.大多数不断增长的软件在这个伟大的日子之前都不会包含UTF-8,即使是在最低级别.有一些明确的依赖点是**必须**(比如`utf8 :: all`,但我更喜欢核心).你可以称之为naïveté. (7认同)
  • 重新"认为某人姓名只能包含某些字符的代码是愚蠢,冒犯和错误的.",Unicode是否采用了以前称为Prince的艺术家的新名称? (6认同)
  • *"Unicode过于复杂而不安全"*; ) (4认同)
  • "一旦你用一个给定的名字成功创建一个文件就相信的代码,当你在它的封闭目录上运行ls或readdir时,你实际上会发现你创建它的名字的文件是错误的,破坏的和错误的." - 我说任何对其用户执行此操作的文件系统API都是问题的根源,而且应该修复它 - 而不是用户的代码.为什么这种行为被认为是正确的?用你的话说,这些文件系统API的作者应该......好吧,自己挑选任何可怕的惩罚,你似乎非常擅长. (4认同)
  • 即使安装了Symbola字体,MSIE 9也不会渲染骆驼和其他符号.同一台Windows 7 PC上的Firefox 3.6会对所有字符进行渲染. (3认同)
  • 我安装了Symbola,它没有在Chrome中修复它.不知道我是否需要重启?Unicode很难. (3认同)
  • 样板文件中的"use strict"是多余的,如果你说"使用5.14.0"那么它默认是打开的. (3认同)
  • @ jm666我非常感谢你:我们应该在*所有新代码中采用零容忍政策*相对于*Unicode兼容性.*是的,你必须区分字节和文本文件的二进制文件他们中有人物,但是从远古时代开始,受到严重虐待的PʀɪsᴏɴᴇʀsOꜰBɪʟʟ就不得不这样做了.就我而言,任何处理文本的新代码***必须承担并理解UNICODE***.我在上面提出了如何通过envaria有选择地升级现有程序的建议.但是每个人都需要知道Unicode.这与Perl有关. (3认同)
  • @tchrist如何判断我的帖子是否完全正确?我已经安装了symbola字体,它有很大的改进,但仍然有一些白色方块 - 应该有吗?我需要一个单元测试!!! (3认同)
  • @tchrist“试图将Unicode转换为ASCII的代码不仅是错误的,它的肇事者也永远不允许再次编程。” 所以您要解雇Stack Exchange团队? (3认同)
  • (1)你说的很多东西在理论上都是正确的,但在实践中 - 很少有人能够同时处理你所做的每一个规定,并且做了任何有用的事情; 当然,即使是需要perl程序员的公司中的很大一部分,也无法提供足够的数量.(2)即使启用默认情况下包含的样板是精神错乱,也没有理由将某个未来版本的Perl无法使用相应的`use $ version` pragma按需启用它.更疯狂的是,在perl中"启用Unicode"只需要50行代码. (3认同)
  • 最后要注意的是,这里的许多规定都暗示任何特定语言的个人(包括母语人士)都会对自己的语言有完全正确的理解; 在一般情况下,这种情况从来都不是真的,对于特定的个人而言,这种情况很少可以完全被视为可能; 它还假设总是需要"正确"的行为!程序的存在是为了服务于人,而不是完美 - 正确性只是始终如一地执行所需行为的副作用 - 即使期望的行为是"不正确的"! (3认同)
  • @wk:明显的问题是 utf8 出血,数据从支持 unicode 的上下文传输到不支持 unicode 的上下文(即:您在代码、数据库、主机环境(操作系统、文件系统等)中使用的第三方代码) ) ) 可能会出现危险情况。如果您没有为数据库接口使用准备好的/绑定的查询,我有坏消息给您。 (2认同)
  • 有趣的是,在安装了Symbola字体(Ubuntu/Chromium)之后,一些符号出现了,其他符号仍然是盒子,如果我突出显示并右键单击,Chromium提供搜索谷歌搜索该角色,这在上下文中完美展示菜单! (2认同)
  • 刚发现,因为"autodie"中的错误导致样板不能完全正常工作.当使用open qw(:utf8:std)pragma时,"use autodie"会稍微关闭它.所以要么打开,要么是autodie - 不是两者......;)(旧的perl bug:http://stackoverflow.com/questions/4959384/does-the-autodie-pragma-have-influence-on-the-encoding/4959646 #4959646) (2认同)
  • @ jm666:是的,没错.我忘了提到那个.我也找到了它.这很烦人.从技术上讲,它应该*真的*使用open qw <:encoding(UTF-8):std>`因为你应该使用严格版本的utf8,而不是松散版本. (2认同)
  • 您忘记提及编号53:假定字符具有可信任属性的代码被破坏.每个版本的Unicode标准都有其中的内容.你引用一个完美的例子:大写上标"A"是小写的.在任何情况下,这都不是正确的,所以你可以打赌,这将在未来发生变化,打破你今天写的所有代码.而且我知道Unicode所做的关于未来验证的"保证",它们几乎和Unicode一样破碎. (2认同)
  • 现在是 2015 年,我有一个完整修补的操作系统/浏览器(Windows 7 上的 Firefox),但该答案的一部分仍然无法正确呈现。我该怎么做才能看到这个答案,因为它应该被看到(或者所有坏控制字符都是重点的一部分?) (2认同)

jro*_*way 95

处理Unicode文本有两个阶段.第一个是"如何输入并输出它而不会丢失信息".第二个是"我如何根据当地语言惯例处理文本".

tchrist的帖子涵盖了两者,但第二部分是他的帖子中99%的文本来自.大多数程序甚至不能正确处理I/O,因此在您开始担心规范化和整理之前理解这一点非常重要.

这篇文章旨在解决第一个问题

当您将数据读入Perl时,它并不关心它是什么编码.它分配一些内存并将字节存放在那里.如果你说print $str,它只是将那些字节闪存到你的终端,这可能会设置为假设写入它的所有内容都是UTF-8,并且你的文本显示出来.

奇妙.

除此之外,事实并非如此.如果您尝试将数据视为文本,您会看到发生了一些不好的事情.你只需length看看Perl对你的字符串的看法,以及你对你的字符串不一致的看法.写一个单行像:perl -E 'while(<>){ chomp; say length }'并输入????,你得到12 ...不正确的答案,4.

那是因为Perl认为你的字符串不是文本.你必须告诉它它之前的文本会给你正确的答案.

这很容易; Encode模块具有执行此操作的功能.通用入口点是Encode::decode(或use Encode qw(decode)当然).该函数从外部世界获取一些字符串(我们称之为"八位字节",一种说"8位字节"的方式),并将其转换为Perl将理解的一些文本.第一个参数是字符编码名称,如"UTF-8"或"ASCII"或"EUC-JP".第二个参数是字符串.返回值是包含文本的Perl标量.

(还有Encode::decode_utf8,假设编码为UTF-8.)

如果我们重写我们的单行:

perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Run Code Online (Sandbox Code Playgroud)

我们输入文字化け并得到"4"作为结果.成功.

那就是Perl中99%的Unicode问题的解决方案.

关键是,无论何时任何文本进入您的程序,您都必须对其进行解码.Internet无法传输字符.文件无法存储字符.您的数据库中没有字符.只有八位字节,你不能将八位字节视为Perl中的字符.您必须使用Encode模块将编码的八位字节解码为Perl字符.

问题的另一半是从程序中获取数据.这很容易; 你只是说use Encode qw(encode),决定你的数据编码是什么(UTF-8到了解UTF-8的终端,UTF-16用于Windows上的文件等),然后输出结果encode($encoding, $data)而不是输出$data.

此操作将Perl的字符转换为可由外部使用的八位字节,这是您的程序操作的字符.如果我们可以通过互联网或终端发送字符会容易得多,但我们不能:仅限八位字节.所以我们必须将字符转换为八位字节,否则结果是未定义的.

总结一下:编码所有输出并解码所有输入.

现在我们将讨论三个使这有点挑战的问题.首先是图书馆.他们是否正确处理文本?答案是......他们试试.如果您下载网页,LWP将以文本形式返回您的结果.如果调用正确的方法的结果,那就是(且恰好是decoded_content,不content,这只是一个字节流,它从服务器得到.)数据库驱动程序可呈片状; 如果你只使用带有Perl的DBD :: SQLite,它会解决,但是如果其他一些工具将文本存储为数据库中除UTF-8之外的某些编码......那么...它将无法正确处理直到你编写代码来正确处理它.

输出数据通常更容易,但是如果你看到"打印中的宽字符",那么你就知道你在某处弄乱了编码.这个警告意味着"嘿,你试图将Perl角色泄露给外界并且没有任何意义".您的程序似乎正常工作(因为另一端通常正确处理原始Perl字符),但它非常破碎,可能随时停止工作.用明确的方法修复它Encode::encode!

第二个问题是UTF-8编码的源代码.除非您use utf8在每个文件的顶部说,Perl不会假设您的源代码是UTF-8.这意味着每次你说出类似的话my $var = '??',你都会在你的程序中注入垃圾,这将完全打破所有可怕的事情.你不必为"使用UTF8",但如果你不这样做,你一定不能在程序中使用任何非ASCII字符.

第三个问题是Perl如何处理过去.很久以前,没有Unicode这样的东西,Perl认为一切都是Latin-1文本或二进制文件.因此,当数据进入您的程序并开始将其视为文本时,Perl会将每个八位字节视为Latin-1字符.这就是为什么当我们要求"文字化け"的长度时,我们得到了12. Perl假设我们使用的是Latin-1字符串"æååã"(这是12个字符,其中一些是非打印字符).

这被称为"隐式升级",这是一个非常合理的事情,但如果您的文本不是Latin-1,那么它不是您想要的.这就是明确解码输入的关键所在:如果你不这样做,Perl就会这样做,而且它可能会做错.

人们遇到麻烦,其中一半的数据是正确的字符串,有些仍然是二进制的.Perl将解释仍然是二进制的部分,就好像它是Latin-1文本,然后将它与正确的字符数据结合起来.这将使它看起来像处理你的角色正确地破坏你的程序,但实际上,你只是没有足够的修复它.

这是一个例子:你有一个程序读取一个UTF-8编码的文本文件,你将Unicode添加PILE OF POO到每一行,然后打印出来.你写它像:

while(<>){
    chomp;
    say "$_ ";
}
Run Code Online (Sandbox Code Playgroud)

然后运行一些UTF-8编码数据,例如:

perl poo.pl input-data.txt
Run Code Online (Sandbox Code Playgroud)

它在每行末尾用便便打印UTF-8数据.完美,我的程序有效!

但不,你只是做二进制连接.您正在从文件中读取八位字节,删除\n带有chomp的八位字节,然后添加字符的UTF-8表示中的PILE OF POO字节.当您修改程序以解码文件中的数据并对输出进行编码时,您会注意到垃圾("ð©")而不是poo.这将使您相信解码输入文件是错误的.不是.

问题是,poo被隐式升级为latin-1.如果use utf8要创建文字文本而不是二进制文件,那么它将再次起作用!

(这是我在帮助人们使用Unicode时遇到的头号问题.他们确实做到了正确而且打破了他们的计划.这就是未定义的结果令人遗憾:你可以长时间使用一个工作程序,但是当你开始修复它时,不要担心;如果你要将编码/解码语句添加到你的程序中它会中断,这只意味着你还有更多工作要做.下一次,当你从头开始设计Unicode时,它将是更容易!)

这就是你需要了解的关于Perl和Unicode的所有信息.如果您告诉Perl您的数据是什么,它在所有流行的编程语言中都具有最佳的Unicode支持.如果您认为它会神奇地知道您正在提供哪种文本,那么您将不可撤销地丢弃您的数据.仅仅因为您的程序今天在您的UTF-8终端上运行并不意味着它将在明天的UTF-16编码文件上运行.所以现在就把它安全吧,让自己省去丢弃用户数据的麻烦!

处理Unicode的简单部分是编码输出和解码输入.困难的部分是找到所有输入和输出,并确定它是什么编码.但这就是为什么你得到了大笔钱:)

  • 原理解释得很好,但是缺少 I/O 的实用方法。显式使用 Encode 模块既乏味又容易出错,并且使得阅读有关 I/O 的代码非常痛苦。I/O 层提供了一种解决方案,因为它们可以根据需要透明地进行编码和解码。`open` 和 `binmode` 允许它们的规范,并且 pragma `open` 设置默认值,正如 tchrist 在他的回答中建议的那样。 (2认同)

小智 48

我们都同意这是一个困难的问题,原因很多,但这正是试图让每个人都更容易理解的原因.

CPAN上有一个最近的模块,即utf8 :: all,它试图"打开Unicode.所有这些".

正如已经指出的那样,你不能神奇地使整个系统(外部程序,外部Web请求等)也使用Unicode,但我们可以合作制作合理的工具,使常见问题更容易.这就是我们是程序员的原因.

如果utf8 :: all没有做你认为应该做的事情,让我们改进它以使其更好.或者让我们制作其他工具,这些工具可以一起满足人们不同的需求.

`

  • @tchrist utf8 :: all的问题跟踪器就在这里.https://github.com/doherty/utf8-all/issues他们很想听听你的建议. (13认同)
  • 我在引用的`utf8 :: all`模块中看到了很多**改进空间**.它是在`unicode_strings`功能之前编写的,FɪɴᴀʟʟʏᴀɴᴅᴀᴛLᴏɴɢLᴀsᴛ修复了正则表达式对它们有一个`/ u`.我不相信它会对编码错误产生异常,这是你真正必须拥有的.它不会加载到`use charnames':full"`pragma,它尚未自动加载.它不会对`[az]`和`printf`字符串宽度发出警告,使用`\n`而不是`\ R`和`.`而不是`\ X`,但也许那些更像是`` Perl ::评论家的事.如果是我,我会加入进出. (5认同)
  • @Schwern:ᴇɴᴏᴛᴜɪᴛs,但我可以随意偷窃和抄袭我在这里写的东西.说实话,我仍然感觉/学习可以做什么和应该做什么,以及在哪里.这是卸载排序的一个很好的例子:`unichars -gs'/(?=\P {Ll})\ p {Lower} |(?=\P {Lu})\ p {Upper}/x'| ucsort --upper | 猫-n | 少-r`.同样,很少有预处理步骤,如`... | ucsort --upper --preprocess ='s /(\ d +)/ sprintf"%#012d",$ 1/ge'`也非常好,而且我不想让别人做出决定.我仍在[构建我的Unicode工具箱](http://training.perl.com/scripts/). (4认同)

bri*_*foy 34

我认为你误解了Unicode及其与Perl的关系.无论您以何种方式存储数据,Unicode,ISO-8859-1或许多其他内容,您的程序都必须知道如何解释它作为输入(解码)获得的字节以及如何表示它想要输出的信息(编码) ).得到错误的解释,你搞砸了数据.你的程序中没有一些神奇的默认设置可以告诉你程序之外的东西如何行动.

你认为这很难,很可能,因为你习惯了所有的ASCII.您应该考虑的所有内容都被编程语言和它必须与之交互的所有内容所忽略.如果一切都只使用UTF-8而你别无选择,那么UTF-8也会如此简单.但并非所有东西都使用UTF-8.例如,你不希望你的输入句柄认为它正在获得UTF-8八位字节,除非它实际上是,并且如果从它们读取的东西可以处理UTF-8你不希望你的输出句柄是UTF-8 .Perl无法知道这些事情.这就是你是程序员的原因.

我不认为Perl 5中的Unicode太复杂了.我认为这很可怕,人们会避免它.有区别.为此,我将Unicode放入Learning Perl,第6版,并且在Effective Perl Programming中有很多Unicode内容.您必须花时间学习和理解Unicode及其工作原理.否则你无法有效地使用它.

  • 我认为你有一点:这很可怕.应该是吗?对我来说是Unicode祝福,在Perl5中使用它不是(我不假设任何东西是ASCII,我的母语至少需要iso8859-4).我安装了Rakudo,我尝试使用UTF-8(在这个有限的沙箱中)开箱即用.我错过了什么?我再次强调它:拥有微调的Unicode支持是好的,但在大多数时候都不需要.为了让人对主题产生恐惧,一种方法是每个人都要阅读很多内容来理解内部.其他:我们有特殊的pragma,所以`使用utf8_everywhere`会让人开心.为什么不是最后一个? (3认同)
  • 我仍然认为你错过了这一点.什么有用?您不需要了解内部.您需要了解*externals*以及如何处理具有不同编码和相同字符的不同表示的字符串.再次阅读汤姆的建议.大多数他说的我打赌你会发现Rakudo不适合你. (3认同)
  • @brian d foy:我认为那些限制很好,就像tchrist说的那样,每个方面都没有灵丹妙药(我承认:在这里提出这个问题之前我没有看过大多数这些问题).因此,当我们用utf8 :: all之类的东西覆盖很多基本内容时,并不需要每个人都可以构建自己的巨大样板,只是为了让utf8处理工作的基础知识."完全没有恐惧",我的意思是:每个人都可以开始他的项目,知道基本知识已被涵盖.是的,你是对的,还有很多问题.但是当开始更容易时,我们将有更多人参与解决这些问题.恕我直言 (2认同)

小智 28

在阅读这个主题时,我经常会觉得人们使用" UTF-8 "作为" Unicode " 的同义词.请区分Unicode的"代码点",它是ASCII代码的放大相对和Unicode的各种"编码".其中有一些,其中UTF-8,UTF-16UTF-32是现有的,还有一些是过时的.

请UTF-8(以及所有其他编码)存在并且仅在输入或输出中具有意义.在内部,从Perl 5.8.1开始,所有字符串都保存为Unicode"Code-points".没错,你必须启用一些以前令人敬佩的功能.

  • 我同意人们常常将Uɴɪᴄᴏᴅᴇ与UTF-8⧸16⧸32混淆,但**从根本上和批判性的*不正确*Uɴɪᴄᴏᴅᴇ只是相对于ᴀsᴄɪɪ的一些放大的字符集.**最多,这只不过是ɪsᴏ-10646](http://en.wikipedia.org/wiki/Universal_Character_Set#Differences_between_ISO_10646_and_Unicode).**Uɴɪᴄᴏᴅᴇ包含更多**:*排序规则,案例折叠,规范化形式,字形集群,单词和换行符,脚本,数字等值,宽度,双向性,字形变体,上下文行为,区域设置,正则表达式,组合类, 100多个属性,等等!* (19认同)
  • @tchrist:第一步是将数据导入您的程序,然后外部世界不会丢失它.然后你可以担心整理,案例折叠,字形变种等宝贝步骤. (15认同)
  • 我同意,将perl变为垃圾输入或输出必须是首要任务.我想要的是有一个模块或编译指示可以体现下面的虚构对话:" - 亲爱的Perl.对于这个程序,所有的输入和输出都将是UTF-8.你能不能丢弃我的数据吗? - 所以你说的只有UFT-8.你确定吗? - 是的. - 真的,非常确定吗? - 绝对. - 你接受我可能会因为我提供非UTF-8数据而表现得很奇怪吗? - 是的,很好. - 那好吧." (7认同)

gee*_*aur 10

在野外有一个真正令人恐怖的古代代码,其中大部分都是常见的CPAN模块.我发现我已经是相当小心使Unicode的,如果我使用,可能受其影响的外部模块,并且现在还在中我经常使用(特别是一些Perl脚本来识别和修复一些Unicode相关故障,iTiVo失败严重因为转码问题而导致的任何非7位ASCII的事情.

  • 没有选项的单独`-C`是**错误且容易出错**.你打破世界.将`PERL5OPT`设置为`-C`,您将看到我的意思.我们在v5.8中尝试过这种方式,这是一场灾难.您根本不能也不能告诉那些不期望它的程序现在他们正在处理Unicode,无论他们喜欢与否.还存在安全问题.至少,如果传递二进制数据,任何"打印而<>"的东西都会破坏.所有数据库代码也是如此.这是一个糟糕的主意. (3认同)