我有一个.Net Regex表达式,可以从字符串中提取所有有效的Url。它通过我的所有20个单元测试,除了一个:当Url包含问号时。这导致正则表达式引擎挂起进程并造成严重破坏。
我花了很多时间在这个表达上,但我不确定如何解决这个问题。希望一个正则表达式专业人士可以伸出援助之手!两部分问题:
1)如何改进此正则表达式模式,以便在评估字符串“http://www.example.com/?a=1”时不会挂起:
正则表达式:
(?<URL>(\s|^)((?<Scheme>[A-Za-z]{3,9})[:][/][/])?([A-Za-z0-9-]+[.])+([A-Za-z]+){2,4}(?(Scheme)|[/])(((?:[\+~%\/.\w-_]*[/]?)?\??#?(?:[\w]*))?)*(\s|$))
我建议使用这个很棒的在线.Net Regex测试引擎:http://regexhero.net/tester/
2)我的呼叫代码可以做些什么来防止/恢复正则表达式引擎挂起?这是调用代码:
Regex linkParser = new Regex(UrlMatchRegex, RegexOptions.Compiled | RegexOptions.IgnoreCase);
// Find matches and add them to the result
foreach (Match m in linkParser.Matches(message))
{
result.Add(m.Value.Trim());
}
我花了很多时间在这个特殊的Regex模式上,并尝试过至少7次我发现的替换(包括此页面中的大多数:http://mathiasbynens.be/demo/url-regex)。我宁愿改进我现有的模式,而不是使用完全不同的模式,但我愿意选择。
最终正则表达式v1:
经过一些修改后,在下面的帮助下,我的最终正则表达式匹配如下:
(?<URL>((?<=\s)|^)((?<Scheme>[A-Za-z]{3,9})[:][/][/])?([A-Za-z0-9-]+[.])+[A-Za-z]{2,4}(?(Scheme)|[/]?)(((?:[\+~%\/.\w-_]*[/]?)?\??#?&?(?:[\w=]*))?)*((?=\s)|$))
** Regex v2:更新为包含域名,端口号,主题标签的集合列表,但不包含尾随斜杠
(?<URL>((?<=\b)|^)((?<Scheme>[A-Za-z]{3,9})[:][/][/])?([A-Za-z0-9-]+[.])+((?<TLD>\b(aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mn|mn|mo|mp|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|nom|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ra|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw|arpa)\b))+(\/?)+((((?:[:\+~%/.\w-_\/])?[\?]?[#]?[&]?(?:[\w=]*))?)+(?=\?|\b)))
测试用例: 这是#
的测试不应该匹配:
like when you a/b test something
this sentence ends with a/
just a #hashtag
应匹配:
sos.me extra
sos.me. extra <-- not period
sos.me, extra <-- not comma
sos.me/ extra <-- should match traililng /
sos.me/#hashtag? extra <-- not questionmark
sos.me? extra <-- not questionmark
www.sos.me? extra <-- not questionmark
sos.me/abcde extra
sos.me/abcde#hashtag extra
sos.me/abcdf/0 extra
sos.me/abcdf/0#hashtag extra
sos.me/something.aspx extra
sos.me/something.aspx#hashtag extra
http://something.com: extra <-- not colon
sos.me/something.aspx?name=value extra
sos.me/something.aspx?name=value#hashtag extra
http://something.com extra
https://something.com extra
http://something.com:80 extra
http://something.com:80/ extra <-- should match trailing /
http://something.com:80/?a=v&1=2 extra
http://something.com:80/?a=v&1=2#hashtag extra
http://something.com:80/?a=v&1=2# extra <-- should match trailing #
答案 0 :(得分:5)
问题1
([A-Za-z]+){2,4}
和
[\+~%\/.\w-_]*[/]?
我猜你的意思是:
([A-Za-z]{2,4})
和
[\+~%\/.\w-_]*
您的正则表达式目前不匹配具有相同符号的网址,这就是灾难性回溯发生的原因:
\??#?(?:[\w]*)
与'a = 1'不匹配,因为\w
不包含'='。你可以很容易地解决这个问题:
\??#?(?:[\w=%]*)
(我也把'%'扔在那里)
此外,您的正则表达式匹配您的URL之前和之后的空白。您可能更喜欢\s
的外观,因为这将匹配空白之前或之后的位置而不是空白本身:
(?<URL>((?<=\s)|^)((?<Scheme>[A-Za-z]{3,9})[:][/][/])?([A-Za-z0-9-]+[.])+[A-Za-z]{2,4}(?(Scheme)|[/])(((?:[\+~%\/.\w-_]*[/]?)?\??#?(?:[\w=]*))?)*((?=\s)|$))
问题2
没有什么可以检测或从灾难性的回溯正则表达式中恢复。你可以尝试的最好的方法是将正则表达式转换为一个独立的线程,然后在一定的超时后终止线程。究竟如何做到这将是一个完全不同的问题,但在线查找.NET教程并不困难。
请参阅: