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

RAM*_*AMA 2 perl yaml perl-data-structures

我正在尝试使用Hash::Mergeperl 模块合并 2 个 yml 文件。并尝试使用DumpfromYMAL模块将其转储到 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);
Run Code Online (Sandbox Code Playgroud)

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

zdi*_*dim 5

从 YAML 文件中获得的数据结构通常包含键,其值为 arrayrefs 和 hashrefs。在您的测试用例中,这是 key 的 arrayref test

那么像这样的工具Hash::Merge只能将hashrefs添加到属于同一键的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: $0 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...
Run Code Online (Sandbox Code Playgroud)

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

使用问题中显示的文件,这会打印

{
  测试 => [
    { 目录 => "LIB_DIR", 名称 => "ObsSel.ktc", 项目 => "TOT" },
    { 目录 => "MODEL_DIR", 名称 => "pipe.v", 项目 => "TOT" },
    {
      目录 => "PCIE_LIB_DIR",
      名称 => "pciechip.ktc",
      项目 => "PCIE_MODE",
    },
    { directory => "NAME_DIR", name => "fame.v", project => "SINGH" },
    { 目录 => "TREE_PROJECT", 名称 => "Syn.yml", 项目 => "TOT" },
  ],
}

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

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

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


最后,不是merge像上面那样手动处理 naive 的结果,而是可以使用Hash::Merge::add_behavior_spec编写所需的行为,然后让模块完成所有操作。有关如何使用此功能的具体示例,请参见例如这篇文章这篇文章这篇文章

请注意,在这种情况下,您仍然需要编写所有代码来完成上述工作,但该模块确实让您摆脱了一些机制。