为了在Perl中以utf-8处理文本,我一直在使用的每个流上使用binmode(<file-handle>, ":encoding(UTF-8)");
。我刚刚发现了
use open ( ":encoding(UTF-8)", ":std" );
可用于全局执行相同的操作。这很棒,因为它意味着减少了很多重复代码。
但是现在我有一个问题:我想在脚本-utf8
中有一个命令行选项,该选项仅在提供时才转换为utf-8。由于use open
是一个杂用语,因此它在词法范围内,我不能将其放在if语句中,但是如果没有if语句,它就不能依赖于命令行选项。
这是说明问题的最小示例,称之为问题。pl
#!/usr/bin/env perl
# hard-coded in my minimal example, normally set by command line option -utf8
my $use_utf8 = 1;
# use only applies within its lexical scope - this does not work
if ($use_utf8) {
use open ( ":encoding(UTF-8)", ":std" );
}
# if I put it at the right lexical scope, it's not conditional on $use_utf8
#..e open ( ":encoding(UTF-8)", ":std" );
while (<>) {
print length($_);
}
当我在文件上运行此代码时,调用input
,其中包含一行带有2字节UTF-8字符的行,例如à
,则输出3:
$ ./problem.pl input
3
如果将use open
语句移到全局范围,则得到的预期结果为2(一个字符加一个换行符)的长度:
$ ./problem.pl input
2
因此,如何在全局上将编码设置为utf-8,但有条件地在命令行选项上使用,这样我在使用-utf8
时将获得2,而在没有while (<>)
时将获得3。
此外,在我的实际用例中,我使用太空飞船运算符(binmode
)在命令行语法中提供了高度灵活性,以处理多个文件,但是在这种情况下,我无法调用{{1} },因为文件句柄由Perl自动管理。如果可以有条件的话,use open
将是一个更好的选择。
PS:是的,我确实确实还有我想继续处理的非utf8数据。谢天谢地,我们大多数数据现在都保存在utf-8中,但不幸的是还不是全部。
答案 0 :(得分:1)
首先:您可以使用if有条件地应用词汇用法。只需确保条件在编译时可用(您可能需要在之前使用BEGIN块)。
my $use_utf8;
BEGIN { $use_utf8 = 1; }
use if $use_utf8, 'open', ':std', ':encoding(UTF-8)';
-C选项的工作方式类似于utf8层的打开编译指示。 -CSD
会将其设置在标准手柄(S)和任何打开的手柄(D)上。不幸的是,它使用的是不太安全的:utf8
层而不是:encoding(UTF-8)
,因此如果将其用于实际上不是UTF-8的输入,则可能会导致字符串损坏。另外,-CD
为整个程序中打开的所有句柄设置默认值,而不仅仅是脚本的词法范围,这可能会中断不需要它的模块的使用。 (-CS
始终是全局的,开放式编译指示的':std'效果也是如此,因为标准句柄是全局的。)
perl -CSD problem.pl input