Як я можу знімати теги HTML із рядка в ASP.NET?


123

Використовуючи ASP.NET, як я можу надійно знімати теги HTML із заданого рядка (тобто не використовувати регулярний вираз)? Я шукаю щось на зразок PHP strip_tags.

Приклад:

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

Вихід:

"Здравствуйте"

Я намагаюся не винаходити колесо, але поки що не знайшов нічого, що відповідало б моїм потребам.


Я б міг уявити, що PHP strip_tags використовує регулярний регулярний вираз!
stevehipwell

10
@Daniel: тому що регулярний вираз в цьому дуже поганий, особливо якщо у вас гніздування.
Джоел Куехорн

Хм, не схоже на те, що PHP's Strip_Tags є особливо надійним, або продовжуючи офіційні замітки та коментарі: uk.php.net/strip_tags
Zhaph - Ben Duguid

Відповіді:


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 Серйозний

@YahooSerious Дякуємо, що надали приклад. Це чудово працює. Дякую.
SearchForKnowledge

Пакет спритності Html - це дорога дорога, я використовував його у веб-формах, щоб знімати цілі веб-сторінки для використання вмісту!
Bojangles

3
@YahooSerious, це дозволить вектору XSS, однак & gt; сценарій & lt; оповіщення ("XXS"); & gt; / скрипт & lt; Регекс не буде дезінфікований, але перетворений HtmlDecode в <script> alert ("XXS"); </ script>

1
@Heather Дуже добре. Знімання HTML-тегів потрібно буде повторити після декодування сутності.
Томалак

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

17
Чому if (doc == null)чек? Це завжди помилково, чи не так?
avesse

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

Просте і приємне. Дякую!
Тілліто

5
Має багато проблем - не стосується атрибутів, що містять у собі <або>, і не справляється з тегами, що охоплюють більше одного рядка, якщо не виконувати RegexOptions.SingleLine.
ChrisF

2
Noooo, використовуй "<[^>] *>".
Пол Кієніц

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, .NETs XML зчитувач - це варіант. Це може не вдатися до добре відформатованого 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 немає. Чи можете ви вказати на реалізацію цього методу чи надати свій власний?
Свен Гросен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.