更新:看起来这些字段实际上是制表符分隔的,而不是空格。如果可以保证,请在 \t 上拆分。
首先,让我们看看为什么(".*?"|\S+)“不起作用”。具体来说,看".*?" 这意味着用双引号括起来的零个或多个字符。好吧,给你带来问题的领域是""C:\Program Files\ABC\ABC XYZ""。请注意,该字段开头和结尾的每个"" 都将匹配".*?",因为"" 包含用双引号括起来的零个字符。
最好尽可能具体地匹配而不是拆分。因此,如果您有一个带有指令和固定格式的配置文件,请形成一个尽可能接近您尝试匹配的格式的正则表达式匹配。
如果您不想要引号,请将它们移到捕获括号之外。
#!/usr/bin/perl
use strict;
use warnings;
my $s = q{StartProgram 1 ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout 1 30};
my @parts = $s =~ m{\A(\w+) ([0-9]) (""[^"]+"") (\w+) ([0-9]) ([0-9]{2})};
use Data::Dumper;
print Dumper \@parts;
输出:
$VAR1 = [
'StartProgram',
'1',
'""C:\\Program Files\\ABC\\ABC XYZ""',
'CleanProgramTimeout',
'1',
'30'
];
在这种情况下,这里有一个更复杂的脚本:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my @strings = split /\n/, <<'EO_TEXT';
StartProgram 1 ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout 1 30
StartProgram 1 c:\opt\perl CleanProgramTimeout 1 30
EO_TEXT
my $re = qr{
(?<directive>StartProgram)\s+
(?<instance>[0-9][0-9]?)\s+
(?<path>"".+?""|\S+)\s+
(?<timeout_directive>CleanProgramTimeout)\s+
(?<timeout_instance>[0-9][0-9]?)\s+(?<timeout_seconds>[0-9]{2})
}x;
for (@strings) {
if ( $_ =~ $re ) {
print Dumper \%+;
}
}
输出:
$VAR1 = {
'timeout_directive' => 'CleanProgramTimeout',
'timeout_seconds' => '30',
'path' => '""C:\\Program Files\\ABC\\ABC XYZ""',
'directive' => 'StartProgram',
'timeout_instance' => '1',
'instance' => '1'
};
$VAR1 = {
'timeout_directive' => 'CleanProgramTimeout',
'timeout_seconds' => '30',
'path' => 'c:\\opt\\perl',
'directive' => 'StartProgram',
'timeout_instance' => '1',
'instance' => '1'
};
更新:我无法让 Text::Balanced 或 Text::ParseWords 正确解析此内容。我怀疑问题出在重复的引号中,这些引号描述了不应拆分的子字符串。以下代码是我通过使用拆分然后选择性地重新收集部分字符串来解决一般问题的最佳(不是很好)尝试。
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $s = q{StartProgram 1 ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout 1 30};
my $t = q{StartProgram 1 c:\opt\perl CleanProgramTimeout 1 30};
print Dumper parse_line($s);
print Dumper parse_line($t);
sub parse_line {
my ($line) = @_;
my @parts = split /(\s+)/, $line;
my @real_parts;
for (my $i = 0; $i < @parts; $i += 1) {
unless ( $parts[$i] =~ /^""/ ) {
push @real_parts, $parts[$i] if $parts[$i] =~ /\S/;
next;
}
my $part;
do {
$part .= $parts[$i++];
} until ($part =~ /""$/);
push @real_parts, $part;
}
return \@real_parts;
}