我有一个Windows Perl (5.16.1 32位)程序,它打开一个媒体文件并(使用ffmpeg)提取音频片段-其目的是将单个专辑音乐轨道(包含多首歌曲)转换为多个单独的歌曲文件。
当要处理的媒体文件的名称都是ASCII字符时,这一切都工作得很好。我最近在一个包含俄语字符的文件名上尝试了这个程序,该程序在几个方面都失败了。
虽然这肯定与Unicode有关,而且我以前从未需要使用Unicode做任何事情-但我对这里遇到的失败的各个方面感到相当困惑,也不知道解决我现在面临的各种问题的方法。
我已经将此提炼到最低限度,以演示问题。
如果我打开一个cmd窗口,输入'chcp',返回值是437。
如果我执行'dir‘命令,则会显示以下内容:
04/01/2019 11:46 AM 71,982,427 IC3PEAK альбом Сладкая.mkv
06/10/2020 10:42 PM 275 test.pl(请注意,在我的cmd窗口中,俄语字符显示为俄语字符。)
我的'test.pl‘Perl脚本如下:
use open ":std", ":encoding(UTF-8)";
$media = "IC3PEAK альбом Сладкая.mkv";
if (-e $media) {
print "Media file does exist\n";
} else {
print "Media file does NOT exist\n";
}
open(IN, $media) || die "Media file ($media) can not be opened!\n";当这个Perl脚本运行时,使用默认的chcp值437,我得到以下输出:
Media file does NOT exist
Media file (IC3PEAK ├É┬░├É┬╗├æ┬î├É┬▒├É┬╛├É┬╝ ├É┬í├É┬╗├É┬░├É┬┤├É┬║├É┬░├æ┬Å.mkv) can not be opened!如果我在cmd窗口中运行'chcp 1250‘,并重新运行此Perl脚本,则会得到以下输出:
Media file does NOT exist
Media file (IC3PEAK Ă°Ă»ÑŒĂ±ĂÂľĂÂĽ Ă¡Ă»Ă°Ă´ĂÂşĂ°Ñ.mkv) can not be opened!问题1:我被告知媒体文件不存在。
问题2:当我将媒体文件名打印到STDOUT时,注意到显示的文件名不再与我执行'dir‘命令时的外观匹配吗?
有谁能提出解决这两个问题的建议吗?
PS -注意,当我将磁盘文件名更改为纯ASCII 'IC3PEAK.mkv',并将$media变量更改为也等于‘IC3PEAK.mkv’时,运行修改后的Perl脚本会得到:
Media file does exist发布于 2020-06-12 13:18:37
以下代码在Windows101903中进行了测试,perl -MWin32 -e"CORE::say Win32::GetACP()"使用Win32草莓返回ACP1252 (Win 10 North America) -perl 5.30.2.1 #1 Tue Mar 17 03:21:32 2020 x64。
首次尝试安装cpan Win32::Unicode::File失败,并显示t/04_print.t (Wstat: 768测试: 13失败: 3)消息。
在谷歌中快速搜索一下,就可以在Perl Monks上关注post。看起来Win32::Unicode::File安装的问题已经存在一段时间了。
注意: ikegami指出,该模块可以强制安装,失败的测试可以忽略。请看他下面的评论。
下面的测试代码证实了强制安装cpan -f -i Win32::Unicode::File会产生预期的结果。
use strict;
use warnings;
use feature 'say';
use utf8;
use Win32::Console;
use Win32::Unicode::File;
Win32::Console::OutputCP( 65001 );
binmode STDOUT, ':encoding(UTF-8)';
binmode STDERR, ':encoding(UTF-8)';
my $fname = 'Доброе утро Россия.mkv';
my $fh = Win32::Unicode::File->new;
open $fh, '<:encoding(UTF-8)', $fname
or die "Can't open $fname $!";
while( <$fh> ) {
say;
}
close $fh;输入文件Доброе утро Россия.mkv的内容为
Доброе утро Россия正如上面提到的帖子所建议的那样,我求助于尝试Win32::LongPath作为替代方案。模块安装已成功完成。
use strict;
use warnings;
use feature 'say';
use utf8;
use Win32::Console;
use Win32::LongPath;
Win32::Console::OutputCP( 65001 );
binmode STDOUT, ':encoding(UTF-8)';
binmode STDERR, ':encoding(UTF-8)';
my $fname = 'IC3PEAK альбом Сладкая.mkv';
my $fh;
openL \$fh, '<:encoding(UTF-8)', $fname
or die "Can't open $fname ($^E)";
while( <$fh> ) {
# process input
say;
}
close $fh;在测试中使用的不是真正的文件IC3PEAKСладкая.mkv,而是具有以下内容的同名文本文件
Привет Москва注意:在真实的mkv文件上使用openL \$fh, '<', $fname读取文件内容
发布于 2020-06-12 05:20:03
需要三个修复。
不带 use utf8;的非ASCII源
您的源包含非ASCII字符。
$media = "IC3PEAK альбом Сладкая.mkv";除非您使用use utf8;,否则Perl希望源代码使用ASCII码进行编码。使用UTF-8对源代码进行编码,并使用use utf8;。
use utf8;
# String of decoded text (aka string of Unicode Code Points).
# Length = 26
my $media = "IC3PEAK альбом Сладкая.mkv";假设您的文件是使用UTF-8编码的,您所拥有的内容等同于以下内容:
use utf8;
use Encode qw( encode );
# String of text encoded using UTF-8 (aka string of bytes).
# Length = 39
my $media = encode("UTF-8", "IC3PEAK альбом Сладкая.mkv");输出编码不正确
您的代码包含
use open ":std", ":encoding(UTF-8)";这将告诉Perl以下内容:
使用UTF-8.
问题是您的终端不需要UTF-8。它应该是cp437 (在chcp 1250之前)或cp1250 (在chcp 1250之后)。
解决方案1:
调整use open行中指定的编码。This展示了如何在不对编码进行硬编码的情况下做到这一点。
当然,只有当终端的OEM代码页(使用chcp设置)支持西里尔文字符时,才能打印这些字符。这给我们带来了第二个解决方案。
解决方案2:
调整终端以提供/期望UTF-8。这可以使用以下命令来完成:
chcp 65001接受文件名的内置函数的限制
Windows为每个接受字符串的函数提供了两个版本:
您可以使用以下内容获取系统的ACP:
Win32::GetACP()" -MWin32 -e"CORE::say perl
不幸的是,Perl函数(命名操作符)使用"A“版本的系统调用,并期望/返回使用ACP编码的文本。这严重限制了可以传递给它们的文件名。
例如,我的系统的ACP是1252,因此系统调用的"A“版本不支持西里尔字符。这意味着我无法让open、-e等处理包含西里尔字符的文件名。唉哟。
Win32-Unicode distribution可以在这方面提供帮助。例如,-e只是对stat的调用,而Win32::Unicode::File提供了statW,这是一个接受文件名作为解码文本的stat版本。类似地,它提供了open的替代品。
相同的模块
https://stackoverflow.com/questions/62318215
复制相似问题