regex - perl 多行正则表达式来分隔段落中的注释
问题描述
下面的脚本有效,但它需要一个 kludge。“kludge”是指使脚本执行我想要的操作的一行代码 --- 但我不明白为什么该行是必要的。显然,我不明白多行正则表达式替换的确切含义,结尾/mg
是在做什么。
难道没有更优雅的方式来完成任务吗?
该脚本逐段读取文件。它将每个段落分成两个子集:$text
和$cmnt
。包括每行的$text
左边部分,即从第一列到第一列%
(如果存在),或者到行尾(如果不存在)。$cmnt
包括其余的。
动机:要阅读的文件是 LaTeX 标记,其中%
宣布评论的开始。如果我们通过 perl 脚本读取,我们可以将 的值更改$breaker
为相等。#
从 分离$text
后$cmnt
,可以跨行执行匹配,例如
print "match" if ($text =~ /WOLF\s*DOG/s);
请参阅标有“kludge”的行。%
没有那条线,在唱片的最后一条之后会发生一些有趣的事情。如果在记录的最后一个注释行之后有行$text
(材料未注释掉%
),则这些行包含在末尾$cmnt
和中$text
。
在下面的示例中,这意味着没有 kludge,在记录 2 中,“cat lion”既包含$text
在它所属的 中,也包含在 中$cmnt
。
(kludge 导致一个不必要%
的出现在每个 non-void 的末尾$cmnt
。这是因为 kludge-pasted-on%
宣布了一个最终的、虚构的空注释行。)
根据https://perldoc.perl.org/perlre.html#Modifiers,/m
正则表达式修饰符意味着
将匹配的字符串视为多行。也就是说,将“^”和“$”从匹配字符串第一行的开头和最后一行的结尾更改为匹配字符串中每一行的开头和结尾。
因此,我期待第二场比赛
s/^([^$breaker]*)($breaker.*?)$/$2/mg
从第一个开始%
,一直延伸到行尾,然后停在那里。所以即使没有kludge,它也不应该在记录2中包括“猫狮”?但显然它确实如此,所以我误读或遗漏了文档的某些部分。我怀疑它与/g
正则表达式修饰符有关?
#!/usr/bin/perl
use strict; use warnings;
my $count_record = 0;
my $breaker = '%';
$/ = ''; # one paragraph at a time
while(<>)
{
$count_record++;
my $text = $_;
my $cmnt;
s/[\n]*\z/$breaker/; # kludge
s/[\n]*\z/\n/; # guarantee each record ends with exactly one newline==LF==linefeed
if ($text =~ s/^([^$breaker]*)($breaker.*?)$/$1/mg) # non-greedy
{
$cmnt = $_;
die "cmnt does not match" unless ($cmnt =~ s/^([^$breaker]*)($breaker.*?)$/$2/mg); # non-greedy
}
else
{
$cmnt = '';
}
print "\nRECORD $count_record:\n";
print "******** text==";
print "\n|";
print $text;
print "|\n";
print "******** cmnt==|";
print $cmnt;
print "|\n";
}
运行它的示例文件:
dog wolf % flea
DOG WOLF % FLEA
DOG WOLLLLLLF % FLLLLLLEA
% what was that?
cat lion
no comments in this line
%The last paragraph of this file is nothing but a single-line comment.
解决方案
您还必须删除不包含以下注释的行$cmnt
:
use feature qw(say);
use strict;
use warnings;
my $count_record = 0;
my $breaker = '%';
$/ = ''; # one paragraph at a time
while(<>)
{
$count_record++;
my $text = $_;
my $cmnt;
s/[\n]*\z/\n/; # guarantee each record ends with exactly one newline==LF==linefeed
if ($text =~ s/^([^$breaker]*)($breaker.*?)$/$1/mg) # non-greedy
{
$cmnt = $_;
$cmnt =~ s/^[^$breaker]*?$//mg;
die "cmnt does not match" unless ($cmnt =~ s/^([^$breaker]*)($breaker.*?)$/$2/mg); # non-greedy
}
else
{
$cmnt = '';
}
print "\nRECORD $count_record:\n";
print "******** text==";
print "\n|";
print $text;
print "|\n";
print "******** cmnt==|";
print $cmnt;
print "|\n";
}
输出:
RECORD 1:
******** text==
|dog wolf
DOG WOLF
DOG WOLLLLLLF
|
******** cmnt==|% flea
% FLEA
% FLLLLLLEA
|
RECORD 2:
******** text==
|
cat lion
|
******** cmnt==|% what was that?
|
RECORD 3:
******** text==
|no comments in this line
|
******** cmnt==||
RECORD 4:
******** text==
||
******** cmnt==|%The last paragraph of this file is nothing but a single-line comment.
|
推荐阅读
- von-neumann - 为什么IAS的指令内存有2段
- c++ - C++:将链表中的数据存储在另一个链表中
- swift4 - swift项目中的常用字符串
- qt - Qt - 有选择地允许点击进入较低的应用程序窗口
- javascript - 在 Discord.js 中检测消息中的链接
- java - jps的名字怎么设置?
- javascript - 即使检测到更新,React HMR 也不会呈现
- jquery - 如何使用 jquery onclick 事件在另一个函数中调用一个函数
- angular - 复制具有复杂对象的单元格特征
- javascript - React Native TypeError store.getState 不是函数