【问题标题】:Filtering microsoft 1252 characters out of an ASCII text file opened in utf8 mode in Perl从 Perl 中以 utf8 模式打开的 ASCII 文本文件中过滤 microsoft 1252 个字符
【发布时间】:2011-10-21 10:26:07
【问题描述】:

我有一个大小合理的文本文档平面文件数据库,大部分以 8859 格式保存,这些文本文档是通过 Web 表单(使用 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 pragma 是:

使用 CGI qw(-utf8); 使用 open IO => ':utf8';

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

对于内部的 2 字节存储,我可能是错误的,因为这似乎暗示 Perl 根据不同的情况处理事情的方式非常不同。

如果有人可以为我提供正则表达式解决方案,我将不胜感激。或者其他一些方法。数周以来,我一直在努力解决这个问题,各种尝试和黑客攻击都失败了。通常需要替换大约 6 1252 个字符,并且使用过滤器方法,我可以在 utf8 中重新保存整个翻转批次,而忘记曾经有一个 1252...

【问题讨论】:

  • 哦...我不能只重置以在 8859 中打开文件并进行过滤,因为数据库现在包含 utf8 和 8859。哎呀。

标签: perl utf-8 windows-1252


【解决方案1】:

Encoding::FixLatin 专门用于帮助修复以与您相同的方式损坏的数据。

【讨论】:

    【解决方案2】:

    Ikegami already mentionedEncoding::FixLatin 模块。

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

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

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

    【讨论】:

    • 太好了我认为这可能是我需要的解决方案 - 我从来没有想过逐行解码而不是将整个文件作为一个或其他。所以这将留下有效的 utf8 字符串,并允许我像以前一样使用正则表达式处理包含非 utf 字符的字符串?
    • ...我不知道 hte FixLatin 模块似乎完全符合我的要求,再次感谢
    • 这两个解决方案都会(除了字符集错误识别的小概率)将所有输入字符串转换为 Perl Unicode 字符串(可能在内部表示为 UTF-8,但你真的不应该关心关于那个),无论它们是用 UTF-8 还是 CP1252 编码的。因此,您不需要在此之上进行任何额外的“正则表达式处理”。 (不过,即使你这样做了,它也可能不会造成任何伤害,因为这些正则表达式永远不会匹配有效的可打印 Unicode 字符串。)
    • 终于开始尝试自定义的逐行解码(据我所知,FixLatin 不起作用),现在已经奏效了,非常感谢,我终于可以了在整个愚蠢的业务下划出一条线,沐浴在 utf-8 的喜悦中(我仍然偶尔会在极少数地方弹出一些奇怪的字符,看起来与 cp1252 无关,但它是可以容忍的)
    • 要查看您提到的两种方法的局限性,请转至here
    【解决方案3】:

    您也可以使用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>
    

    【讨论】:

      【解决方案4】:

      您是否对数据文件进行了重新编码?如果没有,以 UTF-8 格式打开它们将不起作用。你可以简单地打开它们

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

      一切(tm)都应该工作。

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

      【讨论】:

      • Hexdump 在 1252 个字符符合预期的地方显示 91,92,93。为什么正则表达式匹配 /\x91/ /\x92/ 等在这种情况下失败让我感到困惑。注意到我的文本编辑器将新创建的文件报告为 ANSI 的 1252 个字符,而那些没有 utf8 的文件 - 我希望 Perl utf8 IO 层将我的所有文件强制为 utf8。我无法重新编码,直到找到一种方法从旧的 8859 文件中过滤掉 1252 个字符,因为它们都与新的 utf8 文件混合在一起
      猜你喜欢
      • 2012-09-04
      • 2020-06-23
      • 1970-01-01
      • 2012-05-09
      • 2012-11-15
      • 2014-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多