我想构造一个将检查"路径"和"foo"参数(非负整数)的正则表达式."foo"是可选的.这应该:
比赛
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 = ''
Run Code Online (Sandbox Code Playgroud)
不符合
path?foo=37signals # foo is not integer
path?foo=-8 # foo cannot be negative
something?foo=1 # path not found
Run Code Online (Sandbox Code Playgroud)
此外,我想得到的价值foo,而不是额外的匹配.
实现这一目标的最简单的正则表达式是什么?
Sam*_*Sam 19
拧紧 你的辛勤工作,我只想要答案! 好的,你去 ......
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...');
}
}Run Code Online (Sandbox Code Playgroud)
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>Run Code Online (Sandbox Code Playgroud)
您的赏金请求要求"可信和/或官方来源",因此我将在查询字符串上引用RFC.
查询组件包含非分层数据,与路径组件(第3.3节)中的数据一起用于标识URI方案和命名权限(如果有)范围内的资源.查询组件由第一个问号("?")字符表示,并以数字符号("#")字符或URI的末尾结束.
这看起来非常含糊:查询字符串以第一个开头,?并以#(锚的开始)或URI的结尾(或我们的情况下的字符串/行)结束.他们接着提到,大多数数据集是key=value对,这就是好像你有什么期望是解析(所以让我们假设是这种情况).
但是,由于查询组件通常用于携带"key = value"对形式的标识信息,而一个常用值是对另一个URI的引用,因此有时可以更好地避免对这些字符进行百分比编码.
考虑到所有这些,让我们假设一些关于你的URI:
?(查询字符串), #(锚),或字符串的结尾.key=value由&字符附加的对的列表.保持这种心态:
null,前面会有一个?或者&,并且不能包含一个=,&或者#.key=,并且不能包含&或#.#角色之后的任何东西都是锚.让我们从映射出我们的基本URI结构开始.你有一个路径,这是一个字符开始绳子,直到一个?,#或者是字符串的结尾.您有一个可选的查询字符串,它从a开始,?直到#字符串的一个或结尾.你有一个可选的锚点,从a开始#直到字符串的结尾.
^
([^?#]+)
(?:
\?
([^#]+)
)?
(?:
#
(.*)
)?
$
Run Code Online (Sandbox Code Playgroud)
在深入研究查询字符串之前,让我们做一些清理工作.通过替换第一个捕获组,您可以轻松地要求路径等于某个值.无论你用(path)替换它,都必须跟随一个可选的查询字符串,一个可选的锚点和字符串的结尾(不多也不少).由于您不需要解析锚点,因此可以通过#在字符串的a 或末尾(即查询参数的末尾)结束匹配来替换捕获组.
^path
(?:
\?
([^#\+)
)?
(?=#|$)
Run Code Online (Sandbox Code Playgroud)
好的,我一直在做很多设置而不用担心你的具体例子.下一个示例将匹配特定路径(path),并在捕获foo参数值时可选地匹配查询字符串.这意味着您可以在此处停止并检查有效匹配.如果匹配有效,则第一个捕获组必须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)
Run Code Online (Sandbox Code Playgroud)
一些关键的要点:
?或&之前查看foo,这意味着你实际上必须匹配其中一个字符,这意味着查询字符串的开头(寻找a ?)必须是一个先行所以你实际上并没有匹配?.这也意味着您的查询字符串将始终至少包含一个字符(?),因此您希望重复查询字符串[^#]1次以上.foo,在这种情况下它会捕获可选值并继续重复.path?foo=123&foo=bar)将覆盖初始捕获的值.意味着您不会100%能够依赖上述解决方案.好吧..现在我已经捕获了foo值,是时候在非正整数的值上杀死匹配了.
^ (?# 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)
Run Code Online (Sandbox Code Playgroud)
让我们仔细看看表达式中的一些juju:
foo=\d*,我们用一个前瞻,以确保它后跟&,#或者字符串的结尾(查询字符串值的结尾).foo=\d*,那么正则表达式会被交流发电机踢回到之前的通用[^#]匹配.这不好,因为它会继续匹配!因此,在查找通用查询字符串()之前,必须确保不查看(必须由第一次更改处理).这是负面前瞻派上用场的地方.[?&]foo[^#]foo(?![?&]foo=)foo键,因为它们都必须等于非负整数.这foo也是可选的(或相等null).免责声明:大多数Regex101演示使用PHP进行更好的语法突出显示,并包含\n在负字符类中,因为有多行示例.
好问题!起初看起来相当简单......但是有很多陷阱.建议检查任何声明的解决方案将处理以下事项:
额外的比赛测试
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 = ''
Run Code Online (Sandbox Code Playgroud)
其他不匹配的测试
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
Run Code Online (Sandbox Code Playgroud)
我能提出的最简单的正则表达式适用于所有这些额外的情况:
path(?=(\?|$|#))(\?(.+&)?foo=(\d*)(&|#|$)|((?![?&]foo=).)*$)
Run Code Online (Sandbox Code Playgroud)
但是,会建议添加?:到未使用的捕获组,以便忽略它们,您可以轻松地foo从Group 1 获取值 - 请参阅Debuggex演示
path(?=(?:\?|$|#))(?:\?(?:.+&)?foo=(\d*)(?:&|#|$)|(?:(?![?&]foo=).)*$)
Run Code Online (Sandbox Code Playgroud)
