5

我有一个 .Net Regex 表达式,它从字符串中提取所有看起来有效的 Urls。它通过了我的所有 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 引擎挂起中恢复?这是调用代码:

        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());
        }

我在这个特殊的正则表达式模式上花了很多时间,并且已经尝试了至少 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 #
4

1 回答 1

5

问题 1

这导致灾难性的回溯

([A-Za-z]+){2,4}

和:

[\+~%\/.\w-_]*[/]?

我猜你的意思是:

([A-Za-z]{2,4})

和:

[\+~%\/.\w-_]*

您的正则表达式当前不匹配带有等号的 URL,这就是灾难性回溯首先发生的原因:

\??#?(?:[\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 教程并不难。

看:

于 2013-01-28T20:34:48.037 回答