【问题标题】:Normalization on utf8 filenames stored in JSON with perl使用 perl 对存储在 JSON 中的 utf8 文件名进行规范化
【发布时间】:2013-07-02 19:19:57
【问题描述】:

我有两个来自不同操作系统的 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,decode_json 来自JSON::XS

将 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

为我产生相同的输出。

现在问题:

  • 如何简单地读取两个 json 文件以在 $hashrefs 中获得相同的规范化

或者在decode_json 之后需要在两个哈希上运行类似的东西?

while(my($k,$v) = each(%$json1)) {
    $copy->{ NFD($k) } = NFD($v);
}

简而言之:

  • 如何读取不同的 JSON 文件以在 perl $href 的“内部”获得相同的规范化?在每个 key value 上显式执行 NFD 并创建另一个 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

【问题讨论】:

    标签: json perl utf-8


    【解决方案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 ;)

    也许一些更熟练的程序员会给出更多的提示。

    【讨论】:

      【解决方案2】:

      尽管现在仅将几个文件名转换为相同的规范化进行比较可能很重要,但如果 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

      【讨论】:

      • 不幸的是,decode_json NFD(read_file( "nfd.json", { binmode => ':raw' } )) 不起作用。获取错误消息:Wide character in subroutine entry at read2.pl line 18. 抱怨decode_json
      • 是的,像这样的问题是乏味的,而且通常应该有效的简单解决方案不起作用。让我仔细考虑一下,看看我是否可以为您诊断(我现在正试图兼顾 SO 和辅导中心)。这个问题就像我几乎每周都会处理的问题一样,有很多意想不到的怪癖表明世界在 Unicode 方面还不是很完美——尽管当我指出这个现实时,我只是得到了一堆反对意见喜欢这里:(stackoverflow.com/questions/16594636/….
      • 我建议您使用 NFC 作为标准化,然后再传递给 decode_json。试试看。
      • @JosephMyers,不要因为投反对票而烦恼。总有粉丝和不承认自己错了的人。您应该说出您的意见,而不必担心被否决。即使全世界都认为你错了,你也可能是对的。
      • 谢谢,感谢@Lajos-Arpad。
      【解决方案3】:

      嗯。我不能建议你一些更好的“编程”解决方案。但是为什么根本不运行

      perl -CSDA -MUnicode::Normalize -0777 -nle 'print NFD($_)' < freebsd.json >bsdok.json
      perl -CSDA -MUnicode::Normalize -0777 -nle 'print NFD($_)' < osx.json     >osxok.json
      

      现在您的脚本可以读取和使用两者,因为它们都处于相同的规范化状态?因此,请不要在脚本的 inside 中搜索 som 编程解决方案,而是在进入脚本之前解决问题。 (第二个命令是不必要的 - 文件级别的简单转换。当然更容易遍历数据结构......

      【讨论】:

        【解决方案4】:

        与其手动遍历数据结构,不如让模块为您处理。

        【讨论】:

          猜你喜欢
          • 2012-09-12
          • 2012-06-07
          • 1970-01-01
          • 2012-09-17
          • 1970-01-01
          • 2020-07-31
          • 2013-03-08
          • 2019-12-17
          • 2018-12-12
          相关资源
          最近更新 更多