合并两个 yml 文件不处理重复项?

Merge two yml files does not handle duplicates?

我正在尝试使用 Hash::Merge perl 模块合并 2 个 yml 文件。并尝试使用 YMAL 模块中的 Dump 将其转储到 yml 文件。

use strict;
use warnings;
use Hash::Merge qw( merge );
Hash::Merge::set_behavior('RETAINMENT_PRECEDENT');
use File::Slurp qw(write_file);
use YAML;
my $yaml1 = $ARGV[0];
my $yaml2 = $ARGV[1];
my $yaml_output = $ARGV[2];
my $clkgrps = &YAML::LoadFile($yaml1);
my $clkgrps1 = &YAML::LoadFile($yaml2);
my $clockgroups = merge($clkgrps1, $clkgrps);
my $out_yaml = Dump $clockgroups;
write_file($yaml_output, { binmode => ':raw' }, $out_yaml);

合并 yml 文件后,我可以看到重复的条目,即两个 yml 文件中的以下内容相同。合并时将它们视为不同的条目。我们是否有任何隐含的方式来处理重复项?

从YAML文件中得到的数据结构一般包含键值是arrayrefs和hashrefs。在您的测试用例中,这是键 test.

的数组引用

那么像Hash::Merge这样的工具只能将hashrefs添加到属于同一个key的arrayref;它并不意味着比较数组元素,因为没有通用的标准。因此,您需要自己执行此操作以删除重复项,或将您选择的任何特定规则应用于数据。

处理这个问题的一种方法是序列化(因此字符串化)每个可能包含重复项的 arrayref 中的复杂数据结构,以便能够构建以它们为键的散列,这是处理重复项的标准方法(使用O(1) 复杂度,尽管可能有一个大常数)。

在 Perl 中有多种序列化数据的方法。我推荐 JSON::XS,因为它是一种非常快速的工具,其输出可以被任何语言和工具使用。 (当然,请研究其他人,这可能更适合您的确切需求。)

一个简单的完整示例,使用您的测试用例

use strict;
use warnings;
use feature 'say';
use Data::Dump qw(dd pp);

use YAML;
use JSON::XS;
use Hash::Merge qw( merge );
#Hash::Merge::set_behavior('RETAINMENT_PRECEDENT');  # irrelevant here

die "Usage: [=10=] in-file1 in-file2 output-file\n" if @ARGV != 3;

my ($yaml1, $yaml2, $yaml_out) = @ARGV;

my $hr1 = YAML::LoadFile($yaml1);
my $hr2 = YAML::LoadFile($yaml2);
my $merged = merge($hr2, $hr1);
#say "merged: ", pp $merged;

for my $key (keys %$merged) {
    # The same keys get overwritten
    my %uniq = map { encode_json $_ => 1 } @{$merged->{$key}};
    
    # Overwrite the arrayref with the one without dupes
    $merged->{$key} = [ map { decode_json $_ } keys %uniq ];
}
dd $merged;

# Save the final structure...

更复杂的数据结构需要更明智的遍历;考虑为此使用一个工具。

使用打印的问题中所示的文件

{
  test => [
    { directory => "LIB_DIR", name => "ObsSel.ktc", project => "TOT" },
    { directory => "MODEL_DIR", name => "pipe.v", project => "TOT" },
    {
      directory => "PCIE_LIB_DIR",
      name => "pciechip.ktc",
      project => "PCIE_MODE",
    },
    { directory => "NAME_DIR", name => "fame.v", project => "SINGH" },
    { directory => "TREE_PROJECT", name => "Syn.yml", project => "TOT" },
  ],
}

(我使用 Data::Dump 来显示复杂的数据,因为它的简单性和默认的紧凑输出。)

如果序列化和比较整个结构存在问题,请考虑使用某种摘要(校验和、散列)。

另一种选择是按原样比较数据结构,以便手动解决重复问题。对于复杂数据结构的比较,我喜欢使用 Test::More,它非常适合在任何测试之外进行比较。但是当然也有专门的工具,比如 Data::Compare.


最后,不像上面那样手动处理天真的 merge 的结果,可以使用 Hash::Merge::add_behavior_spec and then have the module do it all. For specific examples of how to use this feature see for instance 编写所需的行为代码 和 this post and .

请注意,在这种情况下,您仍需编写所有代码来完成上述工作,但该模块确实让您无需动手操作一些技巧。