gpt4 book ai didi

perl - readdir 以什么编码返回文件名?

转载 作者:太空宇宙 更新时间:2023-11-03 16:51:44 27 4
gpt4 key购买 nike

这是我希望打印的 Perl 脚本 found执行时:

#!/usr/bin/perl
use warnings;
use strict;
use utf8;
use Encode;

use constant filename => 'Bärlauch';

open (my $out, '>', filename) or die;
close $out;

opendir(my $dir, '.') or die;
while (my $filename_read = readdir($dir)) {
# $filename_read = encode('utf8', $filename_read);
print "found\n" if $filename_read eq filename;
}

该脚本首先创建一个名为 filename 的文件。 . (运行脚本后,我可以用 ls 验证文件是否存在,并且该文件不是用“funny”字符创建的。)

然后脚本遍历当前工作目录中的文件并打印 found如果存在名称与刚刚创建的文件相同的文件。显然应该是这样。

但是,它没有(Ubuntu、bash、 LANG=en_US.UTF8)

如果我将常量更改为 Barlauch ,它按预期工作并打印 found .

取消注释 $filename_read = encode('utf8', $filename_read);不会改变行为。

对此是否有解释,我该怎么做才能识别包含 Umlaute 的文件名?

最佳答案

改写的问题(按照我的解释)是:

Why doesn't readdir return back the newly created filename? (Here, represented by the variable filename which is set to Bärlauch).



(注意: filename 是一个 Perl 常量变量,所以这就是为什么它前面缺少 $ 符号的原因。)

背景:

第一个注意事项:由于 use utf8程序开头的语句, filename将在编译时升级为 Unicode 字符串,因为它包含非 ASCII 字符。来自 utf8 的文档附注:

Enabling the utf8 pragma has the following effect: Bytes in the source text that are not in the ASCII character set will be treated as being part of a literal UTF-8 sequence. This includes most literals such as identifier names, string constants, and constant regular expression patterns.



而且,根据 perluniintro “Perl 的 Unicode 模型”部分:

The general principle is that Perl tries to keep its data as eight-bit bytes for as long as possible, but as soon as Unicodeness cannot be avoided, the data is transparently upgraded to Unicode.

...

Internally, Perl currently uses whatever the native eight-bit character set of the platform (for example Latin-1) is, defaulting to UTF-8, to encode Unicode strings.


filename 中的非 ASCII 字符是字母 ä .如果您使用 ISO 8859-1 扩展 ASCII 编码 (Latin-1),则将其编码为字节值 0xE4 ,看到这个 tableascii-code.com .
但是,如果您删除了 ä字符来自 filename ,它将仅包含 ASCII 字符,因此即使您使用 utf8,它也不会在内部升级为 Unicode。语用。

所以 filename现在是带有内部 UTF-8 的 Unicode 字符串标志设置(有关 UTF-8 标志的更多信息,请参阅 utf8 编译指示)。注意字母 ä以 UTF-8 编码为两个字节 0xC3 0xA4 .

写入文件:

写入文件时,文件名会发生什么?如 filename是一个 Unicode 字符串,它将被编码为 UTF-8。但是请注意,不必对 filename 进行编码第一个( encode_utf8( filename ) )。见 Creating filenames with unicode characters想要查询更多的信息。因此,文件名以 UTF-8 编码字节的形式写入磁盘。

读回文件名:

当试图从磁盘读回文件名时, readdir即使文件名包含以 UTF-8 编码的字节,也不返回 Unicode 字符串(设置了 UTF-8 标志的字符串)。它返回二进制或字节字符串,见 perlunitut有关字节字符串与字符 (Unicode) 字符串的讨论。

为什么不 readdir返回Unicode字符串?首先,根据
perlunicode “当 Unicode 不发生时”部分:

There are still many places where Unicode (in some encoding or another) could be given as arguments or received as results, or both in Perl, but it is not. (...)

The following are such interfaces. For all of these interfaces Perl currently (as of v5.16.0) simply assumes byte strings both as arguments and results. (...)

One reason that Perl does not attempt to resolve the role of Unicode in these situations is that the answers are highly dependent on the operating system and the file system(s). For example, whether filenames can be in Unicode and in exactly what kind of encoding, is not exactly a portable concept. (...)

  • chdir, chmod, chown, chroot, exec, link, lstat, mkdir, rename, rmdir, - stat, symlink, truncate, unlink, utime, -X
  • %ENV
  • glob (aka the <*>)
  • open, opendir, sysopen
  • qx (aka the backtick operator), system
  • readdir, readlink


所以 readdir返回字节字符串,因为通常不可能先验地知道文件名的编码。有关为什么这是不可能的背景信息,请参见例如:
  • filename在维基百科中,“编码互操作性”小节,
  • Understanding Unix file name encoding在 unix.stackexchange.com 上

  • 字符串比较:

    现在,最后您尝试比较读取的文件名 $filename_read使用变量 filename :
    print "found\n" if $filename_read eq filename;

    在这种情况下 $filename_read之间的唯一区别和 filename$filename_read没有设置 UTF-8 标志(它不是 Perl 内部识别为“Unicode 字符串”的)。

    现在有趣的是 eq 的结果运算符将取决于 $filename_read 中的字节是否为是否是纯 ASCII。根据 Encode 的文档模块:

    Before the introduction of Unicode support in Perl, The eq operator just compared the strings represented by two scalars. Beginning with Perl 5.8, eq compares two strings with simultaneous consideration of the UTF8 flag.

    ...

    When you decode, the resulting UTF8 flag is on--unless you can unambiguously represent data.



    所以在你的情况下, eq会考虑 UTF-8标志自 $file_name_read不包含纯 ASCII,因此它会
    考虑两个字符串 不是 平等的。如 $filename_readfilename其中相同且仅包含纯 ASCII 字节(并且 filename 仍然设置了 UTF-8 标志, $filename_read 没有设置 UTF-8 标志),然后 eq会认为两个字符串相等。参见 Encode 文档中的讨论有关此行为背景的更多信息。

    结论:

    因此,如果您相对确信所有文件名都是 UTF-8 编码,则可以通过解码从 readdir 返回的字节字符串来解决问题中的问题。转换为 Unicode 字符串(强制设置 UTF-8 标志):
    $filename_read = Encode::decode_utf8( $filename_read );

    更多详情

    注意:由于 Unicode 允许相同字符的多种表示形式,因此存在两种形式的 ä (带分音符的拉丁文小写字母 A)在 Bärlauch .例如,
  • U+00E4 是NFC(Normalization Form canonical Composition)形式,
  • U+0061.0308 是 NFD(Normalization Form canonical Decomposition)形式。

  • 在我的平台 (Linux) 上,UTF-8 编码的文件名使用 NFC 形式存储,但在 Mac OS 上,它们使用 NFD 形式。见 Encode::UTF8Mac 想要查询更多的信息。这意味着如果您在 Linux 机器上工作,例如克隆由 Mac 用户创建的 Git 存储库,您可以轻松地在 Linux 机器上获得 NFD 编码的文件名。所以 Linux 文件系统并不关心文件名的编码方式。它只是将其视为一个字节序列。因此,即使我的语言环境是 "en_US.UTF-8",我也可以轻松编写一个脚本来创建一个 ISO-Latin-1 编码的文件名。 .当前的语言环境设置只是应用程序的指导方针,但如果应用程序忽略了语言环境设置,则没有什么可以阻止它们这样做。

    因此,如果您不确定文件名是否从 readdir 返回正在使用 NFC 或 NFD,您应该在解码后始终分解:
    use Unicode::Normalize;
    print "found\n" if NFD( $filename_read ) eq NFD( filename );

    另见 Perl Unicode Cookbook “总是分解和重新组合”部分。

    最后,要了解有关区域设置如何与 Perl 中的 Unicode 协同工作的更多信息,您可以查看:
  • perllocale 、“Unicode 和 UTF-8”部分和
  • Encode::Locale .
  • 关于perl - readdir 以什么编码返回文件名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37027051/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com