仅当代码在命令行中使用-n标志运行时才出现Unicode错误

时间:2016-01-18 21:46:08

标签: regex perl unicode command-line perl5

以下简单脚本(基本上)在其输入中徘徊,根据正则表达式对其进行拆分,替换结果列表中每个元素中的所有换行符,并逐个打印出已修改的元素:

# demo.pl
use strict;
use utf8;
use open qw(:std :utf8);
use warnings qw(FATAL utf8);

BEGIN { $/ = $\ = undef; }

while ( <> ) {
  s/\n\z//;
  s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/;
}

给定输入文件(INPUTFILE)并将以下(UTF8编码)内容作为其参数

A=42
ΦΡΩΒΩΖΖ=ABCDEFGHIJKLMNOPQRSTUVWXYZ
_B_C_D12=
foo
345bar=nope
baz
  =whatever=
X_Y_Z=quux

...它打印出所需的输出,即:

% perl demo.pl INPUTFILE
A=42
ΦΡΩΒΩΖΖ=ABCDEFGHIJKLMNOPQRSTUVWXYZ
_B_C_D12=\nfoo\n345bar=nope\nbaz\n  =whatever=
X_Y_Z=quux

相比之下,以下几乎相同的CLI单行

% perl -ne 'use strict; use utf8; use open qw(:std :utf8); use warnings qw(FATAL utf8); BEGIN { $/ = $\ = undef; } s/\n\z//; s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/;' INPUTFILE

...为相同的输入文件生成以下内容

A=42\nΦΡΩÎΩÎÎ=ABCDEFGHIJKLMNOPQRSTUVWXYZ
_B_C_D12=\nfoo\n345bar=nope\nbaz\n  =whatever=
X_Y_Z=quux

这里有(显然)两个问题:

  1. 正则表达式无法分隔第一个和第二个项目;
  2. 输出包含难以辨认的子字符串。
  3. (我希望这两个问题都有相同的根本原因。)

    “文件内脚本”(demo.pl)和CLI单行之间的唯一区别是前者用while ( <> ) { ... }显式包装脚本的主体,而对于后者, -n标志会自动插入此包装器。

    问:如何修改上面的单行,以便使用-n标记生成所需的结果

    BTW,毫不奇怪,完全命令行等效于demo.pl(没有-n标志),即

    % perl -e 'use strict; use utf8; use open qw(:std :utf8); use warnings qw(FATAL utf8); BEGIN { $/ = $\ = undef; } while ( <> ) { s/\n\z//; s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/; }' INPUTFILE
    

    也会产生所需的输出。

    所以问题,无论是什么,都与-n标志有关。

    FWIW:

    % perl -v | head -2
    
    This is perl 5, version 20, subversion 2 (v5.20.2) built for x86_64-linux-gnu-thread-multi
    

    编辑:还有一条线索:如果失败的一行内容的输入是通过STDIN传递而不是@ARGV中的文件名(例如将INPUTFILE替换为< INPUTFILE }),然后它产生所需的输出完全清晰,虽然仍然不正确,输出:

    A=42\nΦΡΩΒΩΖΖ=ABCDEFGHIJKLMNOPQRSTUVWXYZ
    _B_C_D12=\nfoo\n345bar=nope\nbaz\n  =whatever=
    X_Y_Z=quux
    

    我目前的猜测是,use open qw(:std :utf8)未涵盖<>@ARGV中作为文件名传递输入时读取的输入流。

1 个答案:

答案 0 :(得分:3)

  

“文件内脚本”(demo.pl)和CLI单行之间的唯一区别是前者用while(&lt;&gt;){...}显式包装脚本的主体,而对于后者,-n标志会自动插入此包装器。

是的,确切地说 - -n所有代码包裹在while (<>) { ... }中。这包括您的use utf8;use open(:utf8);行,因此在启用Unicode 时文件已经打开。

您可以通过运行-n版本的等效程序轻松验证这一点:

while (<>) {
    # demo.pl
    use strict;
    use utf8;
    use open qw(:std :utf8);
    use warnings qw(FATAL utf8);
    BEGIN { $/ = $\ = undef; }
    s/\n\z//;
    s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/;
}

并看到同样的效果。

更有趣的是,您可以看到use声明仍然有效:通过两次运行完全相同的输入文件

perl demo.pl INPUTFILE INPUTFILE

你得到两个输出,第一个输出,第二个输出正确。你的单行也会发生这种情况。

您可以使用-C flag with the i(8)选项默认启用UTF-8输入:

perl -CiO -ne 'use strict; use utf8; BEGIN { $/ = $\ = undef; } s/\n\z//; s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/;' INPUTFILE

确保在打开文件之前启用UTF-8,并获得正确的输出。 O也可以为标准输出启用UTF-8,以便您可以打印它。