有时我必须从外部源读取文本文件,这些文件可以使用各种字符编码。通常是UTF-8,Latin-1或Windows CP-1252。
是否可以方便地读取这些文件,像Vim这样的编辑器来自动检测编码?
我希望有一些简单的东西:
open(my $f, '<:encoding(autodetect)', 'foo.txt') or die 'Oops: $!';
请注意,Encode::Guess并不能解决问题:只有在可以明确检测到编码的情况下,它才起作用,否则会崩溃。大多数UTF-8数据名义上都是有效的latin-1数据,因此在UTF-8文件上失败。
示例:
#!/usr/bin/env perl
use 5.020;
use warnings;
use Encode;
use Encode::Guess qw(utf-8 cp1252);
binmode STDOUT => 'utf8';
my $utf8 = "H\x{C3}\x{A9}llo, W\x{C3}\x{B8}rld!"; # "Héllo, Wørld!" in UTF-8
my $latin = "H\x{E9}llo, W\x{F8}rld!"; # "Héllo, Wørld!" in CP-1252
# Version 1
my $enc1 = Encode::Guess->guess($latin);
if (ref($enc1)) {
say $enc1->name, ': ', $enc1->decode($latin);
}
else {
say "Oops: $enc1";
}
my $enc2 = Encode::Guess->guess($utf8);
if (ref($enc2)) {
say $enc2->name, ': ', $enc2->decode($utf8);
}
else {
say "Oops: $enc2";
}
# Version 2
say decode("Guess", $latin);
say decode("Guess", $utf8);
输出:
cp1252: Héllo, Wørld!
Oops: utf-8-strict or utf8 or cp1252
Héllo, Wørld!
cp1252 or utf-8-strict or utf8 at ./guesstest line 32.
Borodin答案中“更新”下的版本仅适用于UTF-8数据,但不适用于Latin-1数据。
如果您需要同时处理UTF-8和Latin-1文件,则无法使用Encode::Guess
。
这个问题与this one并非同一问题:我正在寻找一种打开文件时自动检测的方法。
答案 0 :(得分:2)
这取决于您可能要处理的编码。
看看
Encode::Guess
模块
通常,很容易判断您是否 是否有ASCII文件,因为代码点是7位,所以超过127的值表示它不是ASCII 。还可以可靠地判断您的文件 不是 ,因为多字节字符的最高有效位具有特定的顺序。其他任何事情都不那么可靠,但是可能
我不知道您可能会使用哪种编码,但这是一般的想法。 Encode::Guess
是核心Encode
模块的一部分,因此不需要安装
use Encode::Guess;
my $enc = guess_encoding($data, qw/ ascii cp1252 iso-8859-1 utf-8 /);
say ref $enc? $enc->name : $enc, "\n";
或者您可以执行最佳猜测解码,而无需检查模块选择了什么
use Encode::Guess qw/ ascii cp1252 iso-8859-1 utf-8 /;
my $chars = decode("Guess", $data);
请记住,您提供的编码可能 更少 ,猜测的准确性就越高。您应该仔细阅读模块文档
这是OP的一线示范,证明Encode::Guess
“没有成功” 写为正确的程序
请注意,如文档所述,guess_encoding
有时可能返回类似utf-8 or iso-8859-1
的字符串,在这种情况下,程序员必须处理歧义。 *在OP的示例中情况并非如此:数据被标识为UTF-8编码,并且guess_encoding
和decode('guess', ...)
均返回正确的结果
您可以使用此代码对您选择的任何字节字符串测试Encode::Guess
:只需修改$raw
的内容
use strict;
use warnings 'all';
use feature 'say';
use open qw/ :std encoding(UTF-8) /;
use Encode;
use Encode::Guess;
use Data::Dump;
my $raw = qq/H\x{C3}\x{A9}llo, W\x{C3}\x{B8}rld!/;
my $enc = guess_encoding($raw);
if ( my $class = ref $enc ) {
printf qq{Guessed encoding \$enc is an %s object "%s"\n}, $class, $enc->name
}
else {
printf qq{Guessed encoding \$enc is a scalar "%s"\n}, $enc;
}
my $chars = decode('guess', $raw);
printf "Decoded characters: %s\n", $chars;
dd $chars;
Guessed encoding $enc is an Encode::utf8 object "utf8"
Decoded characters: Héllo, Wørld!
"H\xE9llo, W\xF8rld!"
答案 1 :(得分:1)
这是我当前的解决方法。至少对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;
}