在Perl中以utf8模式打开的ASCII文本文件中过滤微软1252个字符

时间:2011-10-21 10:26:07

标签: perl utf-8 windows-1252

我有一个合理大小的文本文件大小平面文件数据库,大部分以8859格式保存,通过网络表单(使用Perl脚本)收集。直到最近,我正在使用一组简单的正则表达式来协商常见的1252个字符(曲线引号,撇号等):

$line=~s/\x91/\&\#8216\;/g; # smart apostrophe left
$line=~s/\x92/\&\#8217\;/g; # smart apostrophe right

......等等。

然而,由于我决定我应该转换Unicode,并将我的所有脚本转换为读入并输出utf8(适用于所有新材料),这些(现有的)1252个字符的正则表达式不再有效我的Perl html输出字面上输出4个字符:'\ x92'和'\ x93'等等(至少在utf8模式下它出现在浏览器上,下载(ftp不是http)和在文本编辑器(textpad)中打开它是不同的,一个未定义的字符仍然存在,并在Firefox默认情况下打开输出文件(没有内容类型标题)8859模式呈现正确的字符。)

脚本开头的新utf8编译指示是:<​​/ p>

使用CGI qw(-utf8); 使用open IO =&gt; ':UTF8';

我理解这是由于utf8模式使字符双字节而不是单字节并适用于0x80到0xff范围内的那些字符,已经阅读了与此有关的wikibooks上的文章,但我不是更明智的如何过滤它们。理想情况下,我知道我应该在utf8模式下重新保存所有文件(因为平面文件数据库现在包含8859和utf8的混合),但是如果我打算这样做,我将首先需要某种过滤器

我内部的2字节存储可能是错的,因为它似乎暗示Perl根据各种情况处理的内容非常不同。

如果有人能为我提供正则表达式解决方案,我将非常感激。或者其他一些方法。经过多次尝试和黑客入侵,我已经将头发撕裂数周。通常需要替换大约6 1252个字符,并且使用过滤方法我可以在utf8中重新保存整个flippin并且忘记曾经有过1252 ...

4 个答案:

答案 0 :(得分:2)

Encoding::FixLatin专门用于帮助修复与您的数据相同的数据。

答案 1 :(得分:1)

Ikegami already mentioned Encoding::FixLatin模块。

另一种方法,如果您知道每个字符串 UTF-8或CP1252,但不是两者的混合,则将其作为二进制字符串读取并执行:< / p>

unless ( utf8::decode($string) ) {
    require Encode;
    $string = Encode::decode(cp1252 => $string);
}

与Encoding :: FixLatin相比,这有两个小优点:将CP1252文本误解为UTF-8的可能性略低(因为整个字符串必须是有效的UTF-8)以及用其他一些后备替换CP1252的可能性编码。相应的缺点是,由于某些其他原因,这些代码可能会回退到不完全有效的UTF-8字符串上的CP1252,例如因为它们在多字节字符的中间被截断。

答案 2 :(得分:1)

您还可以使用Encode.pmfallback的支持。

use Encode qw[decode];

my $octets = "\x91 Foo \xE2\x98\xBA \x92";
my $string = decode('UTF-8', $octets, sub {
    my ($ordinal) = @_;
    return decode('Windows-1252', pack 'C', $ordinal);
});

printf "<%s>\n", 
  join ' ', map { sprintf 'U+%.4X', ord $_ } split //, $string;

输出:

<U+2018 U+0020 U+0046 U+006F U+006F U+0020 U+263A U+0020 U+2019>

答案 3 :(得分:0)

您是否重新编码了数据文件?如果没有,打开它们为UTF-8将无法正常工作。你可以简单地打开它们

open $filehandle, '<:encoding(cp1252)', $filename or die ...;

并且所有(tm)都应该有用。

如果你进行了重新编码,似乎出现了问题,你需要分析它是什么,并修复它。我建议使用hexdump来查找文件中的实际内容。文本控制台和编辑器有时会骗你,hexdump永远不会说谎。