我想构造一个将检查“path”和“foo”参数(非负整数)的正则表达式。 “foo”是可选的。它应该:
MATCH
path?foo=67 # path found, foo = 67
path?foo=67&bar=hello # path found, foo = 67
path?bar=bye&foo=1&baz=12 # path found, foo = 1
path?bar=123 # path found, foo = ''
path # path found, foo = ''
不匹配
path?foo=37signals # foo is not integer
path?foo=-8 # foo cannot be negative
something?foo=1 # path not found
此外,我希望获得foo
的价值,而不执行其他匹配。
实现这一目标的最简单的正则表达式是什么?
答案 0 :(得分:18)
螺丝 您的辛勤工作,我只想要答案! Okay, here you go ......
var regex = /^path(?:(?=\?)(?:[?&]foo=(\d*)(?=[&#]|$)|(?![?&]foo=)[^#])+)?(?=#|$)/,
URIs = [
'path', // valid!
'pathbreak', // invalid path
'path?foo=123', // valid!
'path?foo=-123', // negative
'invalid?foo=1', // invalid path
'path?foo=123&bar=abc', // valid!
'path?bar=abc&foo=123', // valid!
'path?bar=foo', // valid!
'path?foo', // valid!
'path#anchor', // valid!
'path#foo=bar', // valid!
'path?foo=123#bar', // valid!
'path?foo=123abc', // not an integer
];
for(var i = 0; i < URIs.length; i++) {
var URI = URIs[i],
match = regex.exec(URI);
if(match) {
var foo = match[1] ? match[1] : 'null';
console.log(URI + ' matched, foo = ' + foo);
} else {
console.log(URI + ' is invalid...');
}
}
&#13;
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
&#13;
您的赏金请求要求提供可信和/或官方来源&#34;,因此我引用RFC on query strings。
查询组件包含非分层数据,与路径组件(第3.3节)中的数据一起用于标识URI方案和命名权限(如果有)范围内的资源。查询组件由第一个问号(&#34;?&#34;)字符表示,并以数字符号(&#34;#&#34;)字符或URI末尾终止。
这似乎有点模糊:查询字符串以第一个?
开头,并以#
(锚的开始)或URI的结尾(或我们的字符串/行)结束案件)。他们继续提到大多数数据集都在key=value
对中,这就像你期望解析的那样(所以我们假设是案件)。
但是,由于查询组件通常用于携带&#34; key = value&#34;形式的识别信息。对和一个经常使用的值是对另一个URI的引用,有时可以更好地避免对这些字符进行百分比编码。
考虑到这一切,让我们假设一些关于你的URI:
?
(查询字符串),#
(锚点)或字符串的结尾。< / LI>
key=value
个字符附加的&
对的列表。保持这种心态:
null
,前面会有?
或&
,并且不能包含=
,&
或{{1} }。#
,不能包含key=
或&
。#
个字符之后的任何内容都是锚点。让我们从mapping out our basic URI structure开始。你有一个路径,它是从字符串开始直到#
,?
或字符串结尾的字符。您有一个可选的查询字符串,它从#
开始,一直到?
或字符串的结尾。你有一个可选的锚点,从#
开始直到字符串的结尾。
#
在深入查询字符串之前,先让我们do some clean up。通过替换第一个捕获组,您可以轻松地要求路径等于某个值。无论你用(^
([^?#]+)
(?:
\?
([^#]+)
)?
(?:
#
(.*)
)?
$
)替换它,都必须跟随一个可选的查询字符串,一个可选的锚点和字符串的结尾(不多也不少)。由于您不需要解析锚点,因此可以通过在path
或字符串末尾(查询参数的末尾)结束匹配来替换捕获组。
#
好的,我一直在做很多设置而不用担心你的具体例子。下一个示例将匹配特定路径(^path
(?:
\?
([^#\+)
)?
(?=#|$)
),并可选地匹配查询字符串,同时捕获path
参数的值。这意味着您可以在此处停止并检查有效匹配。如果匹配有效,则第一个捕获组必须为foo
或非负整数。但这不是你的问题,是吗? This got a lot more complicated,所以我要解释内联的表达式:
null
这里有一些关键的要点:
^ (?# match beginning of the string)
path (?# match path literally)
(?: (?# begin optional non-capturing group)
(?=\?) (?# lookahead for a literal ?)
(?: (?# begin optional non-capturing group)
[?&] (?# keys are preceded by ? or &)
foo (?# match key literally)
(?: (?# begin optional non-capturing group)
= (?# values are preceded by =)
([^&#]*) (?# values are 0+ length and do not contain & or #)
) (?# end optional non-capturing group)
| (?# OR)
[^#] (?# query strings are non-# characters)
)+ (?# end repeating non-capturing group)
)? (?# end optional non-capturing group)
(?=#|$) (?# lookahead for a literal # or end of the string)
之前查看?
或&
,这意味着您实际上必须匹配一个这些字符的含义,意味着查询字符串的开头(查找foo
)必须是预测,以便您实际上不匹配?
。这也意味着您的查询字符串将始终至少包含一个字符(?
),因此您希望重复查询字符串?
1次。[^#]
,否则它会捕获可选值并继续重复。foo
)将覆盖初始捕获的值。意味着你不会100%能够依赖上述解决方案。好的..现在我已经抓住了path?foo=123&foo=bar
值time to kill the match on a values that are not positive integers。
foo
让我们仔细研究一下进入该表达式的juju:
^ (?# match beginning of the string)
path (?# match path literally)
(?: (?# begin optional non-capturing group)
(?=\?) (?# lookahead for a literal ?)
(?: (?# begin optional non-capturing group)
[?&] (?# keys are preceeded by ? or &)
foo (?# match key literally)
= (?# values are preceeded by =)
(\d*) (?# value must be a non-negative integer)
(?= (?# begin lookahead)
[&#] (?# literally match & or #)
| (?# OR)
$ (?# match end of the string)
) (?# end lookahead)
| (?# OR)
(?! (?# begin negative lookahead)
[?&] (?# literally match ? or &)
foo= (?# literally match foo=)
) (?# end negative lookahead)
[^#] (?# query strings are non-# characters)
)+ (?# end repeating non-capturing group)
)? (?# end optional non-capturing group)
(?=#|$) (?# lookahead for a literal # or end of the string)
后,我们使用前瞻来确保它后跟foo=\d*
,&
或字符串的结尾(查询字符串值的结尾) #
还有更多内容,那么正则表达式会被交流发电机踢回foo=\d*
之前[^#]
的{{1}}匹配。这不好,因为它会继续匹配!因此,在查找通用查询字符串([?&]
)之前,必须确保没有查看foo
(必须由第一次更改处理)。这就是负向前瞻[^#]
派上用场的地方。foo
键,因为它们都必须等于非负整数。这样,(?![?&]foo=)
也可以是可选的(或等于foo
)。 免责声明:大多数Regex101演示使用PHP进行更好的语法突出显示,并在负字符类中包含foo
,因为有多行示例。
答案 1 :(得分:5)
好问题!起初看起来相当简单......但是有一些很多的陷阱。建议检查任何声明的解决方案将处理以下事项:
其他比赛测试
path? # path found, foo = ''
path#foo # path found, foo = ''
path#bar # path found, foo = ''
path?foo= # path found, foo = ''
path?bar=1&foo= # path found, foo = ''
path?foo=&bar=1 # path found, foo = ''
path?foo=1#bar # path found, foo = 1
path?foo=1&foo=2 # path found, foo = 2
path?foofoo=1 # path found, foo = ''
path?bar=123&foofoo=1 # path found, foo = ''
其他不匹配的测试
pathbar? # path not found
pathbar?foo=1 # path not found
pathbar?bar=123&foo=1 # path not found
path?foo=a&foofoo=1 # not an integer
path?foofoo=1&foo=a # not an integer
我能提出的最简单的正则表达式适用于所有这些其他情况:
path(?=(\?|$|#))(\?(.+&)?foo=(\d*)(&|#|$)|((?![?&]foo=).)*$)
但是,会建议将?:
添加到未使用的捕获组,以便忽略它们,并且您可以轻松地从第1组获取foo
值 - 请参阅Debuggex Demo
path(?=(?:\?|$|#))(?:\?(?:.+&)?foo=(\d*)(?:&|#|$)|(?:(?![?&]foo=).)*$)
答案 2 :(得分:4)
^path\b(?!.*[?&]foo=(?!\d+(?=&|#|$)))(?:.*[?&]foo=(\d+)(?=&|#|$))?
基本上我把它分成三部分
^path\b # starts with path
(?!.*[?&]foo=(?!\d+(?=&|#|$))) # not followed by foo with an invalid value
(?:.*[?&]foo=(\d+)(?=&|#|$))? # possibly followed by foo with a valid value
在此处查看验证http://regexr.com/39i7g
注意事项:
将匹配path#bar=1&foo=27
与path?foo=
答案 3 :(得分:2)
答案 4 :(得分:2)
您可以尝试以下正则表达式:
path(?:.*?foo=(\d+)\b|()(?!.*foo))
path
后有两种可能的匹配:
.*?foo=(\d+)\b
,即foo
后跟数字。
OR
如果前方没有()(?!.*foo)
,则 foo
为空字符串。
如果您不希望正则表达式解释\b
周围的其他单词(例如,另一个名为barfoobar
的参数),请添加一些单词边界(foo
)。
path(?:.*?\bfoo=(\d+)\b|()(?!.*\bfoo\b))
答案 5 :(得分:1)
您可以检查是否存在3个 rd 匹配的组。它不存在,foo
值为null
;否则,就是小组本身:
/^(path)(?:$|\?(?:(?=.*\b(foo=)(\d+)\b.*$)|(?!foo=).*?))/gm
关于regex101的示例:http://regex101.com/r/oP6lU7/1
答案 6 :(得分:1)
处理javascript引擎以使正则表达式除了与PCRE相比所有的缺点之外,不知何故是令人愉快的!
我做了这个RegEx,简单易懂:
^(?=path\?).*foo=(\d*)(?:&|$)|path$
<强> 说明 强>
^(?=path\?) # A positive lookahead to ensure we have "path" at the very begining
.*foo=(\d*)(?:&|$) # Looking for a string includes foo=(zero or more digits) following a "&" character or end of string
| # OR
path$ # Just "path" itself
Runnable片段:
var re = /^(?=path\?).*foo=(\d*)(?:&|$)|path$/gm;
var str = 'path?foo=67\npath?foo=67&bar=hello\npath?bar=bye&foo=1&baz=12\npath\npathtest\npath?foo=37signals\npath?foo=-8\nsomething?foo=1';
var m, n = [];
while ((m = re.exec(str)) != null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
n.push(m[0]);
}
alert( JSON.stringify(n) );
&#13;
或 Live demo 了解更多详情
答案 7 :(得分:1)
path(?:\?(?:[^&]*&)*foo=([0-9]+)(?:[&#]|$))?
这和大多数一样短,读得更直接,因为在字符串中出现一次的内容在RE中出现一次。
我们匹配:
不幸的是,当foo参数被省略时,它将foo与None匹配,而不是'',而在Python(我选择的语言)中被认为更合适。如果你愿意,你可以抱怨,或者只是或 '。
答案 8 :(得分:0)
根据OP的数据,这里是我的尝试模式
^(path)\b(?:[^f]+|f(?!oo=))(?!\bfoo=(?!\d+\b))(?:\bfoo=(\d+)\b)?
如果找到路径:子模式#1将包含&#34;路径&#34;
如果foo有效:子模式#2将包含&#34; foo值,如果有的话#34;
^(path)\b
&#34;路径&#34; (?:[^f]+|f(?!oo=))
后面跟着&#34; foo =&#34; (?!\bfoo=(?!\d+\b))
if&#34; foo =&#34;发现它除了\d+\b
(?:\bfoo=(\d+)\b)?
如果有效&#34; foo =&#34;被发现,捕获&#34; foo&#34;值答案 9 :(得分:-1)
t = 'path?foo=67&bar=hello';
console.log(t.match(/\b(foo|path)\=\d+\b/))
正则表达式/\b(foo|path)\=\d+\b/