在Perl中规范化Unicode字符串

在Perl中规范化Unicode字符串,perl,unicode,Perl,Unicode,我想将一些来自UTF-8中数据库的文本输出到CP1252(又名Latin1)中的文件中。为此,我使用Text::Iconv,除非要转换的字符串中的字符被分解,否则它工作得很好。这是否是iconv库的失败是我提出的一个问题,答案并不明显。由于iconv可以很好地处理组合字符,因此解决方案是首先规范化字符串,但我似乎无法做到这一点: use strict; use warnings; use Data::Hexdumper qw(hexdump); use Unicode::Normalize;

我想将一些来自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)函数没有做任何事情。我错过什么了吗?我在MacOSX10.7.3上使用Perl5.12.3

这只是我在Perl中处理文本问题的开始,这是我没有预料到的。谢谢你的帮助

编辑:一些上下文似乎很有用。当然,一个
useutf8
子句可以大大帮助我设计的示例。我的实际问题当然不是字符串文本

首先,我从答案中意识到,关于Perl,我需要学习很多东西。事实上,我不是一个Perl程序员,而是一个Objective-C/Cocoa程序员,这些问题根本就不会出现

所以我开始阅读,我发现Perl文档非常混乱,例如,当它谈到本机编码不同于UTF-8时。它没有说明的是如何在MacOSX平台上转换,其中UTF-8是本机编码

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


它从某个数据库(当前为mySQL或SQL Server)获取输入,其中数据通常以UTF-8编码(但有时以CP1252编码)。

Ohoho,在我下面的原始消息中,我似乎遗漏了有关分解字符的基本信息。为了你那封时髦的信,我试了一下:

perl -C3 -lwe '$_ = qq(\x45\x{0301}); print'
在Cygwin上的5.10.1中工作正常


我可能错过了什么。。。但是你在这里似乎采取了一种非常低级的文本处理方法

首先,您的意思是从数据库获取数据,其中编码为UTF-8。那很好。因此,如果驱动程序没有自动检测编码,请考虑告诉它。您并没有说您正在使用哪个数据库,但您可能会通过将数据库和用于“utf”或“编码”的驱动程序(
DBD::*
)进行灰显找到一些信息

然后,为数据库连接提供适当的编码设置,您的文本应该以Perl的形式到达,呃,文本。只有文本,没有编码。比如Java。是的,字符串中有一些内部编码,但您不必担心它是什么

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

open my $fh,  '>:encoding(CP1252)', $filename or die "open $filename: $!";
print $fh $text_from_db;
close $fh;
这就是你所需要做的

您使用
Text::Iconv
的任何特定原因?我想你应该先用这个。但对于你问题中概述的工作,你甚至不需要它


您使用的是Perl 5.12.3,因此Unicode处理应该可以很好地处理所有奇怪的边界情况。这些问题大多与几年前的珍珠有关。我认为5.12和5.10系列应该可以。没有现成的细节,但我曾经不得不使用旧的5.6.1进行Unicode工作,它的Unicode支持是实验性的,非常可怕。

您缺少的是,
$v
被设置为“E”字符,并使用组合的锐音变音符号的utf-8编码,而不是急性变音本身。要解决这个问题,您需要执行以下操作

1)
使用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)项,但我将其作为一种方式来说明当您没有告诉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时遇到问题。)

为什么投票失败?该问题措辞清晰,并有示例代码。很明显,OP正在努力使用Unicode——就像许多人在许多语言中所做的那样。首先请参见和。CP1252与拉丁语-1不同。拉丁语-1是ISO-8859-1。有关差异的详细信息,请参阅。ISO-8859-1和CP1252不完全相同,这一点您是对的。至于Latin1是指ISO-8859-1还是CP1252,我不太确定。我两个都见过。虽然我意识到我必须在这里学习很多关于Perl的知识,但这是一种危险的做法。然而,到目前为止,我的实验并不令人鼓舞。一个简单的例子:我需要输出到文件的编码之一是ISO5426。但是,使用文件模式
“>:encoding(ISO5426)”
打开我的输出文件失败,出现错误
找不到编码“ISO5426”
。Perl不知道这种编码,对此我并不感到惊讶。但是现在,我该怎么处理这种编码呢?我正在通过Sybase驱动程序与Microsoft SQL Server进行对话的数据库。我不知道如何“告诉”编码。@Jean DenisMuys-你试过了吗?您在哪个系统上使用Sybase客户端库?如果没有Sybase而只有MSSQL,那么编译客户机库需要什么要查看受支持的编码,请执行:
perl-MEncode-lwe“为编码打印->编码(':all');”
我在Win7系统上的perl 5.12上也没有ISO5426。我设置了
charset=utf8
:没有更改。我正在使用Mac OS X 10.7.3(出于所有目的和目的,都使用BSD Unix)。我不理解您的编译问题:Perl不编译,我使用CPAN安装了DBD::Sybase。我不知道它在幕后做了什么。我找不到MSSQL DBD驱动程序。谢谢你的一个l