问题描述
|
我正在尝试编写一个正则表达式,它将解析调用脚本的语法并捕获脚本名称。
所有这些都是调用的有效语法
# normal way
run cred=username/password script.bi
# single quoted username password,also separated in a different way
run cred=\'username password\' script.bi
# username/password is optional
run script.bi
# script extension is optional
run script
# the call might be broken into multiple lines using \\
# THIS ONE SHOULD NOT MATCH
run cred=username/password \\
script.bi
这就是我到目前为止
my $r = q{run +(?:cred=(?:[^\\s\\\']*|\\\'.*\\\') +)?([^\\s\\\\]+)};
捕获$1
中的值。
但是我得到了
Unmatched [ before HERE mark in regex m/run +(?:cred=(?:[^\\s\\\']*|\\\'.*\\\') +)?([ << HERE ^\\s\\]+)/
解决方法
\\\\
被视为\\
,因此在正则表达式中它变成\\]
,因此逃脱了]
,因此没有匹配的[
换成run +(?:cred=(?:[^\\s\\\']*|\\\'.*\\\') +)?([^\\s\\\\\\\\]+)
(注意\\\\\\\\
)并尝试。
另外,根据注释,您必须对正则表达式使用qr
,而不仅仅是q
。
(我只是看过错误,而不是正则表达式对您问题的有效性/效率)
,指定正则表达式的问题的实质是一个字节的差:q
与qr
。您正在编写正则表达式,所以称它为正则表达式。将模式视为字符串意味着您必须在正则表达式转义规则的基础上处理字符串引用规则。
至于您的正则表达式匹配的语言,请添加锚点以强制模式匹配整行。正则表达式引擎经过严格确定,将继续工作直到找到匹配项。没有锚,很高兴找到一个子字符串。
有时这会给您令人惊讶的结果。您是否曾经与一个脾气暴躁的孩子(或一个幼稚的成年人)打交道,对您的言论进行狭义的,极其字面的解释?正则表达式引擎就是这种方式,但它正在尝试提供帮助。
在最后一个示例中,它匹配,因为
您用?
量词说过cred=...
子模式可以匹配零次,因此正则表达式引擎跳过了它。
您说脚本名称是下面的子字符串,它由一个或多个非空格,非反斜杠字符组成,因此正则表达式引擎看到了“ 17”,它们都不是空格或反斜杠字符,并且匹配。正则表达式贪婪:它们考虑在它们前面的正确位置,而不考虑给定的子字符串是否“应该”与另一个子模式匹配。
最后一个示例符合要求,尽管不是您想要的那样。正则表达式的重要一课是可以匹配零倍的任何量词(例如?
或*
)总是成功的!
如果没有$
锚点,则问题中的模式将使结尾的反斜杠不匹配,您可以对$runpat
稍作修改即可看到它。
qr{run +(?:cred=(?:[^\\s\']*|\\\'.*\\\') +)?([^\\s\\\\]+)(.*)}; # \' SO hiliter hack
请注意最后的ѭ23来抓住可能剩下的任何非换行符。将循环更改为
while (<DATA>) {
next unless /$runpat/;
print \"line $.: \\$1=[$1]; \\$2=[$2]\\n\";
}
给出第15行的以下输出。
第15行:$ 1 = [cred = username / password]; $ 2 = [\\]
作为一个完整的程序,
#! /usr/bin/env perl
use strict;
use warnings;
# The goofy comment on the next line is a hack to
# help Stack Overflow\'s syntax highlighter recover
# from its confusion after seeing the quotes. It\'s
# for presentation only: you won\'t need it in your
# real code.
my $runpat = qr{^\\s*run +(?:cred=(?:[^\\s\']*|\\\'.*\\\') +)?([^\\s\\\\]+)$}; # \'
while (<DATA>) {
next unless /$runpat/;
print \"line $.: \\$1=[$1]\\n\";
}
__DATA__
# normal way
run cred=username/password script.bi
# single quoted username password,also separated in a different way
run cred=\'username password\' script.bi
# username/password is optional
run script.bi
# script extension is optional
run script
# the call might be broken into multiple lines using \\
# THIS ONE SHOULD NOT MATCH
run cred=username/password \\
script.bi
输出:
第2行:$ 1 = [script.bi]
第5行:$ 1 = [script.bi]
第8行:$ 1 = [script.bi]
第11行:$ 1 = [脚本]
简明并不总是对正则表达式有所帮助。考虑以下替代但等效的规范:
my $runpat = qr{
^ \\s*
(?:
run \\s+ cred=(?:[^\\s\']*|\'.*?\') \\s+ (?<script> [^\\s\\\\]+) # \' hiliter
| run \\s+ (?!cred=) (?<script> [^\\s\\\\]+)
)
\\s* $
}x;
是的,它需要更多的编写空间,但是关于可接受的替代方案更加清晰。您的循环几乎相同
while (<DATA>) {
next unless /$runpat/;
print \"line $.: script=[$+{script}]\\n\";
}
甚至使可怜的读者不必数括号。
要使用命名的捕获缓冲区,例如(?<script>...)
,请确保添加
use 5.10.0;
到程序顶部,以提供最低要求的perl版本的可执行文档。
,脚本有时会有参数吗?如果不是,为什么不这样做:
/^run(?:\\s.*\\s|\\s)(\\S+)\\s*$/
我猜这在行继续位上不起作用。
/^run(?:\\s+cred=(?:[^\'\\s]*|\'[^\']*\')\\s+|\\s+)([^\\\\\\s]+)\\s*$/
测试程序:
#!/usr/bin/perl
$foo=\"# normal way
run cred=username/password script.bi
# single quoted username password,also separated in a different way
run cred=\'username password\' script.bi
# username/password is optional
run script.bi
# script extension is optional
run script
# the call might be broken into multiple lines using \\
# THIS ONE SHOULD NOT MATCH
run cred=username/password \\\\
script.bi
\";
foreach my $line (split(/\\n/,$foo))
{
print \"Looking >$line<\\n\";
print \"Match >$1<\\n\"
if ($line =~ /^run(?:\\s+cred=(?:[^\'\\s]*|\'[^\']*\')\\s+|\\s+)([^\\\\\\s]+)\\s*$/);
}
输出示例:
Looking ># normal way<
Looking >run cred=username/password script.bi<
Match >script.bi<
Looking ><
Looking ># single quoted username password,also separated in a different way<
Looking >run cred=\'username password\' script.bi<
Match >script.bi<
Looking ><
Looking ># username/password is optional<
Looking >run script.bi<
Match >script.bi<
Looking ><
Looking ># script extension is optional<
Looking >run script<
Match >script<
Looking ><
Looking ># the call might be broken into multiple lines using <
Looking ># THIS ONE SHOULD NOT MATCH<
Looking >run cred=username/password \\<
Looking >script.bi<