我如何在perl中使用单个正则表达式将一行划分为代码和注释?



我想通读一个文本文件,并将每行划分为以下三个变量。每个变量都必须定义,尽管它可能等于空字符串。

  • $a1code:所有字符,不包括第一个非转义的百分号。如果没有未转义的百分号,这就是整行。正如我们在下面的例子中看到的,这也可以是一行中的空字符串,其中以下两个变量是非空的。
  • $a2boundary:第一个非转义的百分号(如果有)。
  • $a3cmnt:第一个非转义百分号(如果有)之后的任何字符。

下面的脚本完成了这一点,但需要几行代码、两个哈希和一个复合正则表达式,即由|组合的2个正则表达式。复合句似乎是必要的,因为第一个子句

(?<a1code>.*?)(?<a2boundary>(?<!\)%)(?<a3cmnt>.*)

不匹配没有注释的纯代码行。是否有更优雅的方法,使用单个正则表达式和更少的步骤?特别是,是否有一种方法可以免除%match哈希,并且以某种方式在一个步骤中用所有三个变量填充%+哈希?

#!/usr/bin/env perl
use strict; use warnings;
print join('', 'perl ', $^V, "n",);
use Data::Dumper qw(Dumper); $Data::Dumper::Sortkeys = 1;
my $count=0;
while(<DATA>)
{
$count++;
print "$countt";
chomp;
my %match=(
a2boundary=>'',
a3cmnt=>'',
);
print "|$_|n";
if($_=~/^(?<a1code>.*?)(?<a2boundary>(?<!\)%)(?<a3cmnt>.*)|(?<a1code>.*)/)
{
print "from regex:n";
print Dumper %+;
%match=(%match,%+,);
}
else
{
die "no match? coding error, should never get here";
}
if(scalar keys %+ != scalar keys %match)
{
print "from multiple lines of code:n";
print Dumper %match;
}
print "------------------------------------------n";
}
__DATA__
This is 100% text and below you find an empty line.
abba 5% %comment 9% %Borgia
%all comment
%

结果:

perl v5.34.0
1   |This is 100% text and below you find an empty line.   |
from regex:
$VAR1 = {
'a1code' => 'This is 100\% text and below you find an empty line.   '
};
from multiple lines of code:
$VAR1 = {
'a1code' => 'This is 100\% text and below you find an empty line.   ',
'a2boundary' => '',
'a3cmnt' => ''
};
------------------------------------------
2   ||
from regex:
$VAR1 = {
'a1code' => ''
};
from multiple lines of code:
$VAR1 = {
'a1code' => '',
'a2boundary' => '',
'a3cmnt' => ''
};
------------------------------------------
3   |abba 5% %comment 9% %Borgia|
from regex:
$VAR1 = {
'a1code' => 'abba 5\% ',
'a2boundary' => '%',
'a3cmnt' => 'comment 9\% %Borgia'
};
------------------------------------------
4   |%all comment|
from regex:
$VAR1 = {
'a1code' => '',
'a2boundary' => '%',
'a3cmnt' => 'all comment'
};
------------------------------------------
5   |%|
from regex:
$VAR1 = {
'a1code' => '',
'a2boundary' => '%',
'a3cmnt' => ''
};
------------------------------------------

您可以使用以下命令:

my ($a1code, $a2boundary, $a3cmnt) =
/
^
(  (?: [^\%]+ | \. )* )
(?: (%) (.*) )?
z
/sx;

它不认为%abc\%def中转义了,因为前面的已经转义了。

它不需要回溯,并且总是匹配。

$a1code总是一个字符串。它可以是零字符长(当输入是空字符串并且%是第一个字符时),也可以是整个输入字符串(当没有未转义的%时)。

然而,$a2boundary$a3cmnt只有在存在未转义的%时才会被定义。也就是说,$a2boundary等价于defined($a3cmnt) ? '%' : undef

:[^\%]+匹配除%以外的非转义字符。\.匹配转义字符。因此,(?: [^\%]+ | \. )*为我们提供前缀,如果没有未转义的%,则为整个字符串。

this\%string这样的情况下,百分号前的反斜杠本身是转义的呢?

考虑这样的事情,它不是尝试使用正则表达式将字符串分成三组,而是使用一组来查找应该将字符串分割的位置,并使用substr来进行实际的分割:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
sub splitter {
my $line = shift;
if ($line =~ /
# Match either
(?<!\)% # A % not preceded by a backslash    
| # or                    
(?<=[^\])(?:\\)+K% # Any even number of backslashes followed by a %
/x) {
return (substr($line, 0, $-[0]), '%', substr($line, $+[0]));        
} else {
return ($line, '', '');
}
}
while (<DATA>) {
chomp;
# Assign to an array instead of individual scalars for demonstration purposes
my @vals = splitter $_;
print Dumper(@vals);
}   
__DATA__
This is 100% text and below you find an empty line.
abba 5% %comment 9% %Borgia
%all comment
%
a tricky\%test % case
another \%one % to mess with you

最新更新