Perl的YAML :: XS和unicode

时间:2011-06-19 05:09:46

标签: perl unicode yaml

我正在尝试在unicode字母上使用perl的YAML::XS模块,它看起来并不像它应该的那样。

我在脚本中写了这个(保存在utf-8中)

use utf8;
binmode STDOUT, ":utf8"; 
my $hash = {č => "ř"}; #czech letters with unicode codes U+010D and U+0159

use YAML::XS;
my $s = YAML::XS::Dump($hash);
print $s;

而不是理智的东西,-: Å被打印出来。但是,根据this link,它应该工作正常。

是的,当我YAML::XS::Load回来时,我再次获得了正确的字符串,但我不喜欢转储字符串似乎处于错误编码的事实。

我做错了吗?我总是不确定perl中的unicode,坦率地说......

澄清:我的控制台支持UTF-8。此外,当我将其打印到文件时,使用带有open $file, ">:utf8"而不是STDOUT的utf8句柄打开,它仍然无法打印正确的utf-8字母。

3 个答案:

答案 0 :(得分:7)

是的,你做错了什么。你误解了the link you mentioned的含义。 Dump& Load使用原始UTF-8字节;即包含UTF-8但且UTF-8标志关闭的字符串

当您使用:utf8图层将这些字节打印到文件句柄时,它们会被解释为Latin-1并转换为UTF-8,从而产生双编码输出(只要您可以成功读回双重解码它)。您想改为binmode STDOUT, ':raw'

另一种选择是在Dump返回的字符串上调用utf8::decode。这会将原始UTF-8字节转换为字符串(打开UTF-8标志)。然后,您可以将字符串打印到:utf8文件句柄。

所以,

use utf8;
binmode STDOUT, ":raw"; 
my $hash = {č => "ř"}; #czech letters with unicode codes U+010D and U+0159

use YAML::XS;
my $s = YAML::XS::Dump($hash);
print $s;

或者

use utf8;
binmode STDOUT, ":utf8"; 
my $hash = {č => "ř"}; #czech letters with unicode codes U+010D and U+0159

use YAML::XS;
my $s = YAML::XS::Dump($hash);
utf8::decode($s);
print $s;

同样,从文件中读取时,您希望在:raw模式下阅读或在字符串上使用utf8::encode,然后再将其传递给Load

如果可能,您应该使用DumpFile& LoadFile,让YAML :: XS处理正确打开文件。但是如果你想使用STDIN / STDOUT,你将不得不处理Dump& Load

答案 1 :(得分:2)

如果您不使用binmode STDOUT, ":utf8";,则有效。只是不要问我为什么。

答案 2 :(得分:1)

我正在使用下一个用于utf-8 JSON和YAML。没有错误处理,但可以显示如何操作。 波纹管允许我:

  • 在输入上使用NFC标准化,在输出上使用NO NDF。只需使用NFC中的所有内容
  • 可以使用支持utf8的vim和bash工具
  • 编辑YAML / JSON文件
  • “内部”perl可以处理\w正则表达式和lc uc等内容(至少根据我的需要)
  • 源代码是utf8,因此可以编写正则表达式/á/

我的“肉鸡”......

use 5.014;
use warnings;

use utf8;
use feature qw(unicode_strings);
use charnames qw(:full);
use open qw(:std :utf8);
use Encode qw(encode decode);
use Unicode::Normalize qw(NFD NFC);

use File::Slurp;
use YAML::XS;
use JSON::XS;

run();
exit;

sub run {
    my $yfilein = "./in.yaml"; #input yaml
    my $jfilein = "./in.json"; #input json
    my $yfileout = "./out.yaml"; #output yaml
    my $jfileout = "./out.json"; #output json

    my $ydata = load_utf8_yaml($yfilein);
    my $jdata = load_utf8_json($jfilein);

    #the "uc" is not "fully correct" but works for my needs
    $ydata->{$_} = uc($ydata->{$_}) for keys %$ydata;
    $jdata->{$_} = uc($jdata->{$_}) for keys %$jdata;

    save_utf8_yaml($yfileout, $ydata);
    save_utf8_json($jfileout, $jdata);
}


#using File::Slurp for read/write files
#NFC only on input - and not NFD on output (change this if you want)
#this ensure me than i can edit and copy/paste filenames without problems

sub load_utf8_yaml { return YAML::XS::Load(encode_nfc_read(shift)) }
sub load_utf8_json { return decode_json(encode_nfc_read(shift)) }
sub encode_nfc_read { return encode 'utf8', NFC read_file shift, { binmode => ':utf8' } }
#more effecient
sub rawsave_utf8_yaml { return write_file shift, {binmode=>':raw'}, YAML::XS::Dump shift }
#similar as for json
sub save_utf8_yaml { return write_file shift, {binmode=>':utf8'}, decode 'utf8', YAML::XS::Dump shift }
sub save_utf8_json { return write_file shift, {binmode=>':utf8'}, JSON::XS->new->pretty(1)->encode(shift) }

您可以尝试下一个in.yaml

---
á: ä
č: ď
é: ě
í: ĺ
ľ: ň
ó: ô
ö: ő
ŕ: ř
š: ť
ú: ů
ü: ű
ý: ž