ОЧЕНЬ медленное выполнение регулярного выражения при использовании больших документов - PullRequest
2 голосов
/ 02 октября 2008

Мне нужно преобразовать встроенные атрибуты стиля CSS в их эквивалентные теги HTML. Решение, которое у меня есть, работает, но работает ОЧЕНЬ медленно, используя пространство имен Microsoft .Net Regex и длинные документы (~ 40 страниц HTML). Я пробовал несколько вариантов, но без каких-либо полезных результатов. Я немного поработал над выполнением выражений, но в итоге вызывается только встроенный метод Regex Replace.

Я уверен, что злоупотребляю жадностью регулярного выражения, но я не уверен, что можно обойти это, чтобы достичь того, чего я хочу, используя одно регулярное выражение.

Я хочу запускать следующие юнит-тесты:

[Test]
public void TestCleanReplacesFontWeightWithB()
{
    string html = "<font style=\"font-weight:bold\">Bold Text</font>";
    html = Q4.PrWorkflow.Helper.CleanFormatting(html);
    Assert.AreEqual("<b>Bold Text</b>", html);
}
[Test]
public void TestCleanReplacesMultipleAttributesFontWeightWithB()
{
    string html = "<font style=\"font-weight:bold; color: blue; \">Bold Text</font>";
    html = Q4.PrWorkflow.Helper.CleanFormatting(html);
    Assert.AreEqual("<b>Bold Text</b>", html);
}
[Test]
public void TestCleanReplaceAttributesBoldAndUnderlineWithHtml()
{
    string html = "<span style=\"font-weight:bold; color: blue; text-decoration: underline; \">Bold Text</span>";
    html = Q4.PrWorkflow.Helper.CleanFormatting(html);
    Assert.AreEqual("<u><b>Bold Text</b></u>", html);
}
[Test]
public void TestCleanReplaceAttributesBoldUnderlineAndItalicWithHtml()
{
    string html = "<span style=\"font-weight:bold; color: blue; font-style: italic; text-decoration: underline; \">Bold Text</span>";
    html = Q4.PrWorkflow.Helper.CleanFormatting(html);
    Assert.AreEqual("<u><b><i>Bold Text</i></b></u>", html);
}
[Test]
public void TestCleanReplacesFontWeightWithSpaceWithB()
{
    string html = "<font size=\"10\" style=\"font-weight: bold\">Bold Text</font>";
    html = Q4.PrWorkflow.Helper.CleanFormatting(html);
    Assert.AreEqual("<b>Bold Text</b>", html);
}

Обычное выражение, которое я использую для достижения этой логики, работает, но ОЧЕНЬ медленно. Регулярное выражение в коде c # выглядит так:

public static IReplacePattern IncludeInlineItalicToITag(ICleanUpHtmlFactory factory)
{
    return factory.CreateReplacePattern("(<(span|font) .*?style=\".*?font-style:\\s*italic[^>]*>)(.*?)</\\2>", "$1<i>$3</i></$2>");
}
public static IReplacePattern IncludeInlineBoldToBTag(ICleanUpHtmlFactory factory)
{
    return factory.CreateReplacePattern("(<(span|font) .*?style=\".*?font-weight:\\s*bold[^>]*>)(.*?)</\\2>", "$1<b>$3</b></$2>");
}
public static IReplacePattern IncludeInlineUnderlineToUTag(ICleanUpHtmlFactory factory)
{
    return factory.CreateReplacePattern("(<(span|font) .*?style=\".*?text-decoration:\\s*underline[^>]*>)(.*?)</\\2>", "$1<u>$3</u></$2>");
}

Ответы [ 4 ]

7 голосов
/ 02 октября 2008

Я считаю, что проблема в том, что если он найдет тег span | font, для которого не определен атрибут style, он продолжит его поиск до конца документа из-за «. *?». Я не проверял это, но изменил его на "[^>] *?" может улучшить производительность.

РЕДАКТИРОВАТЬ: Убедитесь, что вы примените это изменение для всех ". *?" у тебя есть; даже тот, который захватывает содержимое между тегами (используйте «[^ <] *?» там), потому что, если файл не будет правильно сформирован, он будет записан до следующего закрывающего тега.

0 голосов
/ 06 августа 2009

Во время тестирования я обнаружил странное поведение. Когда regexp запускается в отдельном потоке, он работает намного быстрее. У меня есть скрипт SQL, который я разделял на разделы из Go to Go с помощью регулярных выражений При работе с этим сценарием без использования отдельного потока он длится около 2 минут. Но при использовании многопоточности это длится всего несколько секунд.

0 голосов
/ 07 октября 2008

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

"(<(span|font) .*?style=\".*?font-style:\\s*italic[^>]*>)(.*?)</\\2>", "$1<i>$3</i></$2>"

с двумя отдельными выражениями:

"(<span .*?style=\".*?font-style:\\s*italic[^>]*>)(.*?)</span>", "$1<i>$2</i></span>"
"(<font .*?style=\".*?font-style:\\s*italic[^>]*>)(.*?)</font>", "$1<i>$2</i></font>"

Конечно, это удваивает разбор файла, но регулярное выражение проще, с меньшим количеством обратных ссылок, на практике это может быть быстрее. Это не очень хорошо (повторение кода), но пока оно работает ...

Как ни странно, я сделал нечто подобное (у меня нет кода), чтобы очистить HTML, сгенерированный инструментом, упростив его так, чтобы JavaHelp мог его понять ... Это один из случаев, когда регулярные выражения для HTML в порядке потому что не человек делает ошибки или меняет мелочи, а создает процесс, а процесс с четко определенными шаблонами.

0 голосов
/ 02 октября 2008

.NET регулярные выражения не поддерживают рекурсивные конструкции. PCRE поддерживает, но это не имеет значения.

рассмотривать

<font style="font-weight: bold;"> text1 <font color="blue"> text2 </font> text3 </font>

Это будет преобразовано в

<b> text1 <font color="blue"> text2 </b> text3 </font>

Я бы предложил использовать правильный синтаксический анализатор разметки и, возможно, использовать regexp для значений тегов style.

Редактировать: Поцарапать это. Кажется, .NET имеет конструкцию для сбалансированных, рекурсивных шаблонов. Но не такой мощный, как в PCRE / perl.

(?<N>content) would push N onto a stack if content matches
(?<-N>content) would pop N from the stack, if content matches.
(?(N)yes|no) would match "yes" if N is on the stack, otherwise "no".

Подробнее см. http://weblogs.asp.net/whaggard/archive/2005/02/20/377025.aspx.

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