2012-04-12 60 views
4

我想输出一些来自UTF-8数据库的文本到CP1252(又名Latin1)中的文件。要做到这一点,我使用Text :: Iconv,它工作正常除非要转换的字符串中的字符被分解。这是否是iconv图书馆的失败是我提出的一个问题,而且答案不明显。由于工作的iconv组成上精细的字符,该解决方案是第一个规范化我的琴弦,但我似乎无法管理做到这一点:在Perl中规范Unicode字符串

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 的本地编码。

在任何情况下,上下文是我的程序产生输出以文本形式的文件,可以有多种格式(包括CSV和包括UNIMARC),和几个编码(四个最频繁的幸福UTF-8,CP1252,MARC8和ISO-5426) 。用户的选择。

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

+1

为什么downvote?这个问题很明显,并有示例代码。很显然,OP在许多语言中都与Unicode一样苦苦挣扎 - 就像很多人一样。有关初学者,请参见[perlunitut](http://perldoc.perl.org/perlunitut.html)和[perlunifaq](http://perldoc.perl.org/perlunifaq.html)。 – Lumi 2012-04-13 08:12:09

+0

CP1252与拉丁-1不同。 Latin-1是ISO-8859-1。有关差异的详细信息,请参阅http://en.wikipedia.org/wiki/Windows-1252。 – 2012-04-13 10:43:35

+0

你说得对,ISO-8859-1和CP1252并不完全相同。至于Latin1是否指定ISO-8859-1或CP1252,我不太确定。我见过两个。这里是一个红色的鲱鱼 – 2012-04-17 09:56:56

回答

2

Ohoho,在我原来的消息下面我似乎已经错过了关于分解字符的基本位。只是试图为你的时髦信É如下:

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

工作正常5.10.1在Cygwin。


我可能会错过一些东西......但看起来您在这里采取了非常低级的文本处理方法。

首先,您是说您从数据库中获取数据,其中编码为UTF-8。没关系。所以如果驱动程序没有自动检测编码,请考虑告诉它。你不是在说你正在使用哪个数据库,但是你可能会发现一些东西是通过对DBI manual进行扫描,也可能是你用于“utf”或“encoding”的驱动程序(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的支持是实验性的,这是可怕的。

+0

我意识到我必须在这里学习很多关于Perl的知识。但是,迄今为止我的实验并不令人鼓舞。一个简单的例子:我需要输出到文件的编码之一是ISO 5426.然而,用文件模式“>>:encoding(ISO5426)”打开我的输出文件“失败,错误”无法找到编码“ISO5426”'。我不惊讶Perl不知道这种编码。但是现在,我应该怎样处理这种编码呢? – 2012-04-17 09:53:01

+0

同上代码:它不知道关于ISO 5426 – 2012-04-17 09:53:27

+0

我正在与Microsoft SQL Server通过Sybase驱动程序交流的数据库。我没有找到如何“告诉”编码问题。 – 2012-04-17 09:54:32

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时,您的脚本正在发生的事情。

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。)