我在 UTF-8 环境中编写了一个 Perl 程序(
LC_CTYPE="en_US.UTF-8"
,Emacs 显示“UUU”),它读取 UTF-8 编码的文件,插入从 OpenLDAP 服务器读取的一些属性值,然后将结果写入 UTF -8 编码文件。
看来我必须使用
use utf8;
use open IO => ':locale';
use open ':std' => ':locale';
use feature qw(unicode_strings);
除了
binmode $fh, ":utf8";
之外,再将输出写入 $fh
。
通过这些添加,Perl 中的字符串长度看起来是正确的,输出文件也是如此。
但是,正如我最近发现的那样,LDAP 中包含非 ASCII 字符(如德语变音符号)的属性值(某些
$ldap->search()
的结果)不正确。
因为它们在我看来就像“双重编码”,所以我像这样添加了解码(为条目创建哈希引用,由 DN 键入):
$data{$entry->dn} = {map {
$_ => [map { decode_utf8($_) } $entry->get_value($_)]
} $entry->attributes(NLK_NO_OPTIONS => 1)};
工作时,这看起来很奇怪,我想知道是否有更优雅的解决方案(毕竟我不太明白内部发生了什么)。
为了测试,我创建了一个这样的用户:
dn: uid=testuser,ou=people,dc=company,dc=org
cn: User Test
gidNumber: 54321
givenName: User
homeDirectory: /tmp/testuser
loginShell: /bin/bash
sn: Test
uid: testuser
uidNumber: 54321
objectClass: top
objectClass: posixAccount
objectClass: inetOrgPerson
displayName:: QsO2c2VyIFVtbGF1dCBpbSBOYW1lbg==
如果没有修复,
displayName
将显示为“Böser Umlaut im Namen”,修复后它将显示为“Böser Umlaut im Namen”。
您必须考虑所有数据边界。有时图书馆会为您做这件事,有时则不会。如果图书馆没有帮你做,你就必须自己做。
图书馆可能会对您隐藏这些详细信息,但在某些时候,某些东西必须了解有关输入的一些信息。您将获得八位字节。编码并没有什么神奇之处,它实际上只是关于解释八位组的指令。 当你交换数据时,你最终必须处理有线协议。
Perl 可以使用环境作为如何处理位的提示,但这并不意味着这些提示是正确的。您甚至可能有多个输入源,每个输入源都有不同的编码。
在始终为 UTF-8 的 JSON 世界中,所有这一切看起来都非常奇怪。这也让世界其他地方因为不一直使用 UTF-8 而显得非常愚蠢。但是,走到这一步也并不容易。
但是,我们使用的许多东西的历史都比 UTF-8 长得多;我认为 LDAP 和 UTF-8 大约是在同一时间开始的。后来出现的一些东西实现了 UTF-8 支持......奇怪的是(考虑如何让 MySQL 正确处理 UTF-8)。有些东西在 UTF-8 成为可能之前就实现了 Unicode 支持,而且它们无法立即改变。
我仍然遇到一些 UTF-16 的问题,这是一个完全不同的混乱,因为有不同的 UTF-16。回到我的科学职业生涯,我必须了解有关硬件的详细信息,以便知道如何处理磁带上的字节。我认为已经很长时间没有人关心大端或小端(甚至“网络”顺序)了。这些往往不是“大”或“小”;而是“大”或“小”。 Perl 的小端解包模板字母是 VAX 的“V”。