【问题标题】:Trouble in Perl using split() function on tab chars when string contains non-Latin characters当字符串包含非拉丁字符时,Perl 在制表符上使用 split() 函数时出现问题
【发布时间】:2013-11-07 17:47:11
【问题描述】:

我正在修改一个 Perl 脚本,该脚本读取一系列 UCS-2LE 编码文件,其中包含制表符分隔格式的字符串,但是当字符串包含外部字符时,我无法在制表符上拆分字符串扩展拉丁字符集。

这是我从这些文件中读取的示例行(制表符分隔):

adını   transcript  asr turkish

当我让我的脚本将这些行写入输出文件以尝试调试此问题时,它正在编写以下内容:

ad1Ů1ĉtranscript    asr turkish

它似乎无法识别土耳其字符后的制表符。这只发生在单词以非拉丁字符结尾(因此与制表符相邻)时。

这是代码块的一部分,其中发生写入输出文件并发生字符串拆分:

for my $infile (@ARGV){  
    if (!open (INFILE, "<$infile")){
        die "Couldn't open $infile.\n";
    }    

binmode (OUTFILE, ":utf8");

while (<INFILE>) {
    chomp;
    $tTot++;

    if ($lineNo == 1) {                
        $_ = decode('UCS-2LE', $_);      
    }
    else {
        $_ = decode('UCS-2', $_);
    }    

    $_ =~ s/[\r\n]+//g;    
    my @foo = split('\t');

    my $orth = $foo[0];
    my $tscrpt = $foo[1];
    my $langCode = $foo[3];

    if (exists $codeHash{$langCode}) {
      unless ($tscrpt eq '') {
        check($orth, $tscrpt, $langCode);
      }
    }
    else {
        print OUTFILE "Unknown language code $langCode at line $lineNo.\n";
        print OUTFILE $_; # printing the string that's not being split correctly
        print OUTFILE "\n";
        $tBad++;
    }
  }

此脚本的目的是检查输入文件中每一行的语言代码是否有效,并根据该代码,根据我们的转录系统检查每个单词的转录是否“合法” .

这是我迄今为止尝试过的:

  1. 在读入输入字符串时更改其编码 UTF-8、UTF-16 或 UTF-16LE
  2. 将 split() 字符更改为 '\w', /[[:blank:]]/、\p{Blank}、\x{09} 和 \N{U+0009}。
  3. 阅读 Perl Unicode 和 perlrebackslash 文档和任何其他远程 我可以在各个网站上找到的相关帖子

有人对我可能尝试的其他事情有任何建议吗?提前致谢!

我还应该提到,我无法控制输入文件编码和输出文件编码;我必须阅读 UCS-2LE 并输出 UTF-8。

【问题讨论】:

  • 您应该能够在不到 5 行的时间内演示 split 的问题。并且不要省略在更新中处理输出编码的行。另外,请提供您即将进行的演示失败的输入。 od -t x1 file 将以不会损坏的格式提供它。
  • 顺便说一句,$_ = decode('UCS-2LE', $_); s/^\x{FEFF}//; 将是解码文件的更简单方法。更好的是在open 中使用'&lt;:raw:encoding(UCS-2le):crlf' 而不是'&lt;'
  • 您的split@foo 似乎与您的输出几乎无关,除了触发一些错误消息。 s 语句下方的任何内容似乎都不会影响您打印(未编码的)$_
  • 顺便说一句,UCS-2le 是 UTF-16le 的子集。最好使用 UTF-16le 进行解码,以防您实际上是 UTF-16le。
  • @ikegami 感谢您的提示。我将尝试使用 UFT-16LE 打开文件。另外,因为我是新手,你能告诉我帖子的哪些部分没有用,以便下次我可以简洁而不遗漏重要部分吗?谢谢!

标签: regex perl tabs ucs


【解决方案1】:

感谢大家的cmet和一些进一步的研究,我想出了如何解决这个问题,它与我想象的略有不同;结果是 split() 问题和编码问题的结合。我必须在显式打开语句中添加编码,而不是在 for 循环中使用隐式打开,并跳过文件开头的前两个字节。

这是我在问题中发布的部分的更正后的工作代码:

for my $infile (@ARGV){
    my $outfile = $infile . '.out';

    # SOLUTION part 1: added explicit open statement
    open (INFILE, "<:raw:encoding(UCS-2le):crlf", $infile) or die "Error opening $infile: $!";

    # SOLUTION part 2: had to skip the first two bytes of the file 
    seek INFILE, 2, 0;

    if (!open (OUTFILE, ">$outfile")) {
        die "Couldn't write to $outfile.\n";
    }

    binmode (OUTFILE, ":utf8");
    print OUTFILE "Line#\tOriginal_Entry\tLangCode\tOffending_Char(s)\n";

    $tBad = 0;
    $tTot = 0;
    $lineNo = 1;

while (<INFILE>) {
    chomp;
    $tTot++;

    # SOLUTION part 3: deleted the "if" block I had here before that was handling encoding

    # Rest of code in the original block is the same    
}

我的代码现在可以正确识别与不属于扩展拉丁语集的字符相邻的制表符字符,并按应有的方式在制表符上进行拆分。

注意:另一种解决方案是将外来词括在双引号中,但在我们的例子中,我们不能保证我们的输入文件会以这种方式格式化。

感谢所有评论并帮助我的人!

【讨论】:

    【解决方案2】:

    您应该首先使用正确的编码打开文件(我不知道这是否正确,但我相信您的话)。那么就不需要调用decode()了:

    open(my $fh, "<:encoding(UCS-2LE)", $file) or die "Error opening $file: $!";
    while (<$fh>) {
      ...
    }
    

    【讨论】:

      猜你喜欢
      • 2012-05-16
      • 2016-05-19
      • 2018-03-09
      • 1970-01-01
      • 2010-12-27
      • 2014-10-30
      • 2011-04-04
      • 2019-09-22
      • 1970-01-01
      相关资源
      最近更新 更多