Почему это регулярное выражение является жадным? - PullRequest
2 голосов
/ 14 ноября 2009

Я пытаюсь извлечь все ссылки, которые содержат / thumb / внутри, внутри "". На самом деле мне нужно только использовать изображения SRC. Я не знаю, будут ли изображения заканчиваться на jpg или возникнут проблемы с чувствительностью к регистру и т. Д. Меня действительно волнует только полная ссылка.

m = Regex.Match(page, @"""(.+?/thumbs/.+?)""");
//...
var thumbUrl = m.Groups[1].Value;

Мой полный код

    var page = DownloadWebPage(url);
    var reg = new Regex(@"Elements\s+\((.*)\)", RegexOptions.Multiline);
    var m = reg.Match(page);
    var szEleCount= m.Groups[1].Value;
    int eleCount = int.Parse(szEleCount);

    m = Regex.Match(page, @"""(.+?/thumbs/.+?)""");
    while (m.Success)
    {
        var thumbUrl = m.Groups[1].Value;
        //i break here to see a problem
        m = m.NextMatch();
    }

thumbUrl выглядит как

center \ "> ... много текста, нет / thumbs / ... src = \" http://images.fdhkdhfkd.com/thumbs/dfljdkl/22350.jpg

Ответы [ 5 ]

4 голосов
/ 14 ноября 2009

Нежелательные регулярные выражения могут быть медленными, потому что движку приходится много возвращаться назад.

Этот использует только жадные выражения:

@"""([^""]*/thumbs/[^""]*)"""

Вместо того, чтобы сопоставлять наименьшее количество чего-либо, оно сопоставляет столько не-двойных кавычек, сколько может.

3 голосов
/ 14 ноября 2009

Способ работы неохотного (не жадного) квантификатора заключается в том, что, как только он начинает совпадать, он останавливается при первой возможности. То, что вы пытаетесь сделать, - это сопоставить минимальный объем текста, который соответствует вашим критериям, что не одно и то же; Вы все еще должны убедиться, что он не начать соответствия, прежде чем вы этого хотите. Как уже предлагали другие, вы можете сделать это, заменив .+? в своем регулярном выражении чем-то, что не соответствует кавычкам, например [^""]+.

Но это все еще оставляет вас с проблемой производительности. В вашем примере регулярное выражение начинает совпадать, когда видит кавычку в center">; когда он достигает котировки на src=" (при условии, что вы изменили .+? на [^""]+), он прерывает эту попытку, пытаясь двигаться дальше. Следующая попытка, начиная с цитаты в src=", будет успешной. Таким образом, вы получаете правильный результат сейчас, но вы все еще тратите много времени на первую неудачную попытку матча.

Ключ к написанию быстрых регулярных выражений состоит в том, чтобы убедиться, что, если попытка совпадения потерпит неудачу, она потерпит неудачу как можно быстрее. Например, я думаю, можно с уверенностью предположить, что вам не нужны угловые скобки между " и /thumbs/, поэтому добавьте их в набор символов, которые вы не хотите сопоставлять: [^""<>]+. Теперь любая попытка совпадения, начинающаяся с кавычки в center">, будет прервана на следующей позиции.

Есть и другие вещи, которые вы можете сделать для дальнейшей оптимизации регулярного выражения, включая атомные группы и негативные взгляды, но это, вероятно, будет так быстро, как вам нужно:

@"""([^""<>]+/thumbs/[^""<>]+)"""
3 голосов
/ 14 ноября 2009

Если вы анализируете (X) HTML, рассмотрите возможность использования правильного парсера.

См .: Каков наилучший способ анализа html в C #? для некоторых примеров C #, как это сделать.

1 голос
/ 14 ноября 2009

Проблема в том. +? также потребляет "s", поэтому он продолжает совпадать вне атрибута src. Используйте это вместо:

m = Regex.Match(page, @"""([^""]+/thumbs/[^""]+)""");
0 голосов
/ 14 ноября 2009

Обычно, когда у вас есть регулярное выражение, вы используете статическое поле и указываете RegexOptions.Compiled параметр:

static Regex template = new Regex(@"""(.+?/thumbs/.+?)""", RegexOptions.Compiled | RegexOptions.Multiline)
...