msc*_*cha 2 io perl character-encoding
我有时必须从外部源读取文本文件,这些文件可以使用各种字符编码;通常是 UTF-8、Latin-1 或 Windows CP-1252。
\n\n有没有一种方法可以方便地读取这些文件,像 Vim 等编辑器一样自动检测编码?
\n\n我希望有这样简单的事情:
\n\nopen(my $f, \'<:encoding(autodetect)\', \'foo.txt\') or die \'Oops: $!\';\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,Encode::Guess并不能解决这个问题:它只有在可以明确检测到编码的情况下才有效,否则就会发出嘎嘎声。大多数 UTF-8 数据名义上是有效的 latin-1 数据,因此它在 UTF-8 文件上失败。
\n\n例子:
\n\n#!/usr/bin/env perl\n\nuse 5.020;\nuse warnings;\n\nuse Encode;\nuse Encode::Guess qw(utf-8 cp1252);\n\nbinmode STDOUT => \'utf8\';\n\nmy $utf8 = "H\\x{C3}\\x{A9}llo, W\\x{C3}\\x{B8}rld!"; # "H\xc3\xa9llo, W\xc3\xb8rld!" in UTF-8\nmy $latin = "H\\x{E9}llo, W\\x{F8}rld!"; # "H\xc3\xa9llo, W\xc3\xb8rld!" in CP-1252\n\n# Version 1\nmy $enc1 = Encode::Guess->guess($latin);\nif (ref($enc1)) {\n say $enc1->name, \': \', $enc1->decode($latin);\n}\nelse {\n say "Oops: $enc1";\n}\nmy $enc2 = Encode::Guess->guess($utf8);\nif (ref($enc2)) {\n say $enc2->name, \': \', $enc2->decode($utf8);\n}\nelse {\n say "Oops: $enc2";\n}\n\n# Version 2\nsay decode("Guess", $latin);\nsay decode("Guess", $utf8);\nRun Code Online (Sandbox Code Playgroud)\n\n输出:
\n\ncp1252: H\xc3\xa9llo, W\xc3\xb8rld!\nOops: utf-8-strict or utf8 or cp1252\nH\xc3\xa9llo, W\xc3\xb8rld!\ncp1252 or utf-8-strict or utf8 at ./guesstest line 32.\nRun Code Online (Sandbox Code Playgroud)\n\nBorodin 的答案中“更新”下的版本仅适用于 UTF-8 数据,但对 Latin-1 数据会发出声音。Encode::Guess如果您需要同时处理 UTF-8 和 Latin-1 文件,则不能使用。
这并不是与这个问题相同的问题:我正在寻找一种在打开文件时自动检测的方法。
\n这是我当前的解决方法。至少对于 UTF-8 和 Latin-1(或 Windows-1252)文件来说是这样。
use 5.024;
use experimental 'signatures';
use Encode qw(decode);
sub slurp($file)
{
# Read the raw bytes
local $/;
open (my $fh, '<:raw', $file) or return undef();
my $raw = <$fh>;
close($fh);
my $content;
# Try to interpret the content as UTF-8
eval { my $text = decode('utf-8', $raw, Encode::FB_CROAK); $content = $text };
# If this failed, interpret as windows-1252 (a superset of iso-8859-1 and ascii)
if (!$content) {
eval { my $text = decode('windows-1252', $raw, Encode::FB_CROAK); $content = $text };
}
# If this failed, give up and use the raw bytes
if (!$content) {
$content = $raw;
}
return $content;
}
Run Code Online (Sandbox Code Playgroud)