Regex 处理“问题”的最佳方法;正则表达式中的无花括号“;内部Perl正则表达式
我最近开始学习Perl来自动化一些无意识的数据任务。我在windows机器上工作,但更喜欢使用Cygwin。编写了一个Perl脚本,它在Cygwin中完成了我想要的一切,但是当我试图通过CMD在Windows上用草莓Perl运行它时,我得到了“在正则表达式中未加scaped的左括号在正则表达式中是非法的”错误 经过一番阅读,我猜我的Cygwin有一个早期版本的Perl,而草莓正在使用的Perl的现代版本不允许这样做。我熟悉正则表达式中的转义字符,但在使用前一个正则表达式匹配中的捕获组进行替换时会出现此错误Regex 处理“问题”的最佳方法;正则表达式中的无花括号“;内部Perl正则表达式,regex,perl,strawberry-perl,Regex,Perl,Strawberry Perl,我最近开始学习Perl来自动化一些无意识的数据任务。我在windows机器上工作,但更喜欢使用Cygwin。编写了一个Perl脚本,它在Cygwin中完成了我想要的一切,但是当我试图通过CMD在Windows上用草莓Perl运行它时,我得到了“在正则表达式中未加scaped的左括号在正则表达式中是非法的”错误 经过一番阅读,我猜我的Cygwin有一个早期版本的Perl,而草莓正在使用的Perl的现代版本不允许这样做。我熟悉正则表达式中的转义字符,但在使用前一个正则表达式匹配中的捕获组进行替换时会
open(my $fh, '<:encoding(UTF-8)', $file)
or die "Could not open file '$file' $!";
my $fileContents = do { local $/; <$fh> };
my $i = 0;
while ($fileContents =~ /(.*Part[^\}]*\})/) {
$defParts[$i] = $1;
$i = $i + 1;
$fileContents =~ s/$1//;
}
然后将这些匹配项存储在数组中。然后从$fileContents中清除匹配项,以避免重复
我确信有更好、更有效的方法来做这些事情,但我很惊讶,当使用一个捕获组时,它会抱怨没有替身的角色
我可以想象存储捕获组,手动转义大括号,然后将其用于替换,但是有没有一种更快或更有效的方法可以避免此错误而不重写整个块?(如果可能的话,我希望避免使用特殊的软件包,以便该脚本易于移植。)
我找到的所有与此错误相关的答案都是在特定的情况下得到的,在这些情况下,使用大括号编辑源代码更为直接或实用
谢谢大家! 至于逃跑的问题,这就是问题所在 打印内容(在v5.16上) 其中,
\Q
添加到正则表达式中。更好的是,如中所述,替换可以在而条件本身中完成
push @defParts, $1 while $fileContents =~ s/($pattern)//;
为了简洁起见,我使用了表单(后缀语法)
对于标量上下文中的,如在while(/($pattern)/g){..}
中,搜索从每次迭代中前一个匹配的位置继续,这是迭代字符串中模式的所有实例的常用方法。请仔细阅读标量上下文中使用的/g
,因为它的行为中有一些细节需要注意
然而,这在这里是很棘手的(尽管它可以工作),因为正则表达式下面的字符串发生了变化。如果不关心效率,您可以在列表上下文中使用/g
捕获所有匹配项,然后将其删除
my @all_matches = $fileContents =~ /$patt/g;
$fileContents =~ s/$patt//g;
虽然效率很低,但由于它会进行两次传递,因此更简单、更清晰
我希望Somedata
永远不可能包含}
,例如嵌套{…}
,对吗?如果是这样的话,您就有一个平衡分隔符的问题,这个分隔符要四舍五入得多。一种方法是使用核心模块。用例子搜索SO帖子。至于逃跑的问题,这就是为什么
打印内容(在v5.16上)
其中,\Q
添加到正则表达式中。更好的是,如中所述,替换可以在而条件本身中完成
push @defParts, $1 while $fileContents =~ s/($pattern)//;
为了简洁起见,我使用了表单(后缀语法)
对于标量上下文中的,如在while(/($pattern)/g){..}
中,搜索从每次迭代中前一个匹配的位置继续,这是迭代字符串中模式的所有实例的常用方法。请仔细阅读标量上下文中使用的/g
,因为它的行为中有一些细节需要注意
然而,这在这里是很棘手的(尽管它可以工作),因为正则表达式下面的字符串发生了变化。如果不关心效率,您可以在列表上下文中使用/g
捕获所有匹配项,然后将其删除
my @all_matches = $fileContents =~ /$patt/g;
$fileContents =~ s/$patt//g;
虽然效率很低,但由于它会进行两次传递,因此更简单、更清晰
我希望Somedata
永远不可能包含}
,例如嵌套{…}
,对吗?如果是这样的话,您就有一个平衡分隔符的问题,这个分隔符要四舍五入得多。一种方法是使用核心模块。用示例搜索SO帖子。我只想绕过整个问题,同时简化代码:
my $i = 0;
while ($fileContents =~ s/(.*Part[^\}]*\})//) {
$defParts[$i] = $1;
$i = $i + 1;
}
在这里,我们只是先做替换。如果成功,它仍然会设置$1
并返回true(就像普通的/…/
),因此以后不必再处理s/$1/
使用$1
(或任何变量)作为模式意味着您必须转义所有正则表达式元字符(例如*
,+
,{
,(
,
,等等),如果您希望它字面上匹配的话。您可以很容易地使用或内联(s/\Q$1/
),但这仍然是一个额外的步骤,因此容易出错
或者,您可以保留原始代码,而不使用s//
。我的意思是,您已经找到了匹配项。为什么要使用s//
再次搜索它
while ($fileContents =~ /(.*Part[^\}]*\})/) {
...
substr($fileContents, $-[0], $+[0] - $-[0], "");
}
我们已经知道匹配项在字符串中的位置。是最后一个正则表达式匹配项的开始位置和结束位置(因此,$+[0]-$-[0]
是匹配字符串的长度)。然后,我们可以使用将该块替换为”
但是让我们继续看s///
:
my $i = 0;
while ($fileContents =~ s/(.*Part[^\}]*\})//) {
$defParts[$i] = $1;
$i++;
}
$i=$i+1;
可以减少为$i++;
(“增量$i”)
我们需要$i
的唯一原因是将元素添加到@defParts
数组中。我们可以通过使用来实现这一点,因此无需维护额外的变量。这为我们节省了另一行
现在,我们可能不需要销毁$fileContents
。如果替换只为了这个循环的好处而存在(因此我不重新匹配已提取的内容),我们可以做得更好:
my @defParts;
while ($fileContents =~ /(.*Part[^\}]*\})/g) {
push @defParts, $1;
}
在标量上下文中使用/g
会将一个“当前位置”附加到$fileContents
,因此下一次匹配尝试将从上一次匹配停止的位置开始。这可能更有效,因为它不必保持重写
my $i = 0;
while ($fileContents =~ s/(.*Part[^\}]*\})//) {
$defParts[$i] = $1;
$i++;
}
my @defParts;
while ($fileContents =~ s/(.*Part[^\}]*\})//) {
push @defParts, $1;
}
my @defParts;
while ($fileContents =~ /(.*Part[^\}]*\})/g) {
push @defParts, $1;
}
my @defParts = $fileContents =~ /(.*Part[^\}]*\})/g;
my @defParts = $fileContents =~ /.*Part[^\}]*\}/g;