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

Why does modern Perl avoid UTF-8 by default?

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

我知道核心Perl脚本存在许多遗留问题,在这些问题上它可能会破坏某些东西。但是,从我的观点来看,在21世纪,大的新项目(或者有大的视角的项目)应该让他们的软件UTF-8从头开始证明。但我还是看不到。例如,驼鹿启用严格和警告,但不启用Unicode。Modern::Perl也减少了样板文件,但没有UTF-8处理。

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

评论@tchrist的时间太长,所以我在这里添加了它。

我好像没说清楚。让我试着增加一些东西。

我和克赖斯特的看法很相似,但我们的结论完全相反。我同意,使用Unicode的情况很复杂,但这就是为什么我们(Perl用户和编码人员)需要一些层(或pragma),这使得UTF-8处理变得像现在一样简单。

TChrist指出了很多要涵盖的方面,我会读几天甚至几周来思考它们。不过,这不是我的观点。tchrist试图证明"启用utf-8"没有一种单一的方法。我没有那么多的知识来反驳这一点。所以,我坚持活生生的例子。

我和拉库多一起玩过,UTF-8就在我需要的时候。我没有任何问题,只是起作用了。也许在更深的地方有一些限制,但是在开始的时候,我测试的所有东西都像我预期的那样工作。

这难道不是现代Perl 5的目标吗?我更强调:我并不是建议将utf-8作为核心Perl的默认字符集,而是建议为开发新项目的人员使用快照来触发它。

另一个例子,但语气更为消极。框架应该使开发更容易。几年前,我尝试过Web框架,但因为"启用UTF-8"太模糊,所以把它们扔掉了。我找不到如何和在何处挂接Unicode支持。这太费时了,我发现走老路比较容易。现在我看到这里有一个赏金来处理与Mason2相同的问题:如何使Mason2utf-8干净?因此,这是一个非常新的框架,但是将它与UTF-8结合使用需要对其内部结构有深入的了解。就像一个红色的大标志:停下来,别用我!

我真的很喜欢Perl。但是处理unicode是很痛苦的。我仍然发现自己在靠墙跑。某种程度上,tchrist是对的,它回答了我的问题:新项目不吸引utf-8,因为它在Perl 5中过于复杂。


??????????????????????????&???????????&????????&?????&??????????????????????????????????????????????????????????????????????????是吗??????????????????????????&????????????????????????????????????????????

  • 把你的PERL_UNICODE设为可嫉妒的AS。这使得所有Perl脚本都将@ARGV解码为utf?8个字符串,并将所有三个stdin、stdout和stderr的编码都设置为utf?8。这两者都是整体效应,而不是词汇效应。好的。

  • 在源文件(程序、模块、库、doHickey)的顶部,突出地声明您正在运行Perl 5.12版或更高版本,方法是:好的。

    use v5.12; # minimal for unicode string feature好的。

    use v5.14; # optimal for unicode string feature好的。

  • 启用警告,因为以前的声明只启用严格和功能,而不启用警告。我还建议将Unicode警告提升为异常,因此使用这两行,而不仅仅是其中一行。但是,请注意,在v5.14下,utf8警告类包含三个可单独启用的子警告:noncharsurrogatenon_unicode。这些你可能希望对其施加更大的控制。好的。

    use warnings;好的。

    use warnings qw( FATAL utf8 );好的。

  • 声明此源单元编码为utf?8。尽管从前这个语用法做了其他的事情,但现在它只为这一个单独的目的服务,而不是为其他目的服务:好的。

    use utf8;好的。

  • 声明在这个词汇范围内打开文件句柄但在其他地方不打开文件句柄的任何操作都假定流是以UTF编码的?8除非你另有说明。这样就不会影响其他模块或其他程序的代码。好的。

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

  • 通过
    {CHARNAME}
    启用命名字符。好的。

    use charnames qw( :full :short );好的。

  • 如果您有一个DATA句柄,则必须显式设置其编码。如果你想用UTF?8,然后说:好的。

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

  • 当然,您最终可能会发现自己所关心的其他问题没有尽头,但是这些问题足以近似于状态目标,即"使所有事情都只与UTF一起工作"。8",尽管对这些术语的理解有所减弱。好的。

    另一个pragma虽然与Unicode无关,但它是:好的。

    1
          use autodie;

    强烈推荐。好的。&是吗??是否?????&??????????????&?????????????????是否??

    说"Perl应该(不知何故)!]默认情况下,启用Unicode"甚至不会开始考虑在某些罕见和孤立的情况下说足够多的话,甚至是稍微有用的话。unicode不仅仅是一个更大的字符库;它也是这些字符以多种方式交互的方式。好的。

    即使是一些人认为他们想要的最简单的措施,也会不幸地打破数以百万计的代码行,这些代码没有机会"升级"到你新的勇敢的新世界现代性。好的。

    这比人们假装的要复杂得多。在过去的几年里,我一直在思考这个问题。我很想被证明我错了。但我不认为我是。Unicode从根本上来说比您想要强加给它的模型要复杂得多,而且这里的复杂性是您永远无法在地毯下扫描的。如果你尝试,你会破坏你自己的代码或者其他人的代码。在某个时候,你只需要破坏并学习Unicode是关于什么的。你不能假装它不是什么东西。好的。

    ??努力使unicode变得简单,比我以前用过的任何东西都要简单得多。如果你认为这不好,试试别的。然后回来??:要么你会回到一个更好的世界,要么你会带来同样的知识,这样我们就可以利用你的新知识来创造??更好地处理这些事情。好的。&是吗?????????????????????????????????????????????????????????????????????????????????????????????????????????

    至少,这里有一些看起来需要的东西??要"默认启用Unicode",如您所说:好的。

  • 全部??默认情况下,源代码应为UTF-8。你可以用use utf8export PERL5OPTS=-Mutf8来获得。好的。

  • 这个??DATA手柄应为UTF-8。您必须按每个包进行此操作,如binmode(DATA,":encoding(UTF-8)")中所述。好的。

  • 程序参数到??默认情况下,脚本应理解为UTF-8。export PERL_UNICODE=Aperl -CAexport PERL5OPTS=-CA。好的。

  • 标准输入、输出和错误流应默认为UTF-8。全部为export PERL_UNICODE=S,或部分为IO、和/或E。这就像perl -CS。好的。

  • 是否有其他手柄被打开??除非另有声明,否则应视为UTF-8;对于其中特定的一种,应视为export PERL_UNICODE=DIOexport PERL5OPTS=-CD起作用。这就使得所有人都得到了以东的支持。好的。

  • 覆盖两个基地加上你用export PERL5OPTS=-Mopen=:utf8,:std打开的所有溪流。见唯一。好的。

  • 您不想错过UTF-8编码错误。试试export PERL5OPTS=-Mwarnings=FATAL,utf8。确保您的输入流始终是binmoded到:encoding(UTF-8),而不仅仅是:utf8。好的。

  • 128–255之间的代码点应该理解为??作为相应的Unicode码位,而不仅仅是未经处理的二进制值。use feature"unicode_strings"export PERL5OPTS=-Mfeature=unicode_strings。这将使uc("\xDF") eq"SS""\xE9" =~ /\w/。一个简单的export PERL5OPTS=-Mv5.12或更好的版本也能得到。好的。

  • 指定的Unicode字符在默认情况下不启用,因此添加export PERL5OPTS=-Mcharnames=:full,:short,latin,greek或某些类似字符。请参见取消命名和tcgrep。好的。

  • 您几乎总是需要从标准的Unicode::Normalize模块访问各种类型的分解的函数。export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD,然后总是通过nfd运行传入的内容,从nfc运行传出的内容。我还没有知道这些的I/O层,但请参阅NFC、NFD、NFKD和NFKC。好的。

  • 字符串比较??使用eqnelccmpsort、&c&cc总是错误的。所以你需要的不是@a = sort @b,而是@a = Unicode::Collate->new->sort(@b)。不妨把它加到你的export PERL5OPTS=-MUnicode::Collate上。您可以缓存用于二进制比较的键。好的。

  • ??像printfwrite这样的内置模块对unicode数据做了错误的处理。前者需要使用Unicode::GCString模块,后者也需要使用Unicode::LineBreak模块。见UWC和Unifmt。好的。

  • 如果你想让它们算作整数,那么你就必须通过Unicode::UCD::num函数运行\d+捕获,因为?内置的ATOI(3)目前还不够聪明。好的。

  • 您要处理文件系统问题吗?文件系统。一些文件系统静默地强制转换为NFC;另一些则静默地强制转换为NFD。而其他人仍然在做其他事情。有些人甚至完全忽视了这件事,这会导致更大的问题。所以你必须做你自己的NFC/NFD处理来保持健康。好的。

  • 你所有的??涉及a-za-z的代码必须更改,包括m//s///tr///。你的代码被破坏了,这应该是一个引人注目的危险信号。但目前尚不清楚该如何改变。获得正确的属性,并了解它们的casefolds,比您想象的要困难。我每天都用独角兽和独角兽。好的。

  • 使用\p{Lu}的代码几乎和使用[A-Za-z]的代码一样错误。你需要用\p{Upper}来代替,并知道原因。是的,\p{Lowercase}\p{Lower}\p{Ll}\p{Lowercase_Letter}不同。好的。

  • 使用[A-Za-z]的代码更糟。不能使用\pL\p{Letter},需要使用\p{Alphabetic}。不是所有的字母都是字母,你知道的!好的。

  • 如果你在找??使用/[\$\@\%]\w+/的变量,则出现问题。您需要查找/[\$\@\%]\p{IDS}\p{IDC}*/,即使这样也不会考虑标点符号变量或包变量。好的。

  • 如果要检查空白,则应根据情况在\h\v之间进行选择。你不应该使用\s,因为它并不意味着[\h\v],这与大众的看法相反。好的。

  • 如果您使用
    作为线边界,或者甚至使用

    ,那么您做的是错误的。你必须使用
    ,这是不一样的!好的。

  • 如果您不知道何时以及是否调用Unicode::StringPrep,那么最好学习一下。好的。

  • 不区分大小写的比较需要检查两个事物是否是相同的字母,不管它们的发音是否相同。最简单的方法是使用标准的unicode::collate模块。Unicode::Collate->new(level => 1)->cmp($a, $b)。还有eq方法等等,你可能也应该学习matchsubstr方法。这些都有明显的优势?内置的。好的。

  • 有时这还不够,而您需要使用unicode::collate::locale模块,就像在Unicode::Collate::Locale->new(locale =>"de__phonebook", level => 1)->cmp($a, $b)中那样。假设Unicode::Collate::->new(level => 1)->eq("d","e")是正确的,但Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d"," e")是错误的。同样,"ae"和"?"如果你不使用本地语言,或者如果你使用英语的话,他们在冰岛的本地语言中是不同的。现在怎么办?我告诉你,这很难。您可以使用ucsort来测试其中的一些内容。好的。

  • 考虑如何匹配字符串"ni"中的模式cvcv(辅音、元音、辅音、元音)?O"。它的NFD形式——你最好记住把它放进去——变成了"ninx 303 o"。现在你打算怎么做?即使假装一个元音是[aeiou](顺便说一下,这是错误的),你也不能做类似(?=[aeiou])\X)的事情,因为即使在nfd中,一个像'?'不分解!不过,使用我刚刚向您展示的UCA比较,它将测试等于"o"。你不能依赖NFD,你必须依赖UCA。好的。

  • &是吗??是否?????????????????&?????????????????????????????是否??

    但这并不是全部。人们对Unicode有无数种错误的假设。直到他们理解这些事情,他们的??代码将被破坏。好的。

  • 假定它可以打开文本文件而不指定编码的代码被破坏。好的。

  • 假定默认编码是某种本机平台编码的代码被破坏。好的。

  • 假设日文或中文网页占用较少的UTF空间的代码?16比UTF?8是错的。好的。

  • 假定Perl使用UTF的代码?8内部错误。好的。

  • 假定编码错误总是引发异常的代码是错误的。好的。

  • 假定Perl代码点被限制为0x10_ffff的代码是错误的。好的。

  • 假定您可以将$/设置为可以使用任何有效行分隔符的代码是错误的。好的。

  • 在案例折叠上假定往返相等的代码,如lc(uc($s)) eq $suc(lc($s)) eq $s,是完全错误的。假设uc("σ")uc("?")都是"Σ",但lc("Σ")不可能同时返回这两个值。好的。

  • 假定每个小写代码点都有一个独特的大写代码点,或者反之亦然的代码被破坏。例如,"a"是没有大写的小写字母;而"?""?"都是字母,但不是小写字母;但是,它们都是没有相应大写版本的小写代码点。明白了吗?他们不是\p{Lowercase_Letter},尽管他们是\p{Letter}\p{Lowercase}。好的。

  • 假定更改大小写而不更改字符串长度的代码已中断。好的。

  • 假定只有两种情况的代码被破坏。还有乳脂酶。好的。

  • 假定只有字母有大小写的代码被破坏。除了字母之外,数字、符号甚至符号都有表壳。事实上,改变情况甚至可以使一些东西改变其主要的一般类别,例如,一个\p{Mark}变成一个\p{Letter}。它还可以使它从一个脚本切换到另一个脚本。好的。

  • 假定案例从不依赖区域设置的代码将被破坏。好的。

  • 假定Unicode给出了有关POSIX区域设置被破坏的图的代码。好的。

  • 假定您可以删除音调符号以获取基本的ASCII字母的代码是邪恶的,仍然的,破碎的,大脑受损的,错误的,以及死刑的理由。好的。

  • 假定发音符号\p{Diacritic}和标记\p{Mark}相同的代码被破坏。好的。

  • 假设\p{GC=Dash_Punctuation}覆盖的代码与\p{Dash}被破坏的代码相同。好的。

  • 假定破折号、连字符和减号是相同的代码,或者假定每个代码只有一个破折号和减号的代码是错误的。好的。

  • 假定每个代码点占用不超过一个打印列的代码被破坏。好的。

  • 假定所有\p{Mark}字符占用零打印列的代码都已中断。好的。

  • 假定相似字符相同的代码被破坏。好的。

  • 假定不相似的字符不相似的代码将被破坏。好的。

  • 假定一行中只有一个\X可以匹配的代码点数量有限制的代码是错误的。好的。

  • 假定\X不能以\p{Mark}字符开头的代码是错误的。好的。

  • 假定\X不能包含两个非\p{Mark}字符的代码是错误的。好的。

  • 假定它不能使用"\x{FFFF}"的代码是错误的。好的。

  • 假定非BMP代码点需要两个UTF-16(代理)代码单元的代码将编码为两个单独的UTF-8字符(每个代码单元一个)是错误的。它不是:它编码到单个代码点。好的。

  • 如果在生成的utf-8的开头放置了一个BOM,那么从utf-16或utf-32转码到utf-8的代码就会被破坏。这太蠢了,工程师应该把眼皮去掉。好的。

  • 假设CESU-8是一个有效的UTF编码错误。Likewise,code that thinks concoding U+0000 as "\xC0\x80"is UTF-8 is broken and wrong.这些人也需要眼睛治疗。

    okay.

  • 假设特征如>〔1〕〔1〕〔1〕〔1〕〔2〕〔1〕的时刻点〕左边的时刻点是错误的,因为事实上它们不正确。

    okay.

  • 假设你第一次输出X和第二次Y的字符,这些字符将以XY的字符出现,那是错误的。有时候他们不会

    okay.

  • 假设美国信息交换标准码对英文写作来说足够好的代码是愚蠢的,短视的,非法的,破碎的,邪恶的,错误的。离开他们的头!如果看上去太极端,我们可以妥协:亨瑟福斯,他们可能只是一只脚上的大脚趾(剩下的仍然是鸭子)。

    okay.

  • 假设所有的\p{Math}点代码都是错误的。

    okay.

  • 只有信件、文摘和理解是错误的。

    okay.

  • 假设^~是错误的标记。

    okay.

  • 假设埃多克斯1〔10〕是错误的。

    okay.

  • 他们中任何信都是错误的。

    okay.

  • 相信\p{InLatin}的法典和\p{Latin}的法典是一样的。

    okay.

  • 相信\p{InLatin}的代码,是最常用的,肯定是错的。

    okay.

  • 《守则》相信给$FIRST_LETTER的第一封信是在一些字母表和$LAST_LETTER中写成的,最后一封信是在同一个字母表中写成的,该字母表是[${FIRST_LETTER}-${LAST_LETTER}]的。

    okay.

  • 代码相信某人的名字只能包含某些性格是愚蠢的,进攻性的,错误的。

    okay.

  • ASCII试图减少Unicode的代码并不是错误的,其永久代码不应再被允许在编程中工作。周期我甚至不认为他们应该再次见到他们,因为他们显然没有这么好。

    okay.

  • 代码相信有某种方式可以假装文本文件编码不存在是破碎和危险的。也许把另一只眼睛也挖出来了。

    okay.

  • Code that converts unknown characters to ?is broken,stupid,braindead,and runs contrary to the standard recommendation,which says not to do that!RTFM for why not.

    okay.

  • 代码相信它可以可靠地估计一个未标记的文本文件的编码是一个致命混合物的胡布里斯和纳米比亚的标记。因为宙斯只会发出一个闪电

    okay.

  • 你相信可以使用的代码?给PAD和Justify Unicode数据的Widths是破碎和错误的。

    okay.

  • 如果代码认为一旦您成功地使用给定的名称创建了一个文件,那么当您在其封闭目录上运行lsreaddir时,您实际上会发现使用您创建的文件名创建的文件有问题、损坏和错误。别惊讶了!好的。

  • 认为UTF-16是固定宽度编码的代码是愚蠢的、破坏的和错误的。吊销他们的编程许可证。好的。

  • 处理一个平面上的代码点的代码与处理其他平面上的代码点不同,这实际上是错误的。回到学校去。好的。

  • 认为像/s/i这样的东西只能与"S""S"匹配的代码是错误的和错误的。你会很惊讶的。好的。

  • 使用\PM\pM*来查找图形集群而不是使用\X的代码是错误的,并且是错误的。好的。

  • 希望回到ASCII世界的人们应该被全心全意地鼓励这样做,为了纪念他们光荣的升级,他们应该免费提供一台预电动手动打字机来满足他们所有的数据输入需要。发送给他们的消息应该通过????????S电报,每行40个字符,由快递员亲手递送。停下来。好的。

  • &是吗???????????????????????????????????????????????????????????????????????????????????????????????????????

    最近我自己的样板看起来像这样:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    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__

    &是吗????????????????????????

    我不知道还有多少"默认Unicode"在?"你能得到比我写的还要多的东西。是的,我知道:你也应该使用Unicode::CollateUnicode::LineBreak。可能更多。好的。

    正如您所看到的,有太多的unicode东西您真的需要担心,以便永远存在"默认为unicode"之类的东西。好的。

    你会发现什么,就像我们以前一样??5.8,不可能把所有这些东西强加给从一开始就没有设计好的代码来解释它们。你善意的自私刚刚打破了整个世界。好的。

    即使你这样做了,仍然有一些关键问题需要大量的思考才能解决。没有可以翻转的开关。只有大脑,我的意思是真正的大脑,在这里就足够了。你必须学习很多东西。模退到手动打字机前,你简直不能指望在无知中偷偷摸摸。这是21??世纪,你不可能希望Unicode因故意的无知而消失。好的。

    你必须学会。时期。"一切都能正常工作"永远不会这么容易,因为这将保证很多事情都不能正常工作,这就使得人们认为"一切都能正常工作"的假设失效。好的。

    你可能会得到一些合理的违约,为极少数和非常有限的操作,但不需要考虑的事情比我认为你有很多。好的。

    作为一个例子,规范化排序将导致一些真正的头痛。???"\x{F5}"?'"o\x{303}"'?'"o\x{303}\x{304}"'?和"o\x{304}\x{303}"_?'是否应全部匹配'?但你到底要怎么做呢?这比看起来难,但这是你需要解释的。???好的。

    如果我对Perl有一点了解的话,那就是它的unicode位的作用和不作用,我向您保证:"???????????????????S??????????U???????????????????????????????????????????????????????"?"?好的。

    你不能仅仅改变一些默认值,顺利航行。我跑步是真的吗??将PERL_UNICODE设置为"SA",但仅此而已,即使这主要用于命令行。对于真正的工作,我要完成上面列出的所有步骤,我非常非常仔细地完成它。好的。?????DL??????????怎么办?聚氨基甲酸酯???P????U??????????NL POO????好啊。


    处理Unicode文本有两个阶段。第一个问题是"我如何在不丢失信息的情况下输入和输出它"。第二个问题是"如何根据当地语言惯例处理文本"。好的。

    奇里斯特的文章涵盖了这两个方面,但第二部分是他文章中99%的文本来自哪里。大多数程序甚至不能正确处理I/O,所以在开始担心规范化和排序之前,了解这一点很重要。好的。

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

    当您将数据读取到Perl中时,它不关心它是什么编码。它分配一些内存并将字节存储在那里。如果你说print $str,它只会将这些字节直接发送到你的终端,而终端可能会设置为假定所有写入它的内容都是utf-8,你的文本就会出现。好的。

    不可思议的。好的。

    但事实并非如此。如果您试图将数据视为文本,您将看到发生了一些错误。您只需进一步观察length,就可以看到Perl对字符串的看法和您对字符串的看法不一致。写一行像:perl -E 'while(<>){ chomp; say length }',输入文字化け,你得到12…回答不正确,4.好的。

    这是因为Perl假定您的字符串不是文本。你必须先告诉它是文本,然后它才能给你正确的答案。好的。

    这很简单;编码模块有这样的功能。通用入口点是Encode::decode(当然也可以是use Encode qw(decode))。这个函数从外部世界获取一些字符串(我们称之为"八位字节",这是一种奇特的说法"8位字节"),并将其转换为Perl可以理解的文本。第一个参数是字符编码名称,如"utf-8"或"ascii"或"euc-jp"。第二个参数是字符串。返回值是包含文本的Perl标量。好的。

    (也有Encode::decode_utf8,它假定编码为utf-8。)好的。

    如果我们重写一行代码:好的。

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

    我们输入文字并得到"4"。成功。好的。

    这就是Perl中99%的Unicode问题的解决方案。好的。

    关键是,每当程序中出现任何文本时,必须对其进行解码。因特网不能传输字符。文件不能存储字符。数据库中没有字符。只有八位字节,在Perl中不能将八位字节视为字符。必须使用编码模块将编码的八位字节解码为Perl字符。好的。

    另一半的问题是从程序中获取数据。这很容易,只需说use Encode qw(encode),就可以确定数据的编码方式(utf-8到能够理解utf-8、utf-16在Windows上的文件等的终端),然后输出encode($encoding, $data)的结果,而不只是输出$data的结果。好的。

    此操作将Perl的字符(即程序所操作的字符)转换为外部世界可以使用的八位字节。如果我们可以通过互联网或终端发送字符,那就容易多了,但我们不能:只发送八位字节。所以我们必须将字符转换成八位字节,否则结果是未定义的。好的。

    总结:对所有输出进行编码并对所有输入进行解码。好的。

    现在,我们将讨论三个问题,这些问题使这项工作有点具有挑战性。第一个是图书馆。他们处理文本是否正确?答案是…他们尝试。如果你下载了一个网页,lwp会把你的结果作为文本返回给你。如果对结果调用正确的方法,也就是说(恰好是decoded_content,而不是content,它只是从服务器中得到的八位字节流。)数据库驱动程序可能会不稳定;如果只使用perl就使用dbd::sqlite,它会成功,但如果其他工具在数据B中放入了以utf-8以外的编码形式存储的文本。ASE…好。。。只有编写代码来正确地处理它,才能正确地处理它。好的。

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

    第二个问题是UTF-8编码的源代码。除非在每个文件的顶部说use utf8,否则Perl不会假定源代码是utf-8。这意味着,每次你说像my $var = 'ほげ'这样的话,你都会将垃圾注入你的程序,这会彻底破坏所有的东西。您不必"使用utf8",但如果不使用,则程序中不能使用任何非ASCII字符。好的。

    第三个问题是Perl如何处理过去。很久以前,还没有Unicode这样的东西,Perl认为一切都是拉丁语1文本或二进制。因此,当数据进入程序并开始将其视为文本时,Perl将每个八位字节视为拉丁-1字符。这就是为什么,当我们要求"文字化"的长度时,我们得到了12。Perl假设我们在操作拉丁-1字符串"?????"(12个字符,有些是非打印字符)。好的。

    这被称为"隐式升级",这是一件非常合理的事情,但如果你的文本不是拉丁语-1,这不是你想要的。这就是为什么显式解码输入很关键:如果不这样做,Perl会这样做,而且它可能会出错。好的。型

    人们会遇到这样的问题:一半的数据是一个合适的字符串,有些数据仍然是二进制的。Perl将解释仍然是二进制的部分,就像它是拉丁-1文本一样,然后将其与正确的字符数据结合起来。这会使你看起来像是正确地处理你的角色,破坏了你的程序,但实际上,你只是没有足够的修复它。好的。型

    这里有一个例子:你有一个程序读取一个UTF-8编码的文本文件,你在每一行上附加一个Unicode PILE OF POO,然后打印出来。你写得像:好的。型

    1
    2
    3
    4
    while(<>){
        chomp;
        say"$_ ??";
    }

    然后运行一些UTF-8编码的数据,比如:好的。型

    1
    perl poo.pl input-data.txt

    它用每行末尾的poo打印utf-8数据。太好了,我的程序工作了!好的。型

    但不,你只是在做二进制连接。您正在从文件中读取八位字节,用chomp删除一个
    ,然后在PILE OF POO字符的utf-8表示中附加字节。当您修改程序来解码文件中的数据并对输出进行编码时,您会注意到您收到了垃圾("e?")而不是大便。这将使您相信解码输入文件是错误的。不是。好的。型

    问题是,poo被隐式升级为拉丁语1。如果您使用use utf8来制作文本而不是二进制,那么它将再次工作!好的。型

    (这是我在帮助Unicode用户时看到的第一个问题。他们做得不好,这破坏了他们的计划。这就是未定义结果的悲哀之处:你可以有一个长期有效的程序,但是当你开始修复它时,它就坏了。别担心,如果你正在向你的程序中添加编码/解码语句,它会中断,这就意味着你有更多的工作要做。下一次,当您从一开始就考虑使用Unicode时,这样做会容易得多!)好的。型

    这就是关于Perl和Unicode的全部知识。如果您告诉Perl您的数据是什么,它在所有流行的编程语言中都有最好的Unicode支持。但是,如果你认为它会神奇地知道你在给它输入什么样的文本,那么你将不可避免地丢弃你的数据。仅仅因为您的程序今天在UTF-8终端上工作,并不意味着它明天在UTF-16编码文件上工作。所以现在要确保安全,避免破坏用户数据的麻烦!好的。型

    处理Unicode的简单部分是编码输出和解码输入。最困难的部分是找到所有的输入和输出,并确定它是哪种编码。但这就是为什么你能赚大钱的原因:)好的。型好啊。


    我们一致认为这是一个困难的问题,原因很多,但这正是让每个人都更容易相处的原因。

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

    正如已经指出的,您不能神奇地使整个系统(外部程序、外部Web请求等)也使用Unicode,但我们可以一起工作,使处理常见问题变得更容易的明智工具。这就是我们成为程序员的原因。

    如果utf8::all不做您认为应该做的事情,那么让我们改进它以使其更好。或者让我们一起制作其他工具,它们可以同时满足人们的不同需求。

    `


    我认为您误解了Unicode及其与Perl的关系。无论您以何种方式存储数据、Unicode、ISO-8859-1或其他许多东西,您的程序都必须知道如何将它得到的字节解释为输入(解码),以及如何表示它想要输出的信息(编码)。把那个解释搞错了,你就把数据弄混了。在你的程序中没有什么神奇的默认设置可以告诉程序外的东西如何操作。

    你认为这很难,很可能,因为你已经习惯了所有的ASCII码。您应该考虑的所有事情都被编程语言和它必须与之交互的所有事情所忽略。如果除了utf-8什么都不用,而你别无选择,那么utf-8也同样简单。但并不是所有东西都使用UTF-8。例如,您不希望输入句柄认为它得到的是UTF-8八位字节,除非它是真的,并且如果从它们读取的内容能够处理UTF-8,您也不希望输出句柄是UTF-8。Perl无法知道这些事情。这就是为什么你是程序员。

    我不认为Perl5中的Unicode太复杂。我觉得这很可怕,人们会避开它。有区别。为此,我将Unicode用于学习Perl,第6版,在有效的Perl编程中有很多Unicode的东西。您必须花时间学习和理解Unicode及其工作原理。否则你将无法有效地使用它。


    在阅读这篇文章的时候,我经常会觉得人们使用"utf-8"作为"unicode"的同义词。请区分Unicode的"码位"(ASCII码的放大相对码)和Unicode的各种"编码"。其中有一些是目前使用的UTF-8、UTF-16和UTF-32,还有一些是过时的。

    请注意,UTF-8(以及所有其他编码)存在,并且仅在输入或输出中有意义。在内部,自Perl5.8.1以来,所有字符串都保持为Unicode"代码点"。是的,您必须启用一些功能,如前所述。


    在野外有相当数量的古老代码,其中大部分是以公共CPAN模块的形式存在的。我发现,如果我使用可能受Unicode影响的外部模块,我必须非常小心地启用Unicode,并且仍在尝试在我经常使用的几个Perl脚本中识别和修复一些与Unicode相关的故障(特别是,由于转码问题,ITIVO在任何非7位ASCII的问题上都会严重失败)。


    您应该启用unicode字符串功能,如果使用v5.14,这是默认的;

    您不应该真正地使用Unicode标识符,尤其是通过UTF8对外部代码使用Unicode标识符,因为它们在Perl5中是不安全的,只有CPerl能够做到这一点。如http://perl11.org/blog/unicode-identifiers.html

    关于文件句柄/流的utf8:您需要自己决定外部数据的编码。库不知道这一点,因为甚至libc都不支持utf8,所以很少有合适的utf8数据。还有更多的wtf8,在utf8周围的窗口畸变。

    顺便说一句:驼鹿并不是真正的"现代珍珠",他们只是劫持了这个名字。moose是完美的Larry Wall风格的后现代Perl,与bjarne stroustrup风格的任何东西混合在一起,适当的Perl6语法有折衷的偏差,例如使用字符串来表示变量名、糟糕的字段语法,以及一个非常不成熟的简单实现,比适当的实现慢10倍。CPerl和Perl6是真正的现代Perl,其形式遵循函数,实现被简化和优化。