.Net Regex从文本中提取Urls永远不会返回

时间:2013-01-28 20:27:48

标签: .net regex

我有一个.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 #

1 个答案:

答案 0 :(得分:5)

问题1

这导致catastrophic backtracking

([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教程并不困难。

请参阅: