4

我想将一些来自 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 不同时。它没有说的是如何为 UTF-8本机编码的 Mac OS X 平台翻译它。

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

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

4

3 回答 3

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

于 2012-04-12T17:22:07.087 回答
2

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

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

在 Cygwin 上适用于 5.10.1。


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

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

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

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

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

这应该就是您需要做的所有事情。

您使用的任何特殊原因Text::Iconv?我认为你应该通过使用Encode模块来获得。但是对于您问题中概述的工作,您甚至不需要它。

您使用的是 Perl 5.12.3,因此 Unicode 处理应该适用于所有奇怪的边界情况。这些问题主要与几年前的 perls 有关。我认为 5.12 和 5.10 系列应该没问题。手头没有详细信息,但我曾经不得不使用旧的 5.6.1 进行 Unicode 工作,它的 Unicode 支持是实验性的,这太可怕了。

于 2012-04-12T17:04:50.587 回答
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 时遇到问题。)

于 2012-04-12T17:57:19.623 回答