Удалить все пустые теги HTML? - PullRequest
1 голос
/ 06 апреля 2011

Я представляю себе функцию, которая, как я полагаю, будет использовать Regex, и она будет рекурсивной для таких экземпляров, как <p><strong></strong></p>, чтобы удалить все пустые теги HTML внутри строки. Это должно было бы учитывать пробелы, если это возможно. Не было бы сумасшедших случаев, когда <символ использовался в значении атрибута. </p>

Я довольно ужасен в регулярных выражениях, но я думаю, что это возможно. Как ты можешь это сделать?

Вот метод, который я имею до сих пор:

Public Shared Function stripEmptyHtmlTags(ByVal html As String) As String
    Dim newHtml As String = Regex.Replace(html, "/(<.+?>\s*</.+?>)/Usi", "")

    If html <> newHtml Then
        newHtml = stripEmptyHtmlTags(newHtml)
    End If

    Return newHtml
End Function

Однако мой текущий Regex в формате PHP, и, похоже, он не работает. Я не знаком с синтаксисом регулярных выражений .NET.

Всем тем, кто говорит, не используйте регулярные выражения: Мне любопытно, каким будет шаблон в любом случае. Конечно, существует шаблон, который может сопоставить все открывающие / закрывающие начальные теги с любым количеством пробелов (или их нет) между тегами? Я видел регулярное выражение, которое сопоставляет теги HTML с любым количеством атрибутов, одним пустым тегом (например, <p></p>) и т. Д.

До сих пор я пробовал следующие шаблоны регулярных выражений в вышеприведенном методе безрезультатно (например, у меня есть текстовая строка с пустыми тегами абзацев, которые даже не были удалены).

Regex.Replace(html, "/(<.+?>\s*</.+?>)/Usi", "")

Regex.Replace(html, "(<.+?>\s*</.+?>)", "")

Regex.Replace(html, "%<(\w+)\b[^>]*>\s*</\1\s*>%", "")

Regex.Replace(html, "<\w+\s*>\s*</\1\s*>", "")

Ответы [ 4 ]

8 голосов
/ 07 апреля 2011

Во-первых, обратите внимание, что пустые элементы HTML по определению не являются вложенными.

Обновление: В приведенном ниже решении рекурсивно применяется регулярное выражение пустого элемента для удаления "nested-empty-element" структур, таких как: <p><strong></strong></p> (с учетом оговорок, указанных ниже ).

Простая версия:

Это работает довольно хорошо (см. Предостережения ниже) для HTML, не имеющего атрибутов начального тега, содержащего <> забавные вещи, в виде (непроверенного) фрагмента VB.NET:

Dim RegexObj As New Regex("<(\w+)\b[^>]*>\s*</\1\s*>")
Do While RegexObj.IsMatch(html)
    html = RegexObj.Replace(html, "")
Loop

Улучшенная версия

<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[\w\-.:]+))?)*\s*/?>\s*</\1\s*>

Вот расширенная версия без комментариев в VB.NET (не проверена):

Dim RegexObj As New Regex("<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:""[^""]*""|'[^']*'|[\w\-.:]+))?)*\s*/?>\s*</\1\s*>")
Do While RegexObj.IsMatch(html)
    html = RegexObj.Replace(html, "")
Loop

Это более сложное регулярное выражение правильно соответствует действительному пустому элементу HTML 4.01 , даже если у него есть угловые скобки в значениях его атрибута (еще раз, учитывая предостережения ниже). Другими словами, это регулярное выражение правильно обрабатывает все значения атрибутов начального тега, которые заключены в кавычки (которые могут иметь <>), не заключены в кавычки (что не может) и пустые. Вот полностью прокомментированная (и протестированная) версия PHP:

function strip_empty_tags($text) {
    // Match empty elements (attribute values may have angle brackets).
    $re = '%
        # Regex to match an empty HTML 4.01 Transitional element.
        <                    # Opening tag opening "<" delimiter.
        (\w+)\b              # $1 Tag name.
        (?:                  # Non-capture group for optional attribute(s).
          \s+                # Attributes must be separated by whitespace.
          [\w\-.:]+          # Attribute name is required for attr=value pair.
          (?:                # Non-capture group for optional attribute value.
            \s*=\s*          # Name and value separated by "=" and optional ws.
            (?:              # Non-capture group for attrib value alternatives.
              "[^"]*"        # Double quoted string.
            | \'[^\']*\'     # Single quoted string.
            | [\w\-.:]+      # Non-quoted attrib value can be A-Z0-9-._:
            )                # End of attribute value alternatives.
          )?                 # Attribute value is optional.
        )*                   # Allow zero or more attribute=value pairs
        \s*                  # Whitespace is allowed before closing delimiter.
        >                    # Opening tag closing ">" delimiter.
        \s*                  # Content is zero or more whitespace.
        </\1\s*>             # Element closing tag.
        %x';
    while (preg_match($re, $text)) {
        // Recursively remove innermost empty elements.
        $text = preg_replace($re, '', $text);
    }
}

Предостережения: Эта функция не анализирует HTML. Он просто сопоставляет и удаляет любую последовательность текстового шаблона, соответствующую действительному пустому элементу HTML 4.01 (который по определению является вложенным , а не ). Обратите внимание, что это также ошибочно сопоставляет и удаляет тот же текстовый шаблон, который может встречаться вне обычной разметки HTML, например, внутри тегов SCRIPT и STYLE, комментариев HTML и атрибутов других начальных тегов. Это регулярное выражение не работает с короткими тегами. Любой поклонник bobenc, желающий дать этому ответу автоматический нисходящий голос, покажите мне один действительный пустой элемент HTML 4.01, которому это регулярное выражение не удается правильно сопоставить. Это регулярное выражение следует спецификации W3C и действительно работает.

Обновление: Это решение для регулярных выражений также не работает (и ошибочно удалит допустимую разметку), если вы сделаете что-то невероятно маловероятно (но совершенно верно), например:

<div att="<p att='">stuff</div><div att="'></p>'">stuff</div>

Резюме:

Если подумать, просто используйте анализатор HTML!

1 голос
/ 07 апреля 2011

Вы не можете сделать это с помощью регулярного выражения.Вы, вероятно, могли бы использовать анализатор xml, предполагая, что html правильно сформирован.

1 голос
/ 07 апреля 2011

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

0 голосов
/ 07 апреля 2011

Почему рекурсивно, вы можете просто запустить

 <(\w+)\s*>\s*</\1\s*>

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

...