Hub*_*ast 1 windows perl character-encoding
我用 perl 编写了一个程序,用于操作(创建、删除、打开、关闭、读取、写入、复制等)文件和目录。在 Linux (Ubuntu) 和 macOS 上运行时,它都能很好地做到这一点。但它也必须在 Windows 下做同样的工作,而且我对包含非 ASCII 字符的文件名编码有问题(例如德语变音,但也有任何其他非 ASCII 字符)。
由于我的原始程序太大,我创建了一个较短的程序进行测试。
这是我的 perl 程序的第一个原始版本的缩短版本(程序文件本身编码为 UTF-8):
#!/usr/bin/perl -w
use strict;
use warnings;
my $filename = 'FäöüßÄÖÜ?çàéâœ.txt';
my $text = 'TäöüßÄÖÜ?çàéâœ';
my $dirname = 'DäöüßÄÖÜ?çàéâœ';
# list all files in the parent directory before any action -------------
listDirectory('.');
# create file and write into file --------------------------------------
print "Going to open file $filename for writing ... ";
if (open(my $fileHandle, '>', $filename)) {
print "done successfully\n";
print "Going to write text '$text' into file $filename ... ";
if (print $fileHandle $text."\n") {
print "done successfully\n";
} else {
errorExit("failed to write into file", __LINE__);
}
close($fileHandle);
} else {
errorExit("failed to open file for writing", __LINE__);
}
# create a new directory -----------------------------------------------
print "Going to create directory $dirname ... ";
if (mkdir($dirname)) {
print "done successfully\n";
} else {
errorExit("failed to create directory", __LINE__);
}
# list all files in the parent directory again -------------------------
listDirectory('.');
# read file ------------------------------------------------------------
print "Going to open file $filename for reading ... ";
if (open(my $fileHandle, '<', $filename)) {
print "done successfully\n";
print "Going to list content of file $filename:\n";
print "--- begin of content ---\n";
while (my $row = <$fileHandle>) {
chomp $row;
print "$row\n";
}
print "--- end of content ---\n\n";
close($fileHandle);
} else {
errorExit("failed to open file for reading", __LINE__);
}
# list all files in the newly created directory ------------------------
listDirectory($dirname);
# end ------------------------------------------------------------------
print "normal end of execution\n";
exit(0);
# subroutines ==========================================================
# list all files in a directory ----------------------------------------
sub listDirectory {
my $dir = shift;
my $dirname = $dir eq '.' ? 'parent directory' : $dir;
print "Content of $dirname\n";
if (opendir (my $dirHandle, $dir)) {
print "--- begin of content of $dirname ---\n";
while (my $file = readdir($dirHandle)) {
print "$file\n";
}
print "--- end of content of $dirname ---\n\n";
closedir($dirHandle);
} else {
errorExit("failed to open $dirname", __LINE__);
}
}
# Error exit -----------------------------------------------------------
sub errorExit {
my $message = shift;
my $line = shift;
print "Error before line $line:\n";
print "program message: $message\n";
print "system message: $!\n";
print "premature end of execution\n";
exit(0);
}
Run Code Online (Sandbox Code Playgroud)
我的程序在 macOS 和 Linux (Ubuntu) 中的输出:
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
--- end of content of parent directory ---
Going to open file FäöüßÄÖÜ?çàéâœ.txt for writing ... done successfully
Going to write text 'TäöüßÄÖÜ?çàéâœ' into file FäöüßÄÖÜ?çàéâœ.txt ... done successfully
Going to create directory DäöüßÄÖÜ?çàé✠... done successfully
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
FäöüßÄÖÜ?çàéâœ.txt
DäöüßÄÖÜ?çàéâœ
--- end of content of parent directory ---
Going to open file FäöüßÄÖÜ?çàéâœ.txt for reading ... done successfully
Going to list content of file FäöüßÄÖÜ?çàéâœ.txt:
--- begin of content ---
TäöüßÄÖÜ?çàéâœ
--- end of content ---
Content of DäöüßÄÖÜ?çàéâœ
--- begin of content of DäöüßÄÖÜ?çàé✠---
.
..
--- end of content of DäöüßÄÖÜ?çàé✠---
normal end of execution
Run Code Online (Sandbox Code Playgroud)
这是预期的输出。
但是当我在 Windows 机器上执行这个程序时,我得到了这个:
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
--- end of content of parent directory ---
Going to open file F?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô.txt for writing ... done successfully
Going to write text 'T?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô' into file F?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô.txt ... done successfully
Going to create directory D?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô ... done successfully
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
F?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô.txt
D?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô
--- end of content of parent directory ---
Going to open file F?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô.txt for reading ... done successfully
Going to list content of file F?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô.txt:
--- begin of content ---
T?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô
--- end of content ---
Content of D?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô
--- begin of content of D?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô ---
.
..
--- end of content of D?ñ?Â???ƒ?ä?û?£ß?×?º?á?®?ó?ô ---
normal end of execution
Run Code Online (Sandbox Code Playgroud)
因此,所有文件名都以错误的编码写入。同样在资源管理器中,您会看到新文件和目录的错误编码文件名。但是虽然文本文件包含正确的内容,但我的程序显示错误。
所以,我摆弄我的程序,直到我得到一个版本,它产生正确的输出(与 macOS 和 Linux 下的第一个原始版本的输出相同))。
但是在文件系统中,文件名仍然是错误的:
13.01.2020 17:36 <DIR> .
10.01.2020 14:46 <DIR> ..
13.01.2020 18:23 2 970 testUmlaut.pl
13.01.2020 18:23 30 FäöüßÄÖÜẞçà éâœ.txt
13.01.2020 18:23 <DIR> DäöüßÄÖÜẞçà éâœ
Run Code Online (Sandbox Code Playgroud)
这是我的程序新版本的代码:
#!/usr/bin/perl -w
use strict;
use warnings;
use utf8;
use Encode;
if ($^O eq 'MSWin32') {
require Win32::Console;
Win32::Console::OutputCP(65001);
}
binmode STDOUT, ":utf8";
my $filename = 'FäöüßÄÖÜ?çàéâœ.txt';
my $text = 'TäöüßÄÖÜ?çàéâœ';
my $dirname = 'DäöüßÄÖÜ?çàéâœ';
# list all files in the parent directory before any action -------------
listDirectory('.');
# create file and write into file --------------------------------------
print "Going to open file $filename for writing ... ";
if (open(my $fileHandle, '>:encoding(UTF-8)', $filename)) {
print "done successfully\n";
print "Going to write text '$text' into file $filename ... ";
if (print $fileHandle $text."\n") {
print "done successfully\n";
} else {
errorExit("failed to write into file", __LINE__);
}
close($fileHandle);
} else {
errorExit("failed to open file for writing", __LINE__);
}
# create a new directory -----------------------------------------------
print "Going to create directory $dirname ... ";
if (mkdir($dirname)) {
print "done successfully\n";
} else {
errorExit("failed to create directory", __LINE__);
}
# list all files in the parent directory again -------------------------
listDirectory('.');
# read file ------------------------------------------------------------
print "Going to open file $filename for reading ... ";
if (open(my $fileHandle, '<:encoding(UTF-8)', $filename)) {
print "done successfully\n";
print "Going to list content of file $filename:\n";
print "--- begin of content ---\n";
while (my $row = <$fileHandle>) {
chomp $row;
print "$row\n";
}
print "--- end of content ---\n\n";
close($fileHandle);
} else {
errorExit("failed to open file for reading", __LINE__);
}
# list all files in the newly created directory ------------------------
listDirectory($dirname);
# end ------------------------------------------------------------------
print "normal end of execution\n";
exit(0);
# subroutines ==========================================================
# list all files in a directory ----------------------------------------
sub listDirectory {
my $dir = shift;
my $dirname = $dir eq '.' ? 'parent directory' : $dir;
print "Content of $dirname\n";
if (opendir (my $dirHandle, $dir)) {
print "--- begin of content of $dirname ---\n";
while (my $file = decode_utf8(readdir($dirHandle))) {
print "$file\n";
}
print "--- end of content of $dirname ---\n\n";
closedir($dirHandle);
} else {
errorExit("failed to open $dirname", __LINE__);
}
}
# Error exit -----------------------------------------------------------
sub errorExit {
my $message = shift;
my $line = shift;
print "Error before line $line:\n";
print "program message: $message\n";
print "system message: $!\n";
print "premature end of execution\n";
exit(0);
}
Run Code Online (Sandbox Code Playgroud)
这个新版本在 Linux 或 macOS 中运行时仍然表现良好。但是 Windows 中的文件名仍然存在这个问题。
我怎样才能解决这个问题?
接受/返回字符串的 Windows 系统调用有两种。“A” (ANSI) 版本处理使用系统活动代码页编码的文本,“W”(宽)版本处理使用 UTF-16le 编码的文本。
Perl 专门使用“A”版本,因此期望使用活动代码页(例如,大多数美国机器的cp1252)对文件名进行编码。
一种解决方案是使用正确的代码页对文件名进行编码。
use utf8; # Source code encoded using UTF-8.
my ($cie, $coe, $ae);
BEGIN {
require Win32;
$cie = "cp" . Win32::GetConsoleCP();
$coe = "cp" . Win32::GetConsoleOutputCP();
$ae = "cp" . Win32::GetACP();
binmode(STDIN, ":encoding($cie)");
binmode(STDOUT, ":encoding($coe)");
binmode(STDERR, ":encoding($coe)");
require "open.pm";
"open"->import(":encoding($ae)"); # Default encoding for open()
}
use Encode qw( encode );
#my $qfn = 'FäöüßÄÖÜ?çàéâœ.txt';
my $qfn = 'FäöüßÄÖÜßçàéâœ.txt';
open(my $fh, '>', encode($ae, $qfn))
or die("Can't create \"$qfn\": $!\n");
print($fh "This is \"$qfn\".\n");
Run Code Online (Sandbox Code Playgroud)
请注意,我替换了“?” 用“ß”是因为“?” 不存在于我的活动代码页 (cp1252) 的字符集中,因此不能用作文件名的一部分。为了避免这个问题,需要使用 Wide 接口。这可以使用Win32::Unicode::File和Win32::Unicode::Dir或Win32::LongPath 来实现。
use utf8; # Source code encoded using UTF-8.
my ($cie, $coe, $ae);
BEGIN {
require Win32;
$cie = "cp" . Win32::GetConsoleCP();
$coe = "cp" . Win32::GetConsoleOutputCP();
$ae = "cp" . Win32::GetACP();
binmode(STDIN, ":encoding($cie)");
binmode(STDOUT, ":encoding($coe)");
binmode(STDERR, ":encoding($coe)");
require "open.pm";
"open"->import(":encoding($ae)"); # Default encoding for open()
}
use Win32::Unicode::File qw( );
my $qfn = 'FäöüßÄÖÜ?çàéâœ.txt';
my $fh = Win32::Unicode::File->new('>', $qfn)
or die("Can't create \"$qfn\": $!\n");
binmode($fh, ":encoding($ae)"); # Didn't happen automatically since we didn't use open()
print($fh "This is \"$qfn\".\n");
Run Code Online (Sandbox Code Playgroud)
在此处阅读更多相关信息。
| 归档时间: |
|
| 查看次数: |
317 次 |
| 最近记录: |