Удалить все пустые теги HTML?

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

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

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

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

Однако мое текущее регулярное выражение находится в формате 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*>", "")


person SventoryMang    schedule 06.04.2011    source источник
comment
Почему регулярное выражение? Вы взглянули на некоторые библиотеки для синтаксического анализа xml, такие как HtmlAgilityPack и т. Д.?   -  person Chandu    schedule 06.04.2011
comment
Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы. - Джейми Завински   -  person nfechner    schedule 07.04.2011
comment
И снова мне вспоминается эта поломка парней при разборе html с регулярным выражением. +1 за две проблемы! См. meta.so   -  person gideon    schedule 07.04.2011
comment
HTML — это лемма прокачки (просто погуглите), поэтому невозможно создать регулярное выражение для его анализа.   -  person DwB    schedule 07.04.2011
comment
To all those saying don't use regex Я не буду раздавать свои лучшие регулярные выражения htlm/xhtml/xml/sgml бесплатно. Это коммерческая тайна (?DEFINE)   -  person    schedule 07.04.2011
comment
Одна из проблем с вашим регулярным выражением заключается в том, что ваша строка шаблона имеет ненужные (и ошибочные) разделители и модификаторы шаблона. то есть "/regex/i" плохо. "regex" хорошо. Я обновил свой ответ фрагментами VB, которые должны вам подойти.   -  person ridgerunner    schedule 07.04.2011
comment
Третье регулярное выражение выше принадлежит мне, но вы используете его неправильно. Это работает, когда вы удаляете разделители %, как показано в моем ответе.   -  person ridgerunner    schedule 07.04.2011


Ответы (4)


Во-первых, обратите внимание, что пустые элементы 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!

person Community    schedule 06.04.2011
comment
Это PHP, мне нужен в формате .NET, который, видимо, использует другой синтаксис. Пробовал и не работает. - person SventoryMang; 07.04.2011
comment
Работал как шарм! Я использовал расширенную версию, потрясающий ридж. - person SventoryMang; 07.04.2011

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

person Oliver Charlesworth    schedule 06.04.2011
comment
Если предположить, что я не хочу использовать библиотеку синтаксического анализа HTML, как будет выглядеть выражение? Рекурсия легко позаботится о том, чтобы применять его снова и снова. - person SventoryMang; 07.04.2011
comment
Но регулярное выражение можно создать так, чтобы оно соответствовало самому внутреннему пустому элементу, а затем это регулярное выражение можно применять итеративно, пока не останется ни одного элемента. - person ridgerunner; 07.04.2011

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

person Dave Rager    schedule 06.04.2011
comment
Да, ты можешь. Пустые теги не вложены, - person ridgerunner; 07.04.2011
comment
@ridge, первый пример, указанный в вопросе, вложен. - person Dave Rager; 07.04.2011
comment
Да вы совершенно правы. Однако, если регулярное выражение в моем решении применяется рекурсивно, оно правильно удалит эти вложенные пустые элементы. Починил это. Спасибо за внимание. - person ridgerunner; 07.04.2011

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

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

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

person Blindy    schedule 06.04.2011
comment
Это не работает, когда я пробую этот шаблон, я получаю сообщение об ошибке: System.ArgumentException: parsing "<\w+\s*>\s*</\1\s*>" - Reference to undefined group number 1. (я не давал вам -1). - person SventoryMang; 07.04.2011
comment
Здесь отсутствует ()… должно быть <(\w+)\s*>\s*</\1\s*> - person bw_üezi; 07.04.2011