Как удалить HTML-теги из строки в ASP.NET?


123

Как с помощью ASP.NET надежно удалить теги HTML из заданной строки (т. Е. Без использования регулярного выражения)? Я ищу что-то вроде PHP strip_tags.

Пример:

<ul><li>Hello</li></ul>

Вывод:

"Привет"

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


Я бы предположил, что PHP strip_tags использует регулярное выражение за кулисами!
stevehipwell

10
@ Дэниел: потому что регулярное выражение очень плохо, особенно если у вас есть вложение.
Джоэл Кохорн,

Хм, не похоже, что PHP Strip_Tags особенно надежен, будь то официальные заметки и комментарии: uk.php.net/strip_tags
- Бен Дугид

Ответы:


112

Если он просто удаляет все теги HTML из строки, это надежно работает и с регулярным выражением. Заменить:

<[^>]*(>|$)

с пустой строкой глобально. Не забудьте потом нормализовать строку, заменив:

[\s\r\n]+

с одним пробелом и обрезкой результата. При желании замените любые объекты символов HTML на фактические символы.

Примечание :

  1. Есть ограничение: HTML и XML допускают >значения атрибутов. Это решение будет возвращать сломанную разметку при столкновении с таких значений.
  2. Решение технически безопасно, например: результат никогда не будет содержать ничего, что можно было бы использовать для выполнения межсайтовых сценариев или нарушения макета страницы. Это просто не очень чисто.
  3. Как и в случае со всем, что касается HTML и регулярных выражений: используйте правильный синтаксический анализатор, если вы должны правильно понимать его при любых обстоятельствах.

52
Хотя это и не требуется, я думаю, что многие читатели захотят также удалить HTM-кодировку, например &quote;. Я совмещаю его с WebUtility.HtmlDecodeтем (который, в свою очередь, не удаляет теги). Используйте его после удаления тега, так как он может перезаписать &gt;и &lt;. НапримерWebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious

@YahooSerious Спасибо за пример. Это прекрасно работает. Спасибо.
SearchForKnowledge

Html Agility Pack - это то, что нужно, я использовал его еще в веб-формах, чтобы вырезать целые веб-страницы для использования контента!
Bojangles

3
@YahooSerious это позволит использовать вектор XSS в & gt; скрипт & lt; предупреждение ( "XXS"); & Gt; / script & lt; Не будет обработан регулярным выражением, но преобразован с помощью HtmlDecode в <script> alert ("XXS"); </ script>

1
@Heather Очень хорошее замечание. Удаление тегов HTML необходимо будет выполнить снова после декодирования объекта.
Tomalak

76

Скачайте HTMLAgilityPack прямо сейчас! ;) Скачать LInk

Это позволяет загружать и анализировать HTML. Затем вы можете перемещаться по DOM и извлекать внутренние значения всех атрибутов. Серьезно, это займет максимум 10 строк кода. Это одна из лучших бесплатных библиотек .net.

Вот образец:

            string htmlContents = new System.IO.StreamReader(resultsStream,Encoding.UTF8,true).ReadToEnd();

            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(htmlContents);
            if (doc == null) return null;

            string output = "";
            foreach (var node in doc.DocumentNode.ChildNodes)
            {
                output += node.InnerText;
            }

2
вы даже можете запрашивать каждый text()узел, обрезать содержимое и строку. Присоединяйтесь к тем, у которых есть пробел. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jessehouwing

или просто используйте doc.DocumentNode.InnerText, хотя, похоже, у него есть некоторые проблемы с обработкой пробелов ...
jessehouwing 02

17
Почему if (doc == null)чек? Это всегда ложь, не так ли?
avesse

67
Regex.Replace(htmlText, "<.*?>", string.Empty);

Просто и красиво. Спасибо!
Тиллито

5
Имеет много проблем - не работает с атрибутами, содержащими <или> в них, и не работает с тегами, охватывающими более одной строки, если не запускается с RegexOptions.SingleLine.
ChrisF

2
Неееет, используйте "<[^>] *>".
Пол Кениц

11
protected string StripHtml(string Txt)
{
    return Regex.Replace(Txt, "<(.|\\n)*?>", string.Empty);
}    

Protected Function StripHtml(Txt as String) as String
    Return Regex.Replace(Txt, "<(.|\n)*?>", String.Empty)
End Function

2
Не работает во многих случаях, включая разрывы строк, отличные от unix.
ChrisF

6

Я разместил это на форумах asp.net, и это все еще кажется одним из самых простых решений. Я не могу гарантировать, что это самый быстрый или самый эффективный, но он довольно надежный. В .NET вы можете использовать сами объекты HTML Web Control. Все, что вам действительно нужно сделать, это вставить строку во временный объект HTML, такой как DIV, а затем использовать встроенный «InnerText», чтобы захватить весь текст, который не содержится в тегах. Ниже приведен простой пример C #:


System.Web.UI.HtmlControls.HtmlGenericControl htmlDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
htmlDiv.InnerHtml = htmlString;
String plainText = htmlDiv.InnerText;

похоже, это не работает, я тестировал это с помощью простого InnerHtml = "<b> foo </b>"; а InnerText имеет значение "<b> foo </b>" :(
Axarydax

Не делай этого. Это решение вводит незакодированный HTML прямо в вывод. Это оставит вас широко открытыми для атак межсайтового скриптинга - вы только что разрешили любому, кто может изменить строку html, внедрять в ваше приложение любые произвольные html и javascript!
saille

5

Я написал довольно быстрый метод на C #, который чертовски превосходит Regex. Он размещен в статье на CodeProject.

Среди его преимуществ - более высокая производительность, а также возможность заменять именованные и нумерованные объекты HTML (например, &amp;amp;и&203; ), замену блоков комментариев и многое другое.

Прочтите соответствующую статью о CodeProject .

Спасибо.


4

Для тех из вас, кто не может использовать HtmlAgilityPack, вариант чтения XML .NETs. Это может привести к сбою в хорошо отформатированном HTML, поэтому всегда добавляйте уловку с regx в качестве резервной копии. Обратите внимание, что это НЕ быстро, но дает прекрасную возможность для отладки старой школы.

public static string RemoveHTMLTags(string content)
    {
        var cleaned = string.Empty;
        try
        {
            StringBuilder textOnly = new StringBuilder();
            using (var reader = XmlNodeReader.Create(new System.IO.StringReader("<xml>" + content + "</xml>")))
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Text)
                        textOnly.Append(reader.ReadContentAsString());
                }
            }
            cleaned = textOnly.ToString();
        }
        catch
        {
            //A tag is probably not closed. fallback to regex string clean.
            string textOnly = string.Empty;
            Regex tagRemove = new Regex(@"<[^>]*(>|$)");
            Regex compressSpaces = new Regex(@"[\s\r\n]+");
            textOnly = tagRemove.Replace(content, string.Empty);
            textOnly = compressSpaces.Replace(textOnly, " ");
            cleaned = textOnly;
        }

        return cleaned;
    }


1

Для тех, кто жалуется на то, что решение Майкла Типтопа не работает, вот способ .Net4 + сделать это:

public static string StripTags(this string markup)
{
    try
    {
        StringReader sr = new StringReader(markup);
        XPathDocument doc;
        using (XmlReader xr = XmlReader.Create(sr,
                           new XmlReaderSettings()
                           {
                               ConformanceLevel = ConformanceLevel.Fragment
                               // for multiple roots
                           }))
        {
            doc = new XPathDocument(xr);
        }

        return doc.CreateNavigator().Value; // .Value is similar to .InnerText of  
                                           //  XmlDocument or JavaScript's innerText
    }
    catch
    {
        return string.Empty;
    }
}

1
using System.Text.RegularExpressions;

string str = Regex.Replace(HttpUtility.HtmlDecode(HTMLString), "<.*?>", string.Empty);

0

Я просмотрел предлагаемые здесь решения на основе Regex, и они не внушают мне никакой уверенности, за исключением самых тривиальных случаев. Угловая скобка в атрибуте - это все, что нужно для взлома, не говоря уже о искаженном HTML из дикой природы. А как насчет таких сущностей, как&amp; ? Если вы хотите преобразовать HTML в простой текст, вам также необходимо декодировать объекты.

Поэтому я предлагаю способ ниже.

Используя HtmlAgilityPack , этот метод расширения эффективно удаляет все HTML-теги из html-фрагмента. Также декодирует объекты HTML, такие как &amp;. Возвращает только внутренние текстовые элементы с новой строкой между каждым текстовым элементом.

public static string RemoveHtmlTags(this string html)
{
        if (String.IsNullOrEmpty(html))
            return html;

        var doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);

        if (doc.DocumentNode == null || doc.DocumentNode.ChildNodes == null)
        {
            return WebUtility.HtmlDecode(html);
        }

        var sb = new StringBuilder();

        var i = 0;

        foreach (var node in doc.DocumentNode.ChildNodes)
        {
            var text = node.InnerText.SafeTrim();

            if (!String.IsNullOrEmpty(text))
            {
                sb.Append(text);

                if (i < doc.DocumentNode.ChildNodes.Count - 1)
                {
                    sb.Append(Environment.NewLine);
                }
            }

            i++;
        }

        var result = sb.ToString();

        return WebUtility.HtmlDecode(result);
}

public static string SafeTrim(this string str)
{
    if (str == null)
        return null;

    return str.Trim();
}

Если вы действительно серьезно, вы хотите игнорировать содержимое определенного HTML тегов тоже ( <script>, <style>, <svg>, <head>,<object> приходят на ум!) , Потому что они , вероятно , не содержат читаемое содержание в том смысле , мы после. То, что вы там будете делать, будет зависеть от ваших обстоятельств и того, как далеко вы хотите зайти, но с помощью HtmlAgilityPack было бы довольно тривиально занести в белый или черный список выбранные теги.

Если вы визуализируете контент обратно на HTML-страницу, убедитесь, что вы понимаете уязвимость XSS и способы ее предотвращения - т.е. всегда кодируйте любой введенный пользователем текст, который отображается обратно на HTML-страницу ( >становится и &gt;т. Д.).


0

Для второго параметра, то есть для сохранения некоторых тегов, вам может понадобиться такой код, используя HTMLagilityPack:

public string StripTags(HtmlNode documentNode, IList keepTags)
{
    var result = new StringBuilder();
        foreach (var childNode in documentNode.ChildNodes)
        {
            if (childNode.Name.ToLower() == "#text")
            {
                result.Append(childNode.InnerText);
            }
            else
            {
                if (!keepTags.Contains(childNode.Name.ToLower()))
                {
                    result.Append(StripTags(childNode, keepTags));
                }
                else
                {
                    result.Append(childNode.OuterHtml.Replace(childNode.InnerHtml, StripTags(childNode, keepTags)));
                }
            }
        }
        return result.ToString();
    }

Дополнительные объяснения на этой странице: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/


0

Вы также можете сделать это с помощью AngleSharp, который является альтернативой HtmlAgilityPack (не то чтобы HAP - это плохо). Его проще использовать, чем HAP, чтобы получить текст из источника HTML.

var parser = new HtmlParser();
var htmlDocument = parser.ParseDocument(source);
var text = htmlDocument.Body.Text();

Вы можете взглянуть на раздел, посвященный ключевым функциям, где они утверждают, что они «лучше», чем HAP. Я думаю, что по большей части это, вероятно, перебор для текущего вопроса, но все же это интересная альтернатива.


-4

Просто используйте string.StripHTML();


3
Как указывает @Serpiton, в BCL такого метода нет. Не могли бы вы указать на реализацию этого метода или предоставить свой собственный?
Sven Grosen
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.