我有两个来自不同操作系统的Json文件。
这两个文件均以UTF-8
编码,并包含UTF-8
编码filenames
。
一个文件来自OS X,文件名为NFD格式:(od -bc
)
0000160 166 145 164 154 141 314 201 057 110 157 165 163 145 040 155 145
v e t l a ́ ** / H o u s e m e
第二个包含相同的文件名,但是采用NFC形式:
000760 166 145 164 154 303 241 057 110 157 165 163 145 040 155 145 163
v e t l á ** / H o u s e m e s
据我所知,这称为“不同的规范化”,并且有一个CPAN模块Unicode::Normalize
用于处理它。
我正在阅读下一个文件:
my $json1 = decode_json read_file($file1, {binmode => ':raw'}) or die "..." ;
my $json2 = decode_json read_file($file2, {binmode => ':raw'}) or die "..." ;
read_file来自File::Slurp
和来自JSON::XS
的decode_json。
将JSON读入perl结构,从一个json文件中,文件名进入key
位置,第二个文件进入values
。我需要搜索第一个哈希中的哈希值key
等价到第二个哈希值的value
,因此需要确保它们与“二进制”相同。
尝试下一个:
grep 'House' file1.json | perl -CSAD -MUnicode::Normalize -nlE 'print NFD($_)' | od -bc
和
grep 'House' file2.json | perl -CSAD -MUnicode::Normalize -nlE 'print NFD($_)' | od -bc
为我产生相同的输出。
现在问题:
$hashrefs
?或需要在decode_json
之后运行像两个哈希一样?
while(my($k,$v) = each(%$json1)) {
$copy->{ NFD($k) } = NFD($v);
}
简而言之:
$href
内部获得相同的规范化?可以在每个NFD
key
上明确地执行value
并创建哈希的另一个NFD规范化(大)副本,从而实现更好的效果吗?一些提示,建议 - 请...
因为我的英语非常糟糕,所以这是问题的模拟
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 Data::Dumper;
use JSON::XS;
#Creating two files what contains different "normalizations"
my($nfc, $nfd);;
$nfc->{ NFC('key') } = NFC('vál');
$nfd->{ NFD('vál') } = 'something';
#save as NFC - this comes from "FreeBSD"
my $jnfc = JSON::XS->new->encode($nfc);
open my $fd, ">:utf8", "nfc.json" or die("nfc");
print $fd $jnfc;
close $fd;
#save as NFD - this comes from "OS X"
my $jnfd = JSON::XS->new->encode($nfd);
open $fd, ">:utf8", "nfd.json" or die("nfd");
print $fd $jnfd;
close $fd;
#now read them
my $jc = decode_json read_file( "nfc.json", { binmode => ':raw' } ) or die "No file" ;
my $jd = decode_json read_file( "nfd.json", { binmode => ':raw' } ) or die "No file" ;
say $jd->{ $jc->{key} } // "NO FOUND"; #wanted to print "something"
my $jc2;
#is here a better way to DO THIS?
while(my($k,$v) = each(%$jc)) {
$jc2->{ NFD($k) } = NFD($v);
}
say $jd->{ $jc2->{key} } // "NO FOUND"; #OK
答案 0 :(得分:1)
即使现在将一些文件名转换为相同的规范化进行比较可能很重要,但如果JSON数据具有不同的规范化,几乎任何地方都可能出现其他意外问题。
所以我的建议是将两个来源的整个输入规范化,作为进行任何解析之前的第一步(即,在读取文件的同时decode_json
之前)。这不应该破坏任何JSON结构,因为它们是使用ASCII字符分隔的。那么你现有的perl代码应该能够盲目地假设所有UTF8字符具有相同的规范化。
$rawdata1 = read_file($file1, {binmode => ':raw'}) or die "...";
$rawdata2 = read_file($file2, {binmode => ':raw'}) or die "...";
my $json1 = decode_json NFD($rawdata1);
my $json2 = decode_json NFD($rawdata2);
为了使这个过程稍快一些(它应该已经足够快,因为模块使用快速的XS过程),你可以找出两个数据文件中的一个是否已经处于某种规范化形式,然后保留该文件保持不变,并将其他文件转换为该格式。
例如:
$rawdata1 = read_file($file1, {binmode => ':raw'}) or die "...";
$rawdata2 = read_file($file2, {binmode => ':raw'}) or die "...";
if (checkNFD($rawdata1)) {
# then you know $file1 is already in Normalization Form D
# (i.e., it was formed by canonical decomposition).
# so you only need to convert $file2 into NFD
$rawdata2 = NFD($rawdata2);
}
my $json1 = decode_json $rawdata1;
my $json2 = decode_json $rawdata2;
当然,您现在必须在开发时间进行实验,看看是否有一个或另一个输入文件已经处于规范化形式,然后在最终版本的代码中,您将不再需要条件语句,但只是将其他输入文件转换为相同的规范化形式。
另请注意,建议以NFC形式生成输出(如果您的程序生成任何将在以后存储和使用的输出)。请参阅此处,例如:http://www.perl.com/pub/2012/05/perlunicookbook-unicode-normalization.html
答案 1 :(得分:1)
嗯。我不能建议你一些更好的“编程”解决方案。但为什么不运行
perl -CSDA -MUnicode::Normalize -0777 -nle 'print NFD($_)' < freebsd.json >bsdok.json
perl -CSDA -MUnicode::Normalize -0777 -nle 'print NFD($_)' < osx.json >osxok.json
现在你的脚本可以读取和使用它们,因为它们都在相同的规范化中?因此,在脚本中搜索som编程解决方案 ,然后在进入脚本之前解决问题。 (第二个命令是不必要的 - 在文件级别上进行简单转换。当然,遍历数据结构更容易......
答案 2 :(得分:1)
在搜索正确的解决方案时,我发现:软件是c * rp :)请参阅:https://stackoverflow.com/a/17448888/632407。
无论如何,为您的特定问题找到了解决方案 - 如何使用文件名读取json而不管规范化:
而不是你的:
#now read them
my $jc = decode_json read_file( "nfc.json", { binmode => ':raw' } ) or die "No file" ;
my $jd = decode_json read_file( "nfd.json", { binmode => ':raw' } ) or die "No file" ;
使用下一个:
#now read them
my $jc = get_json_from_utf8_file('nfc.json') ;
my $jd = get_json_from_utf8_file('nfd.json') ;
...
sub get_json_from_utf8_file {
my $file = shift;
return
decode_json #let parse the json to perl
encode 'utf8', #the decode_json want utf8 encoded binary string, encode it
NFC #conv. to precomposed normalization - regardless of the source
read_file #your file contains utf8 encoded text, so read it correctly
$file, { binmode => ':utf8' } ;
}
这应该(至少我希望)确保分解使用JSON内容,NFC
将其转换为预编译版本,JSON:XS将读取正确解析为相同的内部perl结构。
所以你的例子打印:
something
没有遍历$json
这个想法来自Joseph Myers和Nemo;)
也许一些更熟练的程序员会给出更多提示。
答案 3 :(得分:-1)
不要手动遍历数据结构,而是让模块为您处理。