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

ddarikpa  于 7个月前  发布在  Perl
关注(0)|答案(7)|浏览(90)

我想知道为什么大多数使用Perl构建的现代解决方案在默认情况下不启用UTF-8
我知道核心Perl脚本有许多遗留问题,在那里它可能会破坏东西。(或者有大视角的项目)应该让他们的软件从零开始进行UTF-8验证。但我仍然没有看到它发生。例如,Moose启用严格和警告,Modern::Perl也减少了样板文件,但没有UTF-8处理。
为什么?在2011年的现代Perl项目中,有什么理由避免使用UTF-8吗?
@tchrist的评论太长了,所以我在这里添加它。
我好像没说清楚,让我试着补充一下。

tchrist和我看到的情况非常相似,但我们的结论完全相反。我同意,Unicode的情况很复杂,但这就是为什么我们(Perl用户和编码者)需要一些层(或pragma),使UTF-8处理像现在一样简单。
tchrist指出了很多方面,我会花几天甚至几周的时间阅读和思考。然而,这不是我的观点。tchrist试图证明没有一种方法可以“启用UTF-8”。我没有太多的知识来争论。所以,我坚持使用活生生的例子。

我用Rakudo和UTF-8只是在那里当我需要.我没有任何问题,它只是工作.也许有一些更深层次的限制,但在开始,我测试的所有工作,因为我的预期.
这难道不应该是现代Perl 5的目标吗?我更强调这一点:我并不是建议UTF-8作为核心Perl的默认字符集,我建议为那些开发项目的人快速触发它的可能性。
另一个例子,但语气更消极。框架应该使开发更容易。几年前,我尝试了Web框架,但只是把它们扔掉,因为“启用UTF-8”是如此晦涩难懂。我没有找到如何和在哪里挂钩Unicode支持。这是如此的时间-消费,我发现它更容易走老路.现在我看到这里有一个赏金来处理同样的问题与Mason 2:* How to make Mason2 UTF-8 clean? *.所以,这是一个相当新的框架,但是使用UTF-8需要深入了解它的内部结构。这就像一个大大的红色标志:停止,不要使用我!
我真的很喜欢Perl。但处理Unicode是痛苦的。我仍然发现自己碰壁。在某种程度上
tchrist**是正确的,并回答了我的问题:新项目不吸引UTF-8,因为它在Perl 5中太复杂了。

gijlo24d

gijlo24d1#

:#𝙍𝙚𝙘𝙤𝙢𝙢𝙚𝙣𝙙𝙖𝙩𝙞𝙤𝙣𝙨𝘿𝙞𝙨𝙘𝙧𝙚𝙩𝙚𝟕𝙎𝙞𝙢𝙥𝙡𝙚𝙨𝙩

1.将PERL_UNICODE可变量设置为AS。这将使所有Perl脚本将@ARGV解码为UTF-8字符串,并将stdin、stdout和stderr这三个字符串的编码设置为UTF-8。这两个都是全局效果,而不是词法效果。
1.在源文件(程序、模块、库、do hickey)的顶部,通过以下方式突出声明您正在运行perl 5.12或更高版本:

use v5.12;  # minimal for unicode string feature
use v5.14;  # optimal for unicode string feature

字符串
1.启用警告,因为前面的声明只启用strictures和features,而不是warning。我还建议将Unicode警告提升为异常,因此使用这两行,而不是其中一行。但是请注意,在v5.14下,utf8 warning类包含其他三个子警告,它们都可以单独启用:noncharsurrogatenon_unicode。您可能希望对它们施加更大的控制。

use warnings;
use warnings qw( FATAL utf8 );


1.声明这个源代码单位是以UTF-8编码的。虽然这个杂注曾经做过其他的事情,但它现在只服务于这个单一的目的,而不是其他:

use utf8;


1.声明任何在此词法作用域中打开文件句柄 * 而不是其他地方 * 的操作都假定该流是用UTF-8编码的,除非你另有说明。这样你就不会影响其他模块或其他程序的代码。

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


1.通过\N{CHARNAME}启用命名字符。

use charnames qw( :full :short );


1.如果你有一个DATA句柄,你必须显式地设置它的编码。如果你希望它是UTF-8,那么说:

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


当然,你最终可能会发现自己关心的其他问题是没有尽头的,但这些都足以接近国家的目标,即“让一切都能用UTF-8工作”,尽管这些术语的意义有所减弱。
另一个杂注,虽然它不是Unicode相关的,是:

use autodie;


强烈推荐。
🌴🐪🐫🐪🌞*𝕲𝖔𝕿𝖍𝖔𝖚𝖆𝖓𝖉𝕯𝖔𝕷𝖎𝖐𝖊𝖜𝖎𝖘𝖊 * 🌞🐪🐫🐪 🐁🌞🐪🐫🐪

你好你好🐪𝕮𝖔𝖉𝖊𝕬𝖜𝖆𝖗𝖊𝖀𝖓𝖎𝖈𝖔𝖉𝖊𝖋𝖔𝖗𝖕𝖑𝖆𝖙𝖊𝕭𝖔𝖎𝖑𝖊𝖗

我自己的样板这些天往往看起来像这样:

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 stack-dumped
#   exceptions *unless* we're in an try block, in
#   which case just cluck the stack dump instead
local $SIG{__WARN__} = sub {
    if ($^S) { cluck   "Trapped warning: @_" } 
    else     { confess "Deadly warning: @_"  }
};

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

__END__

你好𝖙𝖊𝖑𝖑𝖚𝕭𝖈𝖎𝖌𝖆𝕸𝖔𝕹

说“Perl应该[以某种方式!]在默认情况下启用Unicode”甚至没有开始开始考虑在某种罕见和孤立的情况下说足够的话,甚至是微不足道的有用。Unicode不仅仅是一个更大的字符库;它也是这些字符以许多方式交互的方式。
即使是(一些)人似乎认为他们想要的头脑简单的最小措施,也肯定会悲惨地破坏数百万行代码,这些代码没有机会“升级”到你漂亮的新 * 美丽新世界 * 现代化。
它比人们假装的要复杂得多。在过去的几年里,我已经想了很多很多。我很想被证明我错了。但我不认为我错了。Unicode从根本上比你想强加给它的模型更复杂,这里的复杂性你永远不能扫到地毯下面。如果你尝试,你会破坏你自己的代码或别人的代码。在某种程度上,你只需要破坏并了解Unicode是关于什么的。你不能假装它不是。
Unicode比我用过的任何东西都要简单🐪🐪

你好你好𝕷𝖎𝖘𝖙𝕷𝖆𝖚𝖓𝖉𝖗𝖞🐪𝕬𝖜𝖆𝖗𝖊𝖀𝖓𝖎𝖈𝖔𝖉𝖊𝖆𝖋𝖔𝖗

至少,这里有一些东西似乎是必需的,以使Unicode“默认启用Unicode”,正如你所说的那样:
1.默认情况下,所有JavaScript源代码都应该是UTF-8。你可以使用use utf8export PERL5OPTS=-Mutf8来获得。
1.🐪DATA的句柄应该是UTF-8。你必须在每个包的基础上这样做,就像在binmode(DATA, ":encoding(UTF-8)")中一样。

  1. JavaScript脚本的程序参数默认应该理解为UTF-8。export PERL_UNICODE=Aperl -CAexport PERL5OPTS=-CA
    1.标准的输入、输出和错误流应该默认为UTF-8. export PERL_UNICODE=S,或者IO和/或E。这就像perl -CS
    1.除非另有声明,否则任何其他由export PERL_UNICODE=D打开的句柄都应该被视为UTF-8;对于其中的特定句柄,export PERL_UNICODE=Dio都可以使用; export PERL5OPTS=-CD也可以使用。这使得-CSAD适用于所有句柄。
    1.覆盖两个基础以及所有使用export PERL5OPTS=-Mopen=:utf8,:std打开的流。参见uniquote
    1.您不想错过UTF-8编码错误。尝试export PERL5OPTS=-Mwarnings=FATAL,utf8。并确保您的输入流始终是binmode d到:encoding(UTF-8),而不仅仅是:utf8
  2. 128-255之间的代码点🐪

1.默认情况下,命名Unicode字符不启用,因此添加export PERL5OPTS=-Mcharnames=:full,:short,latin,greek或类似的字符。参见uninamestcgrep
1.你几乎总是需要访问the standard Unicode::Normalize module各种类型的分解. export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD的函数,然后总是通过NFD运行传入的东西,从NFC运行出站的东西。据我所知,这些还没有I/O层,但请参阅nfcnfdnfkdnfkc

  1. eqnelccmpsort,&c&cc中的字符串比较总是错误的。因此,您需要@a = Unicode::Collate->new->sort(@b)而不是@a = sort @b。不妨将其添加到export PERL5OPTS=-MUnicode::Collate中。您可以缓存键以进行二进制比较。
  2. printfwrite等Unicode内置程序对Unicode数据做了错误的处理。对于前者,您需要使用the Unicode::GCString module,对于后者,您需要同时使用the Unicode::LineBreak moduleuwc。请参阅uwcunifmt
    1.如果你想把它们算作整数,那么你必须通过Unicode::UCD::num函数来运行你的\d+捕获,因为Unicode::UCD::num🐪
    1.你会在NFC文件系统上遇到文件系统问题。一些文件系统会默默地强制转换为NFC;另一些文件系统会默默地强制转换为NFD。还有一些文件系统会做其他的事情。有些甚至会完全忽略这个问题,这会导致更大的问题。所以你必须自己做NFC/NFD处理来保持理智。
    1.所有🐪涉及a-zA-Z必须更改的代码,包括m//s///tr///。这应该是一个尖叫的红旗,表明您的代码被破坏了。但不清楚它必须如何更改。获得正确的属性,并了解它们的大小写,比你想象的要难。我每天都用unicharsuniprops
    1.使用\p{Lu}的代码几乎和使用[A-Za-z]的代码一样错误。您需要使用\p{Upper},并知道原因。是的,\p{Lowercase}\p{Lower}\p{Ll}\p{Lowercase_Letter}不同。
    1.使用[a-zA-Z]的代码更糟糕。它不能使用\pL\p{Letter};它需要使用\p{Alphabetic}。不是所有的字母都是字母,你知道!
    1.如果你正在寻找/[\$\@\%]\w+/的封装变量,那么你会遇到一个问题,你需要寻找/[\$\@\%]\p{IDS}\p{IDC}*/,甚至没有考虑标点变量或包变量。
    1.如果你正在检查空白,那么你应该在\h\v之间选择,具体取决于。你永远不应该使用\s,因为它并不意味着[\h\v],与流行的看法相反。
    1.如果你使用\n作为线边界,甚至\r\n,那么你就错了。你必须使用\R,这是不一样的!
    1.如果你不知道什么时候以及是否调用Unicode::Stringprep,那么你最好学习。
    1.不区分大小写的比较需要检查两个东西是否是相同的字母,不管它们的变音符号等。最简单的方法是使用standard Unicode::Collate模块. Unicode::Collate->new(level => 1)->cmp($a, $b)。还有eq方法等,你可能也应该了解matchsubstr方法。这些方法比match内置方法有明显的优势。🐪
    1.有时候这还不够,你需要the Unicode::Collate::Locale模块来代替,比如Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b)。考虑Unicode::Collate::->new(level => 1)->eq("d", "ð")是true,但是Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð")是false。同样,如果你不使用locale,或者如果你使用英语locale,但是它们在冰岛locale中是不同的,那么“ae”和“”就是eq。现在怎么办?这很坚韧,我告诉你。你可以用ucsort来测试这些东西。
    1.考虑如何匹配模式CVCV(consonant,vowel,consonant,vowel)在字符串“niño"中。它的NFD形式--你最好记得把它放进去--变成了“ninño”。现在你要做什么?即使假设元音是[aeiou](顺便说一句,这是错误的),你也不能做像(?=[aeiou])\X)这样的事情,因为即使在NFD中,像''这样的代码点也不会分解!但是,使用我刚才展示的UCA比较,它将测试为“o”。您不能依赖NFD,必须依赖UCA。

你好你好你好你好𝕣𝔹𝕖𝕞𝕦𝕤𝕤𝕤𝕖𝕟𝕟𝕖𝕜𝕠

这🐪
1.假定可以在不指定编码的情况下打开文本文件的代码被破坏。
1.假定默认编码是某种本机平台编码的代码是不完整的。
1.假设日文或中文网页在UTF-16中占用的空间比在UTF-8中占用的空间少的代码是错误的。
1.假设Perl内部使用UTF-8的代码是错误的。
1.假定编码错误总是会引发异常的代码是错误的。
1.假定Perl代码点限制为0x10_FFFF的代码是错误的。
1.假设您可以将$/设置为与任何有效的行分隔符一起工作的代码是错误的。

1.假设大小写折叠的往返相等的代码,如lc(uc($s)) eq $suc(lc($s)) eq $s,是完全错误的。考虑到uc("σ")uc("ς")都是"Σ",但lc("Σ")不可能同时返回这两个。
1.假设每个字符串代码点都有一个不同的字符串,反之亦然的代码是不完整的。例如,"ª"是一个没有字符串的字符串;而"ᵃ""ᴬ"都是字母,但它们不是字符串;然而,它们都是没有对应的字符串版本的字符串代码点。明白了吗?它们是不是\p{Lowercase_Letter},尽管它们都是\p{Letter}\p{Lowercase}
1.假设改变大小写不会改变字符串长度的代码是不完整的。
1.假设只有两种情况的代码被破坏了。
1.假设只有字母才有大小写的代码被破坏了。除了字母,数字,符号,甚至标记都有大小写。事实上,改变大小写甚至可以改变它的主要类别,比如\p{Mark}变成\p{Letter}。它也可以让它从一个脚本切换到另一个脚本。
1.假定大小写从不依赖于区域设置的代码是不完整的。
1.假设Unicode给出了POSIX区域设置的代码是不完整的。
1.假设你可以删除变音符号来获得基本ASCII字母的代码是邪恶的,静止的,破碎的,大脑受损的,错误的,以及死刑的理由。
1.假设变音符号\p{Diacritic}和标记\p{Mark}是同一个东西的代码被破坏了。
1.假设\p{GC=Dash_Punctuation}覆盖了\p{Dash}的代码是不完整的。
1.假设破折号、连字符和减号彼此是同一个东西,或者每一个都只有一个的代码是不完整的,是错误的。
1.假定每个代码点占用不超过一个打印列的代码是不完整的。
1.假定所有\p{Mark}字符占用零个打印列的代码被破坏。
1.假设看起来像 * 的字符是 * 一样的代码是不完整的。
1.假设看起来不一样的字符看起来不一样的代码是错误的。
1.假设一行中的代码点数有限制,只有一个\X可以匹配的代码是错误的。
1.假设\X永远不能以\p{Mark}字符开头的代码是错误的。
1.假设\X永远不能容纳两个非\p{Mark}字符的代码是错误的。
1.假设不能使用"\x{FFFF}"的代码是错误的。
1.假设需要两个UTF-16(代理)代码单元的非BMP代码点将编码为两个单独的UTF-8字符(每个代码单元一个)的代码是错误的。它不是:它编码为单个代码点。
1.如果代码将BOM从UTF-16或UTF-32转换为UTF-8,那么如果它将BOM放在结果UTF-8的开头,那么代码就会被破坏。这是如此愚蠢,工程师应该去掉他们的眼皮。
1.假设CESU-8是一个有效的UTF编码的代码是错误的。同样,认为将U+0000编码为"\xC0\x80"是UTF-8的代码是错误的。这些家伙也应该得到眼睑治疗。
1.假设像>这样的字符总是指向右边,而<总是指向左边的代码是错误的,因为它们实际上并不是这样。
1.如果你先输出字符X,然后再输出字符Y,那么这些字符会显示为XY,这是错误的,有时候不会。
1.**认为ASCII码足以正确书写英语的代码是愚蠢的、短视的、文盲的、坏的、邪恶的和错误的。**砍掉他们的头!如果这看起来太极端,我们可以妥协:从今以后,他们只能用一只脚的大脚趾打字。(其余的将被胶带封住。)
1.假设所有\p{Math}代码点都是可见字符的代码是错误的。
1.假定\w只包含字母、数字和下划线的代码是错误的。
1.假设^~是标点符号的代码是错误的。
1.假设ü有元音变音的代码是错误的。
1.相信这样的东西包含任何字母的代码是错误的。
1.相信\p{InLatin}\p{Latin}相同的代码是恶意破坏的。
1.认为\p{InLatin}几乎总是有用的代码几乎肯定是错误的。
1.如果代码认为$FIRST_LETTER是某个字母表中的第一个字母,$LAST_LETTER是同一个字母表中的最后一个字母,那么[${FIRST_LETTER}-${LAST_LETTER}]就有任何意义,这几乎总是完全错误的,毫无意义的。
1.认为某人的名字只能包含某些字符的代码是愚蠢的,冒犯的,错误的。
1.试图将Unicode简化为ASCII的代码不仅仅是错误的,它的肇事者不应该被允许再次从事编程工作。

1.相信有某种方法可以假装文本文件编码不存在的代码是破碎的和危险的。还不如把另一只眼睛也戳出来。
1.将未知字符转换为?的代码是破碎的,愚蠢的,愚蠢的,并且与标准建议相反,它说**不要这样做!**RTFM为什么不。
1.相信自己能够可靠地猜出未标记文本文件的编码的代码犯了致命的傲慢和天真的混合错误,只有宙斯的 lightning 才能解决。
1.相信你可以使用1printf宽度来填充和对齐Unicode数据的代码是错误的。
1.代码相信一旦你成功地创建了一个给定名称的文件,当你在它的封闭目录上运行lsreaddir时,你会发现你创建的文件名是错误的,损坏的,错误的。不要对此感到惊讶!
1.认为UTF-16是固定宽度编码的代码是愚蠢的、不完整的和错误的。吊销他们的编程许可证。
1.如果一个平面上的代码点与其他平面上的代码点有一点不同,那么这段代码就 ipso facto 是错误的。回到学校去吧。
1.相信像/s/i这样的东西只能匹配"S""s"的代码是错误的。
1.使用\PM\pM*而不是使用\X来查找字素簇的代码是错误的。
1.我们应该全心全意地鼓励那些想回到ASCII世界的人这样做,为了向他们的光荣升级表示荣誉,我们应该免费为他们提供一台前电动手动打字机,以满足他们所有的数据输入需求。发给他们的信息应该通过电报机以每行40个字符的速度发送,并由信使亲自递送。停止。

你好𝖄𝕽𝕬𝕸𝕸𝖀𝕾

我🐪
正如你所看到的,有太多的Unicode的事情,你真的 * 做 * 必须担心有 * 永远 * 存在任何这样的事情“默认为Unicode”。
你🐪
即使你做到了,仍然有一些关键问题需要大量的思考才能解决。没有你可以翻转的开关。只有大脑,我的意思是 * 真实的大脑 *,在这里就足够了。你必须学习的东西太多了。考虑到退回到手动打字机,你根本不能希望在无知中溜走。这是21世纪的世纪,你不能因为故意的无知而希望Unicode消失。
你必须学会它。句号。它永远不会那么容易,“一切都只是工作,”因为这将保证很多东西工作-这使假设无效,有可能永远是一种方法,“使所有的工作。”
您可能能够为非常少且非常有限的操作获得一些合理的默认值,但这并不是没有考虑到比我认为的要多得多的事情。
举一个例子,规范排序会引起一些真实的麻烦。"\x{F5}" * 匹配“零”"o\x{303}" * 匹配“零”"o\x{303}\x{304}" * 匹配“零”"o\x{304}\x{303}" * 匹配“零”,但是你到底要怎么做呢?这比看起来要难,但是你需要考虑到这一点。
如果说我对Perl有什么了解的话,那就是它的Unicode位能做什么,不能做什么,我向你保证:**”字符串的Unicode位能做什么,不能做什么,“U”字符串的Unicode位能做什么,“U”字符串的Unicode位能做什么,“
你不能仅仅改变一些默认值就一帆风顺。🐪

😈😈

k3fezbri

k3fezbri2#

处理Unicode文本有两个阶段,第一个是“如何在不丢失信息的情况下输入和输出”,第二个是“如何根据本地语言惯例处理文本”。
tchrist的帖子涵盖了这两个方面,但是第二部分是他帖子中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为编码。)
如果我们重写我们的一行:

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

字符串
我们输入了文字化,得到的结果是“4”,成功了。
这就是Perl中99%的Unicode问题的解决方案。
关键是,无论什么时候任何文本进入你的程序,你都必须解码它。互联网不能传输字符。文件不能存储字符。你的数据库中没有字符。只有八位字节,你不能把八位字节当作Perl中的字符。你必须用Encode模块把编码的八位字节解码成Perl字符。
问题的另一半是从程序中获取数据,这很容易做到,你只需说出use Encode qw(encode),确定数据的编码方式(UTF-8到能够理解UTF-8的终端,UTF-16到Windows上的文件,等等),然后输出encode($encoding, $data)的结果,而不是只输出$data
这个操作将Perl的字符,也就是你的程序所使用的字符,转换成可以被外部世界使用的八位字节。如果我们可以通过互联网或我们的终端发送字符,这会容易得多,但是我们不能:只能是八位字节。所以我们必须将字符转换成八位字节,否则结果是未定义的。
总结一下:编码所有输出,解码所有输入。
现在,我们将讨论三个问题,这使得这一点有点挑战。第一个是库。他们处理文本正确吗?答案是......他们会尝试。如果你下载一个网页,LWP会把结果以文本的形式返回给你。如果你对结果调用了正确的方法,那就是(该驱动程序碰巧是decoded_content,而不是content,后者只是从服务器获得得八位字节流.)数据库驱动程序可能不稳定;如果您使用DBD::SQLite只使用Perl,它会解决的,但如果其他工具将文本以UTF-8以外的编码方式存储在数据库中,那么,除非您编写代码来正确处理文本,否则无法正确处理文本。
输出数据通常比较容易,但是如果你看到“打印中的宽字符”,那么你知道你在某个地方把编码搞砸了。这个警告意味着“嘿,你正试图把Perl字符泄露给外部世界,这没有任何意义”。你的程序看起来是工作的(因为另一端通常会正确处理原始Perl字符),但是它非常坏,随时都可能停止工作。用一个显式的Encode::encode来修复它!
第二个问题是UTF-8编码的源代码。除非你在每个文件的顶部说use utf8,否则Perl不会假设你的源代码是UTF-8。这意味着,每次你说my $var = 'ほげ'之类的话,你都在向你的程序中注入垃圾,这将彻底可怕地破坏一切。你不必“使用utf8”,但如果你不这样做,您 * 不能 * 在程序中使用任何非ASCII字符。
第三个问题是Perl如何处理The Past。很久以前,还没有Unicode这样的东西,Perl假设所有的东西都是拉丁1文本或二进制。所以,当数据进入程序并开始将其视为文本时,Perl会将每个八位字节视为拉丁1字符。这就是为什么,当我们询问“文字化"的长度时,我们得到了12个字符。Perl假设我们正在拉丁语1字符串“ææååã”(这是12个字符,其中一些是非打印字符)上操作。

这被称为“隐式升级”,这是一个完全合理的做法,但如果你的文本不是Latin-1,这不是你想要的。这就是为什么显式解码输入是至关重要的:如果你不这样做,Perl会这样做,它可能会做错。
当一半的数据是正确的字符串,而另一些仍然是二进制的时候,人们会遇到麻烦。Perl会将仍然是二进制的部分解释为拉丁1文本,然后合并将其与正确的字符数据结合起来。这会使它看起来像是正确处理字符破坏了程序,但实际上,你只是没有修复它。
下面是一个例子:你有一个程序,它读取一个UTF-8编码的文本文件,你在每行上加上一个Unicode PILE OF POO,然后把它打印出来。你可以这样写:

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


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

perl poo.pl input-data.txt


它打印UTF-8数据,每行末尾都有一个poo。太好了,我的程序可以工作了!
但不是,你只是在做二进制连接。你从文件中阅读八位字节,用chomp删除\n,然后在PILE OF POO字符的UTF-8表示中添加字节。当你修改程序来解码文件中的数据并编码输出时,你会注意到你得到的是垃圾(“垃圾”)而不是poo。这会让你相信解码输入文件是错误的。事实并非如此。
问题是poo被隐式地升级为latin-1。如果你use utf8使文字文本而不是二进制,那么它将再次工作!
(That这是我在帮助人们使用Unicode时看到的第一个问题。他们做对了一部分,这破坏了他们的程序。这就是未定义结果的可悲之处:你可以有一个工作了很长时间的程序,但是当你开始修复它时,它就坏了。不要担心;如果你正在给你程序添加encode/decode语句,而它中断了,这仅仅意味着你有更多的工作要做。2下次,当你从一开始就考虑到Unicode进行设计时,它会容易得多!)
这就是你需要知道的关于Perl和Unicode的全部。如果你告诉Perl你的数据是什么,它在所有流行的编程语言中有最好的Unicode支持。如果你假设它会神奇地知道你输入的是什么类型的文本,那么,你的程序今天可以在UTF-8终端上运行,并不意味着它明天也可以在UTF-8终端上运行。16编码的文件。所以现在让它安全,并保存自己的头痛破坏您的用户的数据!
处理Unicode的简单部分是编码输出和解码输入。困难的部分是找到所有的输入和输出,并确定它是哪种编码。但这就是为什么你会得到大笔巴克斯:)

np8igboo

np8igboo3#

我们都同意,这是一个困难的问题,有很多原因,但这正是试图使每个人都更容易的原因。
CPAN上最近有一个模块utf8::all,它试图“打开Unicode。全部”。
正如已经指出的那样,你不能神奇地让整个系统(外部程序,外部Web请求等)也使用Unicode,但我们可以共同努力,制作合理的工具,使解决常见问题变得更容易。
如果utf8::all没有做一些你认为它应该做的事情,让我们改进它,或者让我们做一些额外的工具,尽可能地满足人们不同的需求。
`

eqqqjvef

eqqqjvef4#

我认为您误解了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, 6th Edition中,而Effective Perl Programming中有很多Unicode的东西。你必须花时间学习和理解Unicode及其工作原理。否则你将无法有效地使用它。

bjg7j2ky

bjg7j2ky5#

在阅读这篇帖子的时候,我经常得到这样的印象,人们把“UTF-8“当作“Unicode“的同义词。请把Unicode的“码点”和Unicode的各种“编码”区分开来。Unicode的“码点”是ASCII码的一个放大的相对关系。有几种,其中UTF-8,UTF-16UTF-32是当前的版本,还有一些已经过时。
请,UTF-8(以及所有其他 * 编码 *)存在,并在输入或输出中只有意义.在内部,自Perl 5.8.1,所有字符串被保存为Unicode“Code-points”.真的,你必须启用一些功能,如钦佩前面介绍.

cnwbcb6i

cnwbcb6i6#

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

t8e9dugd

t8e9dugd7#

您应该启用Unicode字符串特性,如果使用v5.14;,这是默认设置。
你真的不应该使用Unicode标识符,特别是对于通过UTF-8的外来代码,因为它们在Perl 5中是不安全的;只有cperl才能做到这一点。
关于文件句柄/流的UTF-8:您需要自己决定外部数据的编码。库无法知道这一点,因为即使libc也不支持UTF-8,因此正确的UTF-8数据很少。有更多的WTF-8,Windows的UTF-8畸变。
顺便说一句:Moose是完美的Larry Wall风格的后现代Perl混合了Bjarne Stroustrup风格的一切,具有适当的Perl 6语法的折衷偏差,例如,使用字符串作为变量名,可怕的字段语法,以及一个非常不成熟的天真实现,比正确的实现慢10倍。
cperl和Perl 6是真正的现代Perl实现,其中形式遵循功能,并且实现被简化和优化。

相关问题