使用正则表达式解析许可证文件
Parsing license file with regex
我想解析一个以某种方式包含这样内容的许可证文件。
COMPONENTS="A0000 B0000 G0000"
文件中必须有引号,以标记包中组件的开始和结束。我已经在 regex101.com 上测试了组合,但这并没有解决问题
正则表达式 ("?(?<components>\w+)*\s?"?)
适用于字符串 "A0000 B0000 G0000" 并且 return 是我需要的组件的各个值:A0000、B0000 等等
但是当我测试 (COMPONENTS|PACKAGES)=("?(?<components>\w+)\s?"?)
时,该块无法 return 这些值并且 return 只有第一个 A0000。
我需要帮助来提取所有这些单独的值。请帮忙。
我将在这里建议一种不同的方法。您的正则表达式模式变得越来越复杂,这不是一个好主意。
你的问题的核心是,要进行重复匹配,你还要匹配前缀文本,显然只能匹配一次。
但与其试图让您的正则表达式模式变得更加复杂,不如以不同的方式解决问题。
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %conf = do { local $/; <DATA> } =~ m/ (\w+) =" ([^"]+) " /xg;
print Dumper \%conf;
__DATA__
COMPONENTS="A0000 B0000 G0000"
PACKAGES="C0000 D0000 E0000"
这将创建一个数据结构。
$VAR1 = {
'COMPONENTS' => 'A0000 B0000 G0000',
'PACKAGES' => 'C0000 D0000 E0000'
};
如果你想要单独的值。
$_ = [split] for values %conf;
print Dumper \%conf;
这在功能上等同于此。
foreach my $key ( keys %conf ) {
#split the value on whitespace.
my @stuff = split ' ', $conf{$key};
#replace it with your array.
$conf{$key} = \@stuff;
}
产生
$VAR1 = {
'PACKAGES' => [
'C0000',
'D0000',
'E0000'
],
'COMPONENTS' => [
'A0000',
'B0000',
'G0000'
]
};
所以现在你可以写这个了。
foreach my $value ( @{$conf{'COMPONENTS'}} ) {
print $value,"\n";
}
我可能误解了这个问题,但是简单的呢
my %result;
while (<$fh>) {
my ($type, $components) = /(\w+)\s*=\s*"([^"]+)/;
push @{$result{$type}}, split ' ', $components; # all in one arrayref
#push @{$result{$type}}, [ split ' ', $components ]; # or as separate ones
}
这需要多行 COMPONENTS
and/or PACKAGES
(或任何其他前导关键字),并假设它们彼此无关,因此需要单独存储.
如果假设有误,请澄清。
上面的代码创建了哈希
(
COMPONENTS => [ 'A0000', 'B0000', 'G0000', ... ],
PACKAGES => [ ... ],
)
其中 [ ... ]
是一个数组引用,其中包含该关键字所有行的组件。
或者,如果使用代码中注释掉的行而不是上面的行,
(
COMPONENTS => [ ['A0000', 'B0000', 'G0000'], [ ... ], ... ],
PACKAGES => [ [ ... ], [ ... ], ... ],
)
其中 [ ... ]
是包含一行组件的数组引用,它们都存储在数组引用中,数组引用是键的值。因此,这里每一行的组件都单独存储,而不是像前一种情况那样全部存储在一个 arrayref 中。
如果 =
之前有任何其他单词,它们将存储为自己的键,并使用自己的数组引用和来自这些行的数据。
更新 澄清
由于引号可能存在也可能不存在,所以第一个引号可以选择 ?
/(\w+)\s*=\s*"?[^"]+)/;
而尾随的数据不需要处理,因为其余数据需要匹配
由于我的解决方案对您有效,因此我发布了一个答案:
(?:\G(?!^)\s+|(?:COMPONENTS|PACKAGES)=")\K[^\s"]+
模式匹配:
(?:\G(?!^)\s+|(?:COMPONENTS|PACKAGES)=")
- 两种选择之一:
\G(?!^)\s+
- 上一场比赛的结束(\G
匹配 line/string 的开始或上一场比赛的结束,所以 (?!^)
"subtracts" 字符串位置的开始)和 1+ 个空格
|
- 或
(?:COMPONENTS|PACKAGES)=
- COMPONENTS=
或 PACKAGES=
\K
- 匹配重置运算符(丢弃当前匹配的文本)
[^\s"]+
- 除空格和双引号外的 1 个或多个字符。
我想解析一个以某种方式包含这样内容的许可证文件。
COMPONENTS="A0000 B0000 G0000"
文件中必须有引号,以标记包中组件的开始和结束。我已经在 regex101.com 上测试了组合,但这并没有解决问题
正则表达式 ("?(?<components>\w+)*\s?"?)
适用于字符串 "A0000 B0000 G0000" 并且 return 是我需要的组件的各个值:A0000、B0000 等等
但是当我测试 (COMPONENTS|PACKAGES)=("?(?<components>\w+)\s?"?)
时,该块无法 return 这些值并且 return 只有第一个 A0000。
我需要帮助来提取所有这些单独的值。请帮忙。
我将在这里建议一种不同的方法。您的正则表达式模式变得越来越复杂,这不是一个好主意。
你的问题的核心是,要进行重复匹配,你还要匹配前缀文本,显然只能匹配一次。
但与其试图让您的正则表达式模式变得更加复杂,不如以不同的方式解决问题。
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %conf = do { local $/; <DATA> } =~ m/ (\w+) =" ([^"]+) " /xg;
print Dumper \%conf;
__DATA__
COMPONENTS="A0000 B0000 G0000"
PACKAGES="C0000 D0000 E0000"
这将创建一个数据结构。
$VAR1 = {
'COMPONENTS' => 'A0000 B0000 G0000',
'PACKAGES' => 'C0000 D0000 E0000'
};
如果你想要单独的值。
$_ = [split] for values %conf;
print Dumper \%conf;
这在功能上等同于此。
foreach my $key ( keys %conf ) {
#split the value on whitespace.
my @stuff = split ' ', $conf{$key};
#replace it with your array.
$conf{$key} = \@stuff;
}
产生
$VAR1 = {
'PACKAGES' => [
'C0000',
'D0000',
'E0000'
],
'COMPONENTS' => [
'A0000',
'B0000',
'G0000'
]
};
所以现在你可以写这个了。
foreach my $value ( @{$conf{'COMPONENTS'}} ) {
print $value,"\n";
}
我可能误解了这个问题,但是简单的呢
my %result;
while (<$fh>) {
my ($type, $components) = /(\w+)\s*=\s*"([^"]+)/;
push @{$result{$type}}, split ' ', $components; # all in one arrayref
#push @{$result{$type}}, [ split ' ', $components ]; # or as separate ones
}
这需要多行 COMPONENTS
and/or PACKAGES
(或任何其他前导关键字),并假设它们彼此无关,因此需要单独存储.
如果假设有误,请澄清。
上面的代码创建了哈希
( COMPONENTS => [ 'A0000', 'B0000', 'G0000', ... ], PACKAGES => [ ... ], )
其中 [ ... ]
是一个数组引用,其中包含该关键字所有行的组件。
或者,如果使用代码中注释掉的行而不是上面的行,
( COMPONENTS => [ ['A0000', 'B0000', 'G0000'], [ ... ], ... ], PACKAGES => [ [ ... ], [ ... ], ... ], )
其中 [ ... ]
是包含一行组件的数组引用,它们都存储在数组引用中,数组引用是键的值。因此,这里每一行的组件都单独存储,而不是像前一种情况那样全部存储在一个 arrayref 中。
如果 =
之前有任何其他单词,它们将存储为自己的键,并使用自己的数组引用和来自这些行的数据。
更新 澄清
由于引号可能存在也可能不存在,所以第一个引号可以选择 ?
/(\w+)\s*=\s*"?[^"]+)/;
而尾随的数据不需要处理,因为其余数据需要匹配
由于我的解决方案对您有效,因此我发布了一个答案:
(?:\G(?!^)\s+|(?:COMPONENTS|PACKAGES)=")\K[^\s"]+
模式匹配:
(?:\G(?!^)\s+|(?:COMPONENTS|PACKAGES)=")
- 两种选择之一:\G(?!^)\s+
- 上一场比赛的结束(\G
匹配 line/string 的开始或上一场比赛的结束,所以(?!^)
"subtracts" 字符串位置的开始)和 1+ 个空格|
- 或(?:COMPONENTS|PACKAGES)=
-COMPONENTS=
或PACKAGES=
\K
- 匹配重置运算符(丢弃当前匹配的文本)[^\s"]+
- 除空格和双引号外的 1 个或多个字符。