在perl脚本中/随处使用utf8

时间:2018-09-11 14:28:15

标签: windows perl utf-8 decode encode

我正在德语Windows 7下运行最新的perl,我想在我的perl程序中的任何地方使用utf8(用于脚本,文件内容,文件名,邮件文本等)。

一切正常,但是在尝试处理文件名中包含特殊字符的文件时遇到了问题。甚至system调用也不起作用。因此,(如何)我可以告诉perl在任何地方使用utf8

我用encodedecode尝试了一段时间,但目前尚不清楚为什么它能正常工作...另外,我还需要encode('cp850', TEXT)才能在命令提示符窗口中正确显示

示例:

当我需要复制文件时,仅当我使用File::copy(encode("iso-8859-1", $filename), ...)且要使用pdf文件时,它才有效,命令是system(encode('cp850', sprintf('pdftk.exe %s...', decode('utf8', $file))));

为什么会这样(尤其是系统调用中的解码),还有更简单的方法吗?也许与use open ':encoding...'有所关联,但到目前为止我还没有运气。

3 个答案:

答案 0 :(得分:3)

这是最近刚遇到这个确切问题的人的真实,具体和明确的答案:

在Windows上,不能让Perl 5.28.0或更低版本对所有内容使用UTF8。

这是为什么:从Perl 5.28.0开始,为此,perl核心文件处理功能被致命地搞砸了。 Windows将文件名存储为(简单地说)UTF16,并且Windows api宽字符函数将文件名返回为宽字符,类似于Perl内部已使用的字符。但是,当从文件系统中获取这些文件时,perl核心将它们转换为本地系统编码中的字节。反之亦然。因此,从道德上讲,您具有这样的流程,解释为Perl:

use utf8;

sub readdir_perl {
    my $dir = shift;
    my $fn = readdir $dir;
    $fn = encode $fn, CP_ACP;
    return $fn;
}

sub open_perl {
    my $fn = shift;
    $fn = decode $fn, CP_ACP;
    open my $FH, $fn;
    return $FH;
}

两个重要说明:

  • 以上所有内容均已释义。这大概是perl内核在C中实现这些功能的方式,您无法在程序执行期间有效地更改它们,也不能有效地更改CP_ACP。
  • 从宽字符到CP_ACP的转换被强制执行。它不会为错误保释。如果存在无法有效表示的宽字符,则会将其转换为?字符,从而给您带来少量的垃圾。

也就是说,你能做什么?

  1. 使用Win32::LongPath。它可以处理您内部的大部分需求。对于文件。请注意,它只能在配置了短路径的卷上可靠地运行,该卷通常为C :,而没有其他功能。正常使用system,但要确保将所有内容都视为字节并适当地解码/编码。存在一些example code。您还需要手动实现ALL文件处理,并且无法有用地修补其他代码来使用LongPath函数。
  2. 等待直到perl核心被修复。据我所知,目前尚无任何计划可以立即进行,因为任何一种简单的修复都可能破坏依赖于UTF16到系统代码页转换的旧脚本,从而有效地将unium umlauts改成äöü在德国系统上,等等。 。
  3. 使用其他语言。也许是PowerShell。

答案 1 :(得分:1)

首先将命令提示符的代码页设置为65001

chcp 65001

这将允许您在命令提示符下使用和显示utf8字符。 文件名取决于所使用的文件系统。 NTFS使用UTF-16LE编码存储文件名。有关如何在Windows上使用Unicode文件名创建和访问文件的问题,请参见this问题。

System()命令需要在与命令提示符相同的代码页中进行编码,因此在执行chcp 65001之后,您可以在utf8中对system()命令进行编码

答案 2 :(得分:0)

由于目前没有合适的答案,我将尝试在此处写下一个工作示例。希望有一次它不会再有错误了。在此之前,请发布您的建议/解决方案,一旦成功,我将对其进行测试和更新。

当前未解决的问题:

  • 通过open打开pdf文件
  • 通过CAM::PDF->new打开pdf文件
  • 通过system调用处理pdf文件

test.pl:

$| = 1;
use strict;
use warnings;
use utf8;
use CAM::PDF;
use open ':std', ':encoding(UTF-8)';
BEGIN {
  if ($^O eq "MSWin32") {
    require Win32::Unicode::File;
    Win32::Unicode::File->import();
  }
}

my $file = 'Täst.pdf';
print "FILENAME: $file\n";

unlink("file2.pdf");
copyW($file, "file2.pdf") or print "cannot copy file: $!\n";

if (!open(FH, $file)) {
  print "cannot open file by open '$file': $!\n";
}
else {close FH}

my $pdf = CAM::PDF->new($file) or print "cannot open file by CAM::PDF: $!\n";
print "\n";

system("pdftk.exe $file cat 2 4 output out.pdf") or print "cannot run command: $!\n";
print "\n";

test.cmd:

要求为命令行窗口设置字体“ Lucida Console”。

@echo off
chcp 65001 >nul
call perl.exe test.pl
chcp 850 >nul
pause

Windows下的输出:

FILENAME: Täst.pdf

cannot open file by open 'Täst.pdf': No such file or directory

cannot open file by CAM::PDF: No such file or directory

Error: Unable to find file.
Error: Failed to open PDF file:
   Täst.pdf
Drücken Sie eine beliebige Taste . . .