Jak usunąć znaczniki HTML z ciągu znaków w programie ASP.NET?


123

Używając ASP.NET, jak niezawodnie usunąć znaczniki HTML z danego ciągu (tj. Bez użycia wyrażenia regularnego)? Szukam czegoś takiego jak PHP strip_tags.

Przykład:

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

Wynik:

"Dzień dobry"

Staram się nie wymyślać koła na nowo, ale do tej pory nie znalazłem niczego, co spełniałoby moje potrzeby.


Wyobrażam sobie, że strip_tags PHP używa wyrażenia regularnego za kulisami!
stevehipwell

10
@Daniel: ponieważ wyrażenie regularne jest w tym bardzo złe, szczególnie jeśli masz zagnieżdżenie.
Joel Coehoorn

Hmm, nie wygląda na to, że Strip_Tags PHP jest szczególnie wiarygodny, zarówno w oficjalnych notatkach, jak i komentarzach: uk.php.net/strip_tags
Zhaph - Ben Duguid

Odpowiedzi:


112

Jeśli po prostu usuwa wszystkie znaczniki HTML z ciągu znaków, działa to niezawodnie również z wyrażeniem regularnym. Zastąpić:

<[^>]*(>|$)

z pustym ciągiem, globalnie. Nie zapomnij później znormalizować łańcucha, zastępując:

[\s\r\n]+

z pojedynczą spacją i przycinając wynik. Opcjonalnie zamień dowolne jednostki znaków HTML z powrotem na rzeczywiste znaki.

Uwaga :

  1. Istnieje ograniczenie: HTML i XML zezwalają >na wartości atrybutów. Ten roztwór będzie powrotu złamane znaczników po wykryciu tych wartości.
  2. Rozwiązanie jest technicznie bezpieczne, na przykład: Wynik nigdy nie będzie zawierał niczego, co mogłoby zostać użyte do wykonania cross-site scripting lub do złamania układu strony. Po prostu nie jest zbyt czysty.
  3. Jak w przypadku wszystkich rzeczy w HTML i regex:
    użyj odpowiedniego parsera, jeśli musisz to zrobić dobrze w każdych okolicznościach.

52
Chociaż nie jest to wymagane, myślę, że wielu czytelników będzie chciało również usunąć kodowanie HTM, na przykład &quote;. Łączę to z WebUtility.HtmlDecodetym (co z kolei nie usunie tagów). Użyj go po usunięciu tagów, ponieważ może przepisać &gt;i &lt;. Np.WebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious

@YahooSerious Dziękujemy za podanie przykładu. To działa świetnie. Dziękuję Ci.
SearchForKnowledge

Html Agility Pack jest drogą do zrobienia, użyłem go już w formularzach internetowych, aby rozebrać całe strony internetowe do wykorzystania zawartości!
Bojangles

3
@YahooSerious pozwoli to jednak na wektor XSS w & gt; skrypt & lt; alert („XXS”); & gt; / skrypt & lt; Nie zostanie oczyszczony przez wyrażenie regularne, ale zostanie przekonwertowany przez HtmlDecode na <script> alert ("XXS"); </ script>

1
@ Heather Bardzo dobra uwaga. Usuwanie znaczników HTML musiałoby zostać wykonane ponownie po zdekodowaniu jednostki.
Tomalak

76

Pobierz teraz HTMLAgilityPack! ;) Pobierz LInk

Umożliwia to ładowanie i analizowanie kodu HTML. Następnie możesz nawigować po DOM i wyodrębnić wewnętrzne wartości wszystkich atrybutów. Poważnie, zajmie ci to maksymalnie około 10 linii kodu. Jest to jedna z największych darmowych bibliotek .net.

Oto próbka:

            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
możesz nawet wysyłać zapytania do każdego text()węzła, przycinać zawartość i ciąg znaków, łączyć te ze spacją. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jessehouwing

lub po prostu użyj doc.DocumentNode.InnerText, chociaż wydaje się, że ma to pewne problemy z obsługą białych znaków ...
jessehouwing

17
Dlaczego if (doc == null)czek? To zawsze jest fałszywe, prawda?
avesse

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

Proste i przyjemne. Dzięki!
Tillito

5
Ma wiele problemów - nie radzi sobie z atrybutami zawierającymi <lub> i nie radzi sobie dobrze z tagami, które obejmują więcej niż jedną linię, chyba że działają z RegexOptions.SingleLine.
ChrisF

2
Nieee, użyj „<[^>] *>”.
Paul Kienitz,

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
Nie działa w przypadku wielu przypadków, w tym podziałów wierszy innych niż unixowe.
ChrisF

6

Opublikowałem to na forach asp.net i nadal wydaje się, że jest to jedno z najłatwiejszych rozwiązań. Nie gwarantuję, że jest najszybszy lub najbardziej wydajny, ale jest całkiem niezawodny. W .NET możesz używać samych obiektów HTML Web Control. Wszystko, co naprawdę musisz zrobić, to wstawić swój ciąg do tymczasowego obiektu HTML, takiego jak DIV, a następnie użyć wbudowanego 'InnerText', aby pobrać cały tekst, który nie jest zawarty w tagach. Poniżej znajduje się prosty przykład w języku C #:


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

to nie działa, przetestowałem to z prostym InnerHtml = "<b> foo </b>"; a InnerText ma wartość „<b> foo </b>” :(
Axarydax

Nie rób tego. To rozwiązanie wprowadza niezakodowany kod HTML bezpośrednio do danych wyjściowych. To pozostawiłoby cię otwartą na ataki Cross Site Scripting - po prostu pozwoliłeś każdemu, kto może zmienić ciąg html, na wstrzyknięcie dowolnego html i javascript do twojej aplikacji!
żagiel

5

Napisałem dość szybką metodę w języku C #, która bije piekło z Regex. Jest hostowany w artykule na CodeProject.

Jego zalety to między innymi lepsza wydajność możliwość zastępowania nazwanych i ponumerowanych encji HTML (takich jak &amp;amp;i &203;) oraz zastępowanie bloków komentarzy i nie tylko.

Przeczytaj powiązany artykuł w CodeProject .

Dziękuję Ci.


4

Dla tych z Was, którzy nie mogą używać HtmlAgilityPack, czytnik XML .NET jest opcją. Może się to jednak nie powieść w przypadku dobrze sformatowanego kodu HTML, więc zawsze dodawaj haczyk z regx jako kopię zapasową. Zwróć uwagę, że nie jest to szybkie, ale zapewnia dobrą okazję do przejścia przez debugowanie w starej szkole.

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;
    }

3
string result = Regex.Replace(anytext, @"<(.|\n)*?>", string.Empty);

1

Dla tych, którzy twierdzą, że rozwiązanie Michaela Tiptopa nie działa, oto sposób na zrobienie tego .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

Przyjrzałem się proponowanym tutaj rozwiązaniom opartym na Regex i nie dają mi one pewności, z wyjątkiem najbardziej banalnych przypadków. Do złamania wystarczy nawias ostry w atrybucie, nie mówiąc już o nieprawidłowo sformatowanym HTML z „dzikiego”. A co z takimi bytami &amp;? Jeśli chcesz przekonwertować HTML na zwykły tekst, musisz również zdekodować jednostki.

Dlatego proponuję poniższą metodę.

Korzystając z HtmlAgilityPack , ta metoda rozszerzenia skutecznie usuwa wszystkie tagi HTML z fragmentu HTML. Dekoduje również jednostki HTML, takie jak &amp;. Zwraca tylko wewnętrzne elementy tekstowe, z nowym wierszem między każdym elementem tekstowym.

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();
}

Jeśli jesteś naprawdę poważne, że chcesz, aby zignorować zawartości niektórych tagów HTML zbyt ( <script>, <style>, <svg>, <head>, <object>przychodzą do głowy!), Ponieważ prawdopodobnie nie zawierają treści czytelne w tym sensie jesteśmy po. To, co tam zrobisz, będzie zależeć od twoich okoliczności i tego, jak daleko chcesz się posunąć, ale używając HtmlAgilityPack byłoby dość trywialne dodanie do białej lub czarnej listy wybranych tagów.

Jeśli renderujesz treść z powrotem na stronę HTML, upewnij się, że rozumiesz lukę w zabezpieczeniach XSS i jak temu zapobiec - tj. Zawsze koduj tekst wprowadzony przez użytkownika, który jest renderowany z powrotem na stronie HTML ( >staje się &gt;itd.).


0

W przypadku drugiego parametru, czyli zachowania niektórych tagów, możesz potrzebować kodu takiego jak ten, używając 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();
    }

Więcej wyjaśnień na tej stronie: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/


0

Możesz to również zrobić za pomocą AngleSharp, który jest alternatywą dla HtmlAgilityPack (nie, że HAP jest zły). Jest łatwiejszy w użyciu niż HAP, aby uzyskać tekst ze źródła HTML.

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

Możesz rzucić okiem na sekcję kluczowych funkcji, w której argumentują, że są „lepsi” niż HAP. Myślę, że w większości jest to przesada w obecnym pytaniu, ale nadal jest to interesująca alternatywa.


-4

Po prostu użyj string.StripHTML();


3
Jak wskazuje @Serpiton, w BCL nie ma takiej metody. Czy mógłbyś wskazać na implementację tej metody lub podać własną?
Sven Grosen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.