2

我试图找到一种方法来用管道(||)替换空格和双引号,同时保持双引号内的空格不受影响。

例如,它会将类似“word word word”之类的东西变成'word||word word||word',而另一个类似'word word word'的东西变成'word||word||word'。

现在我有这个工作了:

[%- MACRO typestrip(value) PERL -%]
my $htmlVal = $stash->get('value');
$htmlVal =~ s/"/||/g;
print $htmlVal
[%- END -%]

它可以很好地处理用管道替换双引号。

我不知道这应该是多么简单或复杂,或者是否可以完成,因为我没有实际的编程背景,虽然我使用过一些 Perl,但以前从来没有这种情况,所以如果我我很抱歉我没有很好地解释这一点。

4

4 回答 4

9

我认为使用核心模块在未引用的空格上拆分可能更容易Text::ParseWords,然后用管道重新加入“单词”。

#!/usr/bin/env perl

use warnings;
use strict;

use Text::ParseWords;

while (my $line = <DATA>) {
  print space2pipes($line); 
  print "\n";
}

sub space2pipes {
  my $line = shift;
  chomp $line;
  my @words = parse_line( qr/\s+/, 0, $line );
  return join '||', @words;
}

__DATA__
word "word word" word
word word word

将其放入您的模板引擎中作为练习留给读者:-)

于 2012-06-27T18:31:54.477 回答
4

这与Perl FAQ 的第 4 节中的一个常见问题有关

除非在 [character] 内,否则如何拆分 [character] 分隔的字符串?

有几个模块可以处理这种解析—— Text::BalancedText::CSVText::CSV_XSText::ParseWords等等。

以尝试将逗号分隔的字符串拆分为不同字段的示例为例。您不能使用split(/,/),因为如果逗号在引号内,您不应该拆分。例如,采用这样的数据线:

SAR001,"","Cimetrix, Inc","Bob Smith","CAM",N,8,1,0,7,"错误,核心转储"

由于引号的限制,这是一个相当复杂的问题。值得庆幸的是,我们有Mastering Regular Expressions的作者 Jeffrey Friedl为我们处理这些问题。他建议(假设您的字符串包含在 中$text):

my @new = ();
push(@new, $+) while $text =~ m{
           # groups the phrase inside the quotes
             "([^\"\\]*(?:\\.[^\"\\]*)*)",?
           | ([^,]+),?
           | ,
     }gx;
push(@new, undef) if substr($text,-1,1) eq ',';

如果您想在引号分隔的字段中表示引号,请使用反斜杠(例如"like \"this\"")转义它们。

或者,Text::ParseWords模块(标准 Perl 发行版的一部分)让您说:

use Text::ParseWords;
@new = quotewords(",", 0, $text);

但是,对于解析或生成 CSV,强烈建议使用Text::CSV而不是自己实现它;您只需使用已经在生产环境中尝试和测试多年的代码,就可以避免以后出现的奇怪错误。

使该技术适应您的情况

my $htmlVal = 'word "word word" word';

my @chunks;
push @chunks, $+ while $htmlVal =~ m{
    "([^\"\\]*(?:\\.[^\"\\]*)*)"
  | (\S+)
}gx;

$htmlVal = join "||", @chunks;
print $htmlVal, "\n";

输出:

单词||单词单词||单词

回顾过去,事实证明这是 Randal 规则的一个应用,正如Mark Dominus在Regular Expression Mastery中所说的那样:

兰德尔法则

使用捕获或m//g当您知道要保留什么时。

split当您知道要丢弃什么时使用。

兰德尔·施瓦茨

在您的情况下,您知道要保留什么,因此请使用m//g引号内的文本或以空格分隔的其他文本。

于 2012-06-27T19:23:43.550 回答
1

虽然Joel 的回答很好,但可以通过专门shellwords用于标记行来简化一些事情:

#!/usr/bin/env perl

use strict; use warnings;
use Text::ParseWords qw( shellwords );

my @strings = (
    'word "word word" word',
    'word "word word" "word word"',
);

@strings = map join('||', shellwords($_)), @strings;

use YAML;
print Dump \@strings;

这不是比一堆 regex-gobbledygook 更具可读性吗?

于 2012-06-28T13:12:11.943 回答
0

如果只有正则表达式适用,似乎可能并且可能有用:

 $htmlVal =~ s/(?:"([^"]+)"(\s*))|(?:(\S+)(\s*))/($1||$3).($2||$4?'||':'')/eg;

(仔细反省后可能会被美化一点。)

输入:

 my $htmlVal ='word "word word" word';

输出:

 word||word word||word


在这种情况下失败后,原始代码已被修改:

 my $htmlVal ='word "word word" "word word"';

现在也可以工作了:

 word||word word||word word


解释:

 $htmlVal =~ s/
               (?: " ([^"]+) " (\s*)) # search "abc abc" ($1), End ($2)
               |                      # OR
               (?: (\S+) (\s*))       # abcd ($3), End ($4)
              /
               ($1||$3) . ($2||$4 ? '||' : '') # decide on $1/$2 or $3/$4 
              /exg;

问候

rbo

于 2012-06-27T19:03:06.803 回答