在Windows上使用Perl处理Unicode目录和文件名

9

我在使用Perl和Windows时遇到了编码问题。在运行Perl(草莓5.16)的Windows 7上,我需要打开文件和/或访问名称/路径中含有非英文字母的目录。对于打开文件,我已经找到了一个解决方案,似乎可以正常工作:

#!/usr/bin/perl -w

use strict;
use warnings;
use Win32::Unicode::File;
use Encode;
use Tk;

my $mw = Tk::MainWindow->new;
my $tissue_but = $mw->Button(
    -text => 'Open file',
    -command =>  [ \&select_unicode_file ],
);
$tissue_but->grid( -row => 3, -column => 1 );
Tk::MainLoop();

sub select_unicode_file{
my $types = [ ['Txt', '.txt'],
          ['All Files',   '*'],];
my $input_file= $mw->getOpenFile(-filetypes => $types);
my $fh = Win32::Unicode::File->new;
if ($fh->open('<', $input_file)){
  while (my $line = $fh->readline()){
    print "\n$line\n";
  }
   close $fh;
}
 else{
  print "Couldn't open file: $!\n";
}
}

这段代码可以正确打开诸如“Поиск/Поиск.txt”这样的文件。

但我无法简单地获取目录路径并进行处理。我认为应该使用Win32::Unicode::Dir,但我真的无法理解文档。

大概应该是这样:

#!/usr/bin/perl -w

use strict;
use warnings;
use Win32::Unicode::Dir;
use Encode;
use Tk;

my $mw = Tk::MainWindow->new;
my $tissue_but = $mw->Button(
    -text => 'Open file',
    -command =>  [ \&select_unicode_directory ],
);
$tissue_but->grid( -row => 3, -column => 1 );
Tk::MainLoop();

sub select_unicode_directory{
my $dir = $mw->chooseDirectory( );
my $wdir = Win32::Unicode::Dir->new;
my $dir = $wdir->open($dir) || die $wdir->error;
my $dir_complete = "$dir/a.txt";
open (MYFILE, $dir_complete );
    while (<MYFILE>) {
    chomp;
    print "$_\n";
}
close (MYFILE); 
}

open 接受字节。使用 Win32API::File 的 CreateFileW 打开文件。如果我没记错的话,你需要先通过 encode('UTF-16le', "$qfn\0") 进行转换。 - ikegami
抱歉,我无法理解。如何使用Win32::Unicode::Dir->new在上述代码中获取路径? - Kelly o'Brian
啊,你有两个问题。抱歉,现在无法查看。 - ikegami
有机会得到答案吗? - Kelly o'Brian
哎呀,关于Tk我什么都不懂。但是这个周末我可以试着去学一下。 - ikegami
1个回答

1

存在逻辑错误:

my $dir = $wdir->open($dir) || die $wdir->error;
my $dir_complete = "$dir/a.txt";

$wdir->open('path') 返回的是一个对象,而不是一个字符串。你不能像使用路径一样使用它。但更糟糕的是,很遗憾,似乎 Tk 实现还没有支持 Unicode 文件名(包括 chooseDirectory)。我想你将不得不编写一个自定义目录选择器,但我不确定这是否可行。

这可以列出 ascii 字符文件夹中的文件(并且 ->fetch 可以列出 utf-8 文件),但在打开带有 utf-8 字符的文件夹时会崩溃。好吧,公平地说,它在打开 ?????? 时会崩溃。

use strict;
use warnings;
use Win32::Unicode::Dir;
use Win32::Unicode::Console;
use Encode;
use Tk;

my $mw = Tk::MainWindow->new;
my $tissue_but = $mw->Button(
    -text => 'Select dir',
    -command =>  [ \&select_unicode_directory ],
);
$tissue_but->grid( -row => 3, -column => 1 );
Tk::MainLoop();

sub select_unicode_directory {
    my $wdir = Win32::Unicode::Dir->new;
    my $selected = $mw->chooseDirectory(-parent =>$mw);
       # http://search.cpan.org/dist/Tk/pod/chooseDirectory.pod#CAVEATS
       $selected = encode("utf-8", $selected);
    print "selected: $selected\n";

    $wdir->open($selected) || die $wdir->error;

    print "\$mw->chooseDirectory:    $selected\n";
    print "\$wdir->open(\$selected): $wdir\n";


# CRASH HERE, presumably because winders can't handle '?' in a file (dir) name
    for ($wdir->fetch) {
# http://search.cpan.org/~xaicron/Win32-Unicode-0.38/lib/Win32/Unicode/Dir.pm
        next if /^\.{1,2}$/;
        my $path = "$selected/$_";
        if (file_type('f', $path)) { print "file: $path\n"; } 
        elsif (file_type('d', $path)) { print " dir: $path\n"; }
    }
    print "closing \n";
    $wdir->close || die $wdir->error;

}

示例输出(打开 Поиск/):

以下两个示例均使用:Strawberry Perl 5.12.3 for MSWin32-x64-multi-thread构建

selected: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/?????
$mw->chooseDirectory:    C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/?????
$wdir->open($selected): Win32::Unicode::Dir=HASH(0x2e38158)
>>> perl crash <<<

示例输出(打开“Поиск”的父级):

selected: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk
$mw->chooseDirectory:    C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk
$wdir->open($selected): Win32::Unicode::Dir=HASH(0x2b92c10)
file: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/.select_uni_dir.pl.swp
file: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/o
file: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/o.dir
file: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/select_uni_dir.pl
file: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/select_uni_file.pl
 dir: C:/cygwin/home/jaroslav/tmp/so/perl/open-file-tk/Поиск

结论

Tk目录选择器返回?????而不是“搜索”。在寻找启用Tk中的Unicode的方法时,我发现了这个网站:

http://search.cpan.org/dist/Tk/pod/UserGuide.pod#Perl/Tk_and_Unicode :

(...) 不幸的是,仍有一些Perl对Unicode无知的地方。其中之一就是文件名。因此,Perl/Tk中的文件选择器不能正确处理文件名的编码。当前它们假定文件名使用iso-8859-1编码,至少在Unix系统上是这样。一旦Perl有了文件名编码的概念,那么Perl/Tk也将实现这样的方案。

因此,乍一看似乎你所尝试的是不可能的(除非你编写或查找自定义dir-selector)。实际上,提交这个错误可能并不是一个坏主意,因为GUI确实显示了“搜索”,所以错误在返回值中。


实际上,“对 Unicode 一无所知的 Perl” 使用 'C'(原始)作为字符表,非常类似于 'iso-8859-1',但是在不同平台上'C'表是不同的。我们可以将其理解为“本地原始8字节系统字符表”。 - Znik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接