在Perl中规范化Unicode字符串

时间:2012-04-12 16:42:40

标签: perl unicode

我想将一些来自UTF-8数据库的文本输出到CP1252(又名Latin1)中的文件。为此,我使用Text :: Iconv工作正常,除非要转换的字符串中的字符被分解。这是否是iconv库的失败是我提出的一个问题,答案并不明显。由于iconv在组合字符上工作正常,因此解决方案是首先规范化我的字符串,但我似乎无法做到这一点:

use strict;
use warnings;
use Data::Hexdumper qw(hexdump);
use Unicode::Normalize;

my $v =  "É"; # E=U+0045 followed by combining ´=U+0301. UTF-8: 0x45CC81
print "'$v'\n";
print hexdump($v);

my $n = NFC $v;  # should be É=U+00C9. UTF-8: 0xC389
print "'$n'\n";
print hexdump($n);

但这是我得到的输出:

'É'
  0x0000 : 45 CC 81 00 00 00 00 00 00 00 00 00 00 00 00 00 : E...............
'É'
  0x0000 : 45 CC 81 00 00 00 00 00 00 00 00 00 00 00 00 00 : E...............

换句话说,NFC(转换为标准化表格C)功能没有做任何事情。我错过了什么吗?我在Mac OS X 10.7.3上使用Perl 5.12.3。

这只是我在Perl中处理文本问题的开始,我不会想到这一点。谢谢你的帮助。

编辑:某些上下文似乎很有用。当然,use utf8条款可以帮助我做出很多有用的例子。我的实际问题当然不是字符串文字。

首先,我从答案中了解到,我需要了解很多关于Perl的知识。事实上,我不是Perl程序员,而是Objective-C / Cocoa程序员,而这些问题根本没有出现。

所以我开始读出来,我发现Perl文档很混乱,例如当它谈到本机编码与UTF-8不同时。它没有说的是如何在Mac OS X平台上翻译它,其中UTF-8 本机编码。

在任何情况下,上下文是我的程序在texte文件中生成输出,可以有多种格式(包括csv和Unimarc),以及几种编码(最常见的四种是UTF-8,CP1252,MARC8和ISO-5426)。用户的选择。

它从一些数据库(目前是mySQL或SQL Server)获取输入,其中数据通常以UTF-8编码(但有时在CP1252中)。

3 个答案:

答案 0 :(得分:3)

你缺少的是$v被设置为'E'字符和组合急性重音变音符号的 utf-8编码,而不是组合急性变音符号本身。要解决这个问题,你需要做类似

的事情

1)use utf8 - 使Perl自动utf-8解码您的源代码

2)明确解码$v

my $v = chr(0x45) . chr(0xCC) . chr(0x81);
use Encode;
$v = Encode::decode('utf-8', $v);    # now $v is 0x45 0x301

3)使用chr$v明确设置为您的意思

my $v = chr(0x45) . chr(0x301);

我不会真的推荐数字(2),但是当你没有use utf8时,我将其作为一种方式来说明你的脚本会发生什么。

答案 1 :(得分:2)

Ohoho,在我下面的原始信息中,我似乎错过了关于分解字符的基本要点。只是为您的时髦字母É尝试了以下内容:

perl -C3 -lwe '$_ = qq(\x45\x{0301}); print'

在Cygwin上适用于5.10.1。


我可能会遗漏一些东西......但是看起来你在这里采用的是一种非常低级的文本处理方法。

首先,您说您从数据库中获取数据,其中编码为UTF-8。没关系。因此,如果驱动程序没有自动检测编码,请考虑告诉它。你不是说你正在使用哪个数据库,但是你可能会通过点击DBI manual以及你用于“utf”或“编码”的驱动程序(DBD::*)找到一些东西。 ”

然后,给定数据库连接的适当编码设置,您的文本应该以Perl形式到达,例如,文本。只是文字,没有编码。例如,在Java中。是的,字符串有一些内部编码,但你不应该打扰它是什么。

然后,在写入文件时,只需使用以下代码:

open my $fh,  '>:encoding(CP1252)', $filename or die "open $filename: $!";
print $fh $text_from_db;
close $fh;

这应该就是你需要做的一切。

您使用Text::Iconv的任何特殊原因?我认为你应该使用Encode module。但是对于你问题中概述的工作,你甚至不需要那个。

你正在使用Perl 5.12.3,因此Unicode处理应该适用于所有奇怪的边界情况。这些问题主要是可以追溯到几年前的perls。我认为5.12和5.10系列应该没问题。没有方便的细节,但我曾经不得不使用旧的5.6.1进行Unicode工作,其中Unicode支持是实验性的,而且非常糟糕。

答案 2 :(得分:2)

你没告诉Perl你的文件是UTF-8。

您没有告诉Perl如何对输出进行编码。

use strict;
use warnings;

use utf8;                             # UTF-8 source.
use open ':std', ':encoding(UTF-8)';  # UTF-8 output. Don't forget to chcp 65001..

use Data::Dumper       qw( Dumper );
use Unicode::Normalize qw( NFC );

local $Data::Dumper::Useqq = 1;    
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;

my $v =  "\x{0045}\x{0301}";
print "'$v'\n";
print Dumper($v), "\n";

my $n = NFC $v;
print "'$n'\n";
print Dumper($n), "\n";

(我在加载Hexdumper时遇到问题。)