除了空格是双引号外,Perl如何在空白上拆分一行?

时间:2009-10-14 12:51:20

标签: regex perl split

我有以下字符串:

StartProgram    1   ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout    1   30

我需要一个正则表达式来分割这一行,但忽略Perl中双引号中的空格。

以下是我尝试但不起作用。

(".*?"|\S+)

4 个答案:

答案 0 :(得分:9)

曾几何时我也试图重新发明轮子,并自己解决这个问题。

现在我只使用Text::ParseWords 让它为我做好工作。

答案 1 :(得分:4)

更新:看起来这些字段实际上是制表符分隔的,而不是空格。如果有保证,请在\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::BalancedText::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;
}

答案 2 :(得分:0)

 my $x = 'StartProgram 1    ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout    1   30';

 my @parts = $x =~ /("".*?""|[^\s]+?(?>\s|$))/g;

答案 3 :(得分:0)

my $str = 'StartProgram    1    ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout    1       30';

print "str:$str\n";

@A =  $str =~ /(".+"|\S+)/g;

foreach my $l (@A) {
        print "<$l>\n";
}

这让我:

$ ./test.pl 
str:StartProgram    1   ""C:\Program Files\ABC\ABC XYZ"" CleanProgramTimeout    130
<StartProgram>
<1>
<""C:\Program Files\ABC\ABC XYZ"">
<CleanProgramTimeout>
<1>
<30>