首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Perl新手首次使用Unicode (文件名、-e运算符、打开运算符和命令窗口)

Perl新手首次使用Unicode (文件名、-e运算符、打开运算符和命令窗口)
EN

Stack Overflow用户
提问于 2020-06-11 14:17:37
回答 2查看 377关注 0票数 3

我有一个Windows Perl (5.16.1 32位)程序,它打开一个媒体文件并(使用ffmpeg)提取音频片段-其目的是将单个专辑音乐轨道(包含多首歌曲)转换为多个单独的歌曲文件。

当要处理的媒体文件的名称都是ASCII字符时,这一切都工作得很好。我最近在一个包含俄语字符的文件名上尝试了这个程序,该程序在几个方面都失败了。

虽然这肯定与Unicode有关,而且我以前从未需要使用Unicode做任何事情-但我对这里遇到的失败的各个方面感到相当困惑,也不知道解决我现在面临的各种问题的方法。

我已经将此提炼到最低限度,以演示问题。

如果我打开一个cmd窗口,输入'chcp',返回值是437。

如果我执行'dir‘命令,则会显示以下内容:

代码语言:javascript
复制
04/01/2019  11:46 AM        71,982,427 IC3PEAK альбом Сладкая.mkv
06/10/2020  10:42 PM               275 test.pl

(请注意,在我的cmd窗口中,俄语字符显示为俄语字符。)

我的'test.pl‘Perl脚本如下:

代码语言:javascript
复制
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,我得到以下输出:

代码语言:javascript
复制
Media file does NOT exist
Media file (IC3PEAK альбом Сладкая.mkv) can not be opened!

如果我在cmd窗口中运行'chcp 1250‘,并重新运行此Perl脚本,则会得到以下输出:

代码语言:javascript
复制
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脚本会得到:

代码语言:javascript
复制
Media file does exist
EN

回答 2

Stack Overflow用户

发布于 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会产生预期的结果。

代码语言:javascript
复制
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的内容为

代码语言:javascript
复制
Доброе утро Россия

正如上面提到的帖子所建议的那样,我求助于尝试Win32::LongPath作为替代方案。模块安装已成功完成。

代码语言:javascript
复制
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,而是具有以下内容的同名文本文件

代码语言:javascript
复制
Привет Москва

注意:在真实的mkv文件上使用openL \$fh, '<', $fname读取文件内容

票数 2
EN

Stack Overflow用户

发布于 2020-06-12 05:20:03

需要三个修复。

不带 use utf8;非ASCII源

您的源包含非ASCII字符。

代码语言:javascript
复制
$media = "IC3PEAK альбом Сладкая.mkv";

除非您使用use utf8;,否则Perl希望源代码使用ASCII码进行编码。使用UTF-8对源代码进行编码,并使用use utf8;

代码语言:javascript
复制
use utf8;

# String of decoded text (aka string of Unicode Code Points).
# Length = 26
my $media = "IC3PEAK альбом Сладкая.mkv";

假设您的文件是使用UTF-8编码的,您所拥有的内容等同于以下内容:

代码语言:javascript
复制
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");

输出编码不正确

您的代码包含

代码语言:javascript
复制
use open ":std", ":encoding(UTF-8)";

这将告诉Perl以下内容:

使用UTF-8.

  • Encode字符对从标准输入接收的字节进行解码,这些字符使用UTF-8发送到标准输入和标准输入。
  • 对在当前词汇范围中打开的文件句柄执行相同的操作。

问题是您的终端不需要UTF-8。它应该是cp437 (在chcp 1250之前)或cp1250 (在chcp 1250之后)。

解决方案1:

调整use open行中指定的编码。This展示了如何在不对编码进行硬编码的情况下做到这一点。

当然,只有当终端的OEM代码页(使用chcp设置)支持西里尔文字符时,才能打印这些字符。这给我们带来了第二个解决方案。

解决方案2:

调整终端以提供/期望UTF-8。这可以使用以下命令来完成:

代码语言:javascript
复制
chcp 65001

接受文件名的内置函数的限制

Windows为每个接受字符串的函数提供了两个版本:

  • "UNICODE“版本(以表示”宽“的"W”为后缀)接受/返回使用UTF-16le编码的字符串。此版本支持所有Unicode "ANSI“版本(以”A“为后缀)接受/返回使用活动代码页( characters.
  • The )编码的字符串。"A“版本仅支持Unicode字符的一小部分。

您可以使用以下内容获取系统的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的替代品。

相同的模块

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62318215

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档