读取两个 YAML 文件并比较两者中出现的模块
Read two YAML files and compare the modules that appear in both
我正在尝试比较两个 YAML 文件,如下所示
忽略所有注释行
如果文件具有相同的模块,则比较这些模块的版本
如果版本不同,则将差异打印到文件 output.log
并引发错误
file1.yml
Modules:
python:
PATH: /cfg/python/version-1.0
c:
PATH: /cfg/c/release-1.2.0
c++:
PATH: /cfg/c++/release-1.1.5
java:
PATH: /cfg/java/version-2.157
#connect:
# PATH: /cfg/connect/release-1.2.3
file2.yml
Modules:
python:
PATH: /cfg/python/version-1.1
c:
PATH: /cfg/c/release-1.2.0
java:
PATH: /cfg/java/version-2.161
eclipse:
PATH: /cfg/eclipse/version-4.5
#connect:
# PATH: /cfg/connect/release-1.2.0
期望的输出
Error:
File1.yml - python: PATH: /cfg/python/verison-1.0
file2.yml - python: PATH: /cfg/python/verison-1.1
Error:
file1.yml - java: PATH: /cfg/java/version-2.157
file2.yml - java: PATH: /cfg/java/version-2.161
我的代码
use strict;
use warnings;
open f1, "file1.yml" or die "couldn't open the file: $! \n ";
my @line1 = <f1>;
close(f1);
open f2, "file2.yml" or die "couldn't open the file: $! \n ";
my @line2 = <f2>;
close(f2);
open( OUT, ">", "error.txt" );
for ( my $i = 0; $i < @line1; $i++ ) {
for ( my $j = 0; $j < @line2; $j++ ) {
if ( $line1[$i] =~ $line2[$j] ) {
print OUT "Match Found: \n $line1[$i] \n $line2[$j]";
}
}
}
close OUT;
输出
Match Found:
Modules:
Modules:
Match Found:
python:
python:
Match Found:
c:
c:
Match Found:
java:
java:
Match Found:
#connect:
#connect:
从技术上讲,您的程序并没有任何问题,因此我不会提供修复或解释。您编写的代码与您试图解决的代码不同。
相反,我将逐步向您展示一种不同的方法。首先,请记住 YAML 是一种数据结构描述。您可以将其读入 Perl 并将其转换为 Perl 理解的本机数据结构,而不是使用文本比较。你的两个文件都有相似的结构,这使得这很简单。
读取数据结构并将其转换为不同的格式很困难。幸运的是,Perl 的众多优势之一是已经解决了很多问题,并且可以在 CPAN 上使用。
use strict;
use warnings;
use YAML 'LoadFile';
my $file1 = LoadFile('file1.yml');
my $file2 = LoadFile('file2.yml');
此程序将读取您的两个文件并使用 LoadFile
from the YAML module.
将内部的 YAML 数据结构转换为 Perl 哈希引用
它本身当然不是很有用。那么让我们检查一下这些结构是什么样的。 Data::Dumper is very useful for that, and it comes with your Perl installation. (Personally I would use Data::Printer,因为我认为输出更具可读性,但这取决于您。
use Data::Dumper;
print Dumper $file1;
print Dumper $file2;
这将向我们展示:
$VAR1 = {
'Modules' => {
'java' => {
'PATH' => '/cfg/java/version-2.157'
},
'c' => {
'PATH' => '/cfg/c/release-1.2.0'
},
'c++' => {
'PATH' => '/cfg/c++/release-1.1.5'
},
'python' => {
'PATH' => '/cfg/python/verison-1.0'
}
}
};
$VAR1 = {
'Modules' => {
'c' => {
'PATH' => '/cfg/c/release-1.2.0'
},
'java' => {
'PATH' => '/cfg/java/version-2.161'
},
'python' => {
'PATH' => '/cfg/python/verison-1.1'
},
'eclipse' => {
'PATH' => '/cfg/eclipse/version-4.5'
}
}
};
请注意密钥的顺序与它们在文件中的顺序不同。那是因为 Perl 中的散列是 无序的 。这是一个特点。为了获得一致的输出,我们需要稍后 sort
这些键。
所以我们看到两个结构都有一个键Modules。在里面,有另一个哈希引用,它以语言作为键,在每个哈希引用中,都有另一个哈希引用,只有一个 PATH 键和一个值。我们对每种语言的每个 PATH 的值感兴趣。
为了获得语言,我们迭代了两个文件之一的 Modules 的 keys
。然后我们可以一直向下比较这些值。如果它们是 not equal,我们将打印错误消息。
use v5.10; # to get say
# get shortcuts so the lines are not as long
my $modules1 = $file1->{Modules};
my $modules2 = $file2->{Modules};
foreach my $language (sort keys %{ $file1->{Modules} }) {
if ($modules1->{$language}->{PATH} ne $modules2->{$language}->{PATH} ) {
say "Error:";
say "file1.yml - $language: PATH: $modules1->{$language}->{PATH}";
say "file2.yml - $language: PATH: $modules2->{$language}->{PATH}";
}
}
我正在使用 say
instead of print
,它在末尾为我们附加了一个换行符。额外的变量 $modules1
和 $modules2
只是为了让代码更容易阅读,因为 $file1->{Modules}->{$language}->{PATH}
很长。
现在,如果我们 运行 我们的程序到此为止,我们将得到以下输出。
Error:
file1.yml - c++: PATH: /cfg/c++/release-1.1.5
file2.yml - c++: PATH:
Error:
file1.yml - java: PATH: /cfg/java/version-2.157
file2.yml - java: PATH: /cfg/java/version-2.161
Error:
file1.yml - python: PATH: /cfg/python/verison-1.0
file2.yml - python: PATH: /cfg/python/verison-1.1
Use of uninitialized value in string ne at /home/simbabque/code/scratch/scratch.pl line 133.
Use of uninitialized value in concatenation (.) or string at /home/simbabque/code/scratch/scratch.pl line 136.
这几乎就是我们想要的,但是有一些讨厌的警告(因为我们打开了 use warnings
),并且 c++ 没有任何价值。我们需要过滤掉只存在于两个文件之一中的语言。
只存在于file2.yml的语言已经不考虑了,因为我们只看来自file1.yml[=的键75=]。要从第一个文件中删除它们,我们必须在访问它们之前检查它们是否 exist
在另一个文件中。
next unless exists $modules2->{$language};
unless
keyword is like if (not ...)
, and I consider it much easier to read, especially in this very concise post-fix notation。现在程序给出了预期的输出。
Error:
file1.yml - java: PATH: /cfg/java/version-2.157
file2.yml - java: PATH: /cfg/java/version-2.161
Error:
file1.yml - python: PATH: /cfg/python/verison-1.0
file2.yml - python: PATH: /cfg/python/verison-1.1
这是全部内容。不要忘记从代码中删除 Data::Dumper ,因为它与生产程序无关。我们仅将其用作调试助手。
use strict;
use warnings;
use YAML 'LoadFile';
my $file1 = LoadFile('file1.yml');
my $file2 = LoadFile('file2.yml');
use v5.10;
# get shortcuts so the lines are not as long
my $modules1 = $file1->{Modules};
my $modules2 = $file2->{Modules};
foreach my $language (sort keys %{ $file1->{Modules} }) {
next unless exists $modules2->{$language};
if ($modules1->{$language}->{PATH} ne $modules2->{$language}->{PATH} ) {
say "Error:";
say "file1.yml - $language: PATH: $modules1->{$language}->{PATH}";
say "file2.yml - $language: PATH: $modules2->{$language}->{PATH}";
}
}
我正在尝试比较两个 YAML 文件,如下所示
忽略所有注释行
如果文件具有相同的模块,则比较这些模块的版本
如果版本不同,则将差异打印到文件
output.log
并引发错误
file1.yml
Modules:
python:
PATH: /cfg/python/version-1.0
c:
PATH: /cfg/c/release-1.2.0
c++:
PATH: /cfg/c++/release-1.1.5
java:
PATH: /cfg/java/version-2.157
#connect:
# PATH: /cfg/connect/release-1.2.3
file2.yml
Modules:
python:
PATH: /cfg/python/version-1.1
c:
PATH: /cfg/c/release-1.2.0
java:
PATH: /cfg/java/version-2.161
eclipse:
PATH: /cfg/eclipse/version-4.5
#connect:
# PATH: /cfg/connect/release-1.2.0
期望的输出
Error:
File1.yml - python: PATH: /cfg/python/verison-1.0
file2.yml - python: PATH: /cfg/python/verison-1.1
Error:
file1.yml - java: PATH: /cfg/java/version-2.157
file2.yml - java: PATH: /cfg/java/version-2.161
我的代码
use strict;
use warnings;
open f1, "file1.yml" or die "couldn't open the file: $! \n ";
my @line1 = <f1>;
close(f1);
open f2, "file2.yml" or die "couldn't open the file: $! \n ";
my @line2 = <f2>;
close(f2);
open( OUT, ">", "error.txt" );
for ( my $i = 0; $i < @line1; $i++ ) {
for ( my $j = 0; $j < @line2; $j++ ) {
if ( $line1[$i] =~ $line2[$j] ) {
print OUT "Match Found: \n $line1[$i] \n $line2[$j]";
}
}
}
close OUT;
输出
Match Found:
Modules:
Modules:
Match Found:
python:
python:
Match Found:
c:
c:
Match Found:
java:
java:
Match Found:
#connect:
#connect:
从技术上讲,您的程序并没有任何问题,因此我不会提供修复或解释。您编写的代码与您试图解决的代码不同。
相反,我将逐步向您展示一种不同的方法。首先,请记住 YAML 是一种数据结构描述。您可以将其读入 Perl 并将其转换为 Perl 理解的本机数据结构,而不是使用文本比较。你的两个文件都有相似的结构,这使得这很简单。
读取数据结构并将其转换为不同的格式很困难。幸运的是,Perl 的众多优势之一是已经解决了很多问题,并且可以在 CPAN 上使用。
use strict;
use warnings;
use YAML 'LoadFile';
my $file1 = LoadFile('file1.yml');
my $file2 = LoadFile('file2.yml');
此程序将读取您的两个文件并使用 LoadFile
from the YAML module.
它本身当然不是很有用。那么让我们检查一下这些结构是什么样的。 Data::Dumper is very useful for that, and it comes with your Perl installation. (Personally I would use Data::Printer,因为我认为输出更具可读性,但这取决于您。
use Data::Dumper;
print Dumper $file1;
print Dumper $file2;
这将向我们展示:
$VAR1 = {
'Modules' => {
'java' => {
'PATH' => '/cfg/java/version-2.157'
},
'c' => {
'PATH' => '/cfg/c/release-1.2.0'
},
'c++' => {
'PATH' => '/cfg/c++/release-1.1.5'
},
'python' => {
'PATH' => '/cfg/python/verison-1.0'
}
}
};
$VAR1 = {
'Modules' => {
'c' => {
'PATH' => '/cfg/c/release-1.2.0'
},
'java' => {
'PATH' => '/cfg/java/version-2.161'
},
'python' => {
'PATH' => '/cfg/python/verison-1.1'
},
'eclipse' => {
'PATH' => '/cfg/eclipse/version-4.5'
}
}
};
请注意密钥的顺序与它们在文件中的顺序不同。那是因为 Perl 中的散列是 无序的 。这是一个特点。为了获得一致的输出,我们需要稍后 sort
这些键。
所以我们看到两个结构都有一个键Modules。在里面,有另一个哈希引用,它以语言作为键,在每个哈希引用中,都有另一个哈希引用,只有一个 PATH 键和一个值。我们对每种语言的每个 PATH 的值感兴趣。
为了获得语言,我们迭代了两个文件之一的 Modules 的 keys
。然后我们可以一直向下比较这些值。如果它们是 not equal,我们将打印错误消息。
use v5.10; # to get say
# get shortcuts so the lines are not as long
my $modules1 = $file1->{Modules};
my $modules2 = $file2->{Modules};
foreach my $language (sort keys %{ $file1->{Modules} }) {
if ($modules1->{$language}->{PATH} ne $modules2->{$language}->{PATH} ) {
say "Error:";
say "file1.yml - $language: PATH: $modules1->{$language}->{PATH}";
say "file2.yml - $language: PATH: $modules2->{$language}->{PATH}";
}
}
我正在使用 say
instead of print
,它在末尾为我们附加了一个换行符。额外的变量 $modules1
和 $modules2
只是为了让代码更容易阅读,因为 $file1->{Modules}->{$language}->{PATH}
很长。
现在,如果我们 运行 我们的程序到此为止,我们将得到以下输出。
Error:
file1.yml - c++: PATH: /cfg/c++/release-1.1.5
file2.yml - c++: PATH:
Error:
file1.yml - java: PATH: /cfg/java/version-2.157
file2.yml - java: PATH: /cfg/java/version-2.161
Error:
file1.yml - python: PATH: /cfg/python/verison-1.0
file2.yml - python: PATH: /cfg/python/verison-1.1
Use of uninitialized value in string ne at /home/simbabque/code/scratch/scratch.pl line 133.
Use of uninitialized value in concatenation (.) or string at /home/simbabque/code/scratch/scratch.pl line 136.
这几乎就是我们想要的,但是有一些讨厌的警告(因为我们打开了 use warnings
),并且 c++ 没有任何价值。我们需要过滤掉只存在于两个文件之一中的语言。
只存在于file2.yml的语言已经不考虑了,因为我们只看来自file1.yml[=的键75=]。要从第一个文件中删除它们,我们必须在访问它们之前检查它们是否 exist
在另一个文件中。
next unless exists $modules2->{$language};
unless
keyword is like if (not ...)
, and I consider it much easier to read, especially in this very concise post-fix notation。现在程序给出了预期的输出。
Error:
file1.yml - java: PATH: /cfg/java/version-2.157
file2.yml - java: PATH: /cfg/java/version-2.161
Error:
file1.yml - python: PATH: /cfg/python/verison-1.0
file2.yml - python: PATH: /cfg/python/verison-1.1
这是全部内容。不要忘记从代码中删除 Data::Dumper ,因为它与生产程序无关。我们仅将其用作调试助手。
use strict;
use warnings;
use YAML 'LoadFile';
my $file1 = LoadFile('file1.yml');
my $file2 = LoadFile('file2.yml');
use v5.10;
# get shortcuts so the lines are not as long
my $modules1 = $file1->{Modules};
my $modules2 = $file2->{Modules};
foreach my $language (sort keys %{ $file1->{Modules} }) {
next unless exists $modules2->{$language};
if ($modules1->{$language}->{PATH} ne $modules2->{$language}->{PATH} ) {
say "Error:";
say "file1.yml - $language: PATH: $modules1->{$language}->{PATH}";
say "file2.yml - $language: PATH: $modules2->{$language}->{PATH}";
}
}