Utilisation d'expressions régulières C # pour supprimer les balises HTML

139

Comment utiliser l'expression régulière C # pour remplacer / supprimer toutes les balises HTML, y compris les crochets angulaires? Quelqu'un peut-il m'aider avec le code?

Keltex
la source
Vous ne l'indiquez pas, mais j'en déduis que vous souhaitez également supprimer entièrement les éléments de script et de style et pas seulement supprimer la balise. La réponse HTML Agility Pack ci-dessous est correcte pour supprimer les balises, mais pour supprimer le script et le style, vous aurez également besoin de quelque chose comme stackoverflow.com/questions/13441470/...
John
1
La question indiquée comme duplicata contient beaucoup d'informations (et Tony le poney!), Mais elle ne demandait que des balises d'ouverture, pas toutes les balises. Je ne suis donc pas sûr que ce soit techniquement un doublon. Cela dit, la réponse est la même: ne le faites pas.
revoir

Réponses:

154

Comme souvent indiqué précédemment, vous ne devez pas utiliser d'expressions régulières pour traiter des documents XML ou HTML. Ils ne fonctionnent pas très bien avec les documents HTML et XML, car il n'y a aucun moyen d'exprimer des structures imbriquées de manière générale.

Vous pouvez utiliser ce qui suit.

String result = Regex.Replace(htmlDocument, @"<[^>]*>", String.Empty);

Cela fonctionnera dans la plupart des cas, mais il y aura des cas (par exemple CDATA contenant des chevrons) où cela ne fonctionnera pas comme prévu.

Daniel Brückner
la source
13
Il s'agit d'une implémentation naïve. Autrement dit, <div id = "x <4>"> est malheureusement un html valide. Traite les cas les plus sains cependant ..
Ryan Emerle
8
Comme indiqué, je suis conscient que cette expression échouera dans certains cas. Je ne suis même pas sûr que le cas général puisse être traité par une expression régulière sans erreur.
Daniel Brückner
1
Non, cela échouera dans tous les cas! son gourmand.
Jake
13
@Cipher, pourquoi pensez-vous que la cupidité est un problème? En supposant que la correspondance commence au début d'une balise HTML valide, elle ne s'étendra jamais au-delà de la fin de cette balise. C'est à cela que sert le [^>].
Alan Moore
1
@AlanMoore html n'est pas un "langage régulier", c'est-à-dire que vous ne pouvez pas faire correspondre correctement tout ce qui est du HTML valide avec des expressions régulières. voir: stackoverflow.com/questions/590747/…
Kache
78

La bonne réponse est de ne pas faire cela, utilisez le HTML Agility Pack .

Modifié pour ajouter:

Pour voler sans vergogne le commentaire ci-dessous de jesse, et pour éviter d'être accusé de ne pas avoir répondu correctement à la question après tout ce temps, voici un extrait de code simple et fiable utilisant le HTML Agility Pack qui fonctionne avec les morceaux de HTML capricieux même les plus imparfaitement formés:

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(Properties.Resources.HtmlContents);
var text = doc.DocumentNode.SelectNodes("//body//text()").Select(node => node.InnerText);
StringBuilder output = new StringBuilder();
foreach (string line in text)
{
   output.AppendLine(line);
}
string textOnly = HttpUtility.HtmlDecode(output.ToString());

Il y a très peu de cas défendables pour l'utilisation d'une expression régulière pour analyser du HTML, car le HTML ne peut pas être analysé correctement sans une prise en compte du contexte qui est très difficile à fournir même dans un moteur de regex non traditionnel. Vous pouvez y arriver partiellement avec un RegEx, mais vous devrez effectuer des vérifications manuelles.

Html Agility Pack peut vous fournir une solution robuste qui réduira le besoin de corriger manuellement les aberrations qui peuvent résulter du traitement naïf du HTML comme une grammaire sans contexte.

Une expression régulière peut vous apporter principalement ce que vous voulez la plupart du temps, mais elle échouera dans des cas très courants. Si vous pouvez trouver un analyseur meilleur / plus rapide que HTML Agility Pack, allez-y, mais veuillez ne pas soumettre le monde à un piratage HTML plus cassé.

JasonVrai
la source
27
HTML Agility Pack n'est pas la réponse à tout ce qui concerne le travail avec HTML (par exemple, que faire si vous ne voulez travailler qu'avec des fragments du code HTML ?!).
PropellerHead
7
Cela fonctionne plutôt bien avec des fragments de HTML, et c'est la meilleure option pour le scénario décrit par l'affiche originale. Un Regex, par contre, ne fonctionne qu'avec un HTML idéalisé et rompra avec un HTML parfaitement valide, car la grammaire du HTML n'est pas régulière. S'il utilisait Ruby, j'aurais quand même suggéré nokogiri ou hpricot, ou beautifulsoup pour Python. Il est préférable de traiter le HTML comme du HTML, et non un flux de texte arbitraire sans grammaire.
JasonTrue
1
Le HTML n'est pas une grammaire régulière et ne peut donc pas être analysé uniquement avec des expressions régulières. Vous pouvez utiliser des expressions régulières pour le lexing, mais pas pour l'analyse. C'est vraiment aussi simple que cela. Les linguistes se seraient mis d’accord sur ce point avant même l’existence du HTML.
JasonTrue
20
Ce n'est pas une question d'opinion. Une expression régulière peut vous apporter principalement ce que vous voulez la plupart du temps, mais elle échouera dans des cas très courants. Si vous pouvez trouver un analyseur meilleur / plus rapide que HTML Agility Pack, allez-y, mais veuillez ne pas soumettre le monde à un piratage HTML plus cassé.
JasonTrue
2
Vous ne pouvez pas identifier correctement les balises HTML de manière fiable sans analyser le HTML. Comprenez-vous toute la grammaire du HTML? Voyez le hack diabolique pour être "assez proche" que d'autres réponses suggèrent, et dites-moi pourquoi vous voudriez avoir à maintenir cela. Me rejeter parce qu'une tentative rapide de piratage fonctionne pour votre entrée d'échantillon ne rendra pas votre solution correcte. J'ai parfois utilisé des expressions régulières pour générer des rapports à partir de contenu HTML ou pour corriger une référence CSS en utilisant la correspondance négative sur & gt; pour limiter les risques d'erreurs, mais nous avons effectué des vérifications supplémentaires; ce n'était pas un objectif général.
JasonTrue
38

La question est trop large pour recevoir une réponse définitive. Parlez-vous de la suppression de toutes les balises d'un document HTML réel, comme une page Web? Si tel est le cas, vous devrez:

  • supprimer la déclaration <! DOCTYPE ou le prologue <? xml s’ils existent
  • supprimer tous les commentaires SGML
  • supprimer tout l'élément HEAD
  • supprimer tous les éléments SCRIPT et STYLE
  • faire Grabthar-sait-quoi avec les éléments FORM et TABLE
  • supprimer les balises restantes
  • supprimer les séquences <! [CDATA [et]]> des sections CDATA mais laisser leur contenu seul

C'est juste par dessus ma tête - je suis sûr qu'il y a plus. Une fois que vous avez fait tout cela, vous vous retrouverez avec des mots, des phrases et des paragraphes exécutés ensemble à certains endroits, et de gros morceaux d'espaces inutiles dans d'autres.

Mais, en supposant que vous travaillez avec juste un fragment et que vous puissiez simplement supprimer toutes les balises, voici l'expression régulière que j'utiliserais:

@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>"

Faire correspondre les chaînes entre guillemets simples et doubles dans leurs propres alternatives suffit pour résoudre le problème des crochets angulaires dans les valeurs d'attribut. Je ne vois aucun besoin de faire correspondre explicitement les noms d'attributs et d'autres éléments à l'intérieur de la balise, comme le fait l'expression régulière dans la réponse de Ryan; la première alternative gère tout cela.

Au cas où vous vous poseriez des questions sur ces (?>...)constructions, ce sont des groupes atomiques . Ils rendent le regex un peu plus efficace, mais plus important encore, ils empêchent le retour en arrière incontrôlé, ce à quoi vous devez toujours faire attention lorsque vous mélangez l'alternance et les quantificateurs imbriqués comme je l'ai fait. Je ne pense pas vraiment que ce serait un problème ici, mais je sais que si je ne le mentionne pas, quelqu'un d'autre le fera. ;-)

Cette expression régulière n'est pas parfaite, bien sûr, mais elle est probablement aussi bonne que vous en aurez jamais besoin.

Alan Moore
la source
1
C'est de loin la meilleure réponse. Vous répondez à la question de l'affiche et expliquez pourquoi une expression régulière ne doit pas être utilisée pour la tâche donnée. Bien joué.
JWilliams
26
Regex regex = new Regex(@"</?\w+((\s+\w+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", RegexOptions.Singleline);

La source

Ryan Emerle
la source
18

@JasonTrue a raison, que la suppression des balises HTML ne doit pas être effectuée via des expressions régulières.

Il est assez simple de supprimer les balises HTML à l'aide de HtmlAgilityPack:

public string StripTags(string input) {
    var doc = new HtmlDocument();
    doc.LoadHtml(input ?? "");
    return doc.DocumentNode.InnerText;
}
zzzzBov
la source
1
Bien que je sois un peu en retard sur ce point, je voudrais mentionner que cela fonctionne également sur XML comme celui produit par Word et d'autres produits de bureau. Quiconque a déjà eu besoin de traiter avec Word xml ferait bien d'envisager son utilisation car cela aide beaucoup, surtout si vous devez supprimer les balises du contenu, ce pour quoi j'en avais besoin.
Steve Pettifer
Quand tout le reste a semblé échouer, ce simple extrait de code a sauvé la mise. Merci!
Ted Krapf
14

Je voudrais faire écho à la réponse de Jason bien que parfois vous ayez besoin d'analyser naïvement du HTML et d'extraire le contenu du texte.

J'avais besoin de faire cela avec du Html qui avait été créé par un éditeur de texte riche, toujours amusant et amusant.

Dans ce cas, vous devrez peut-être supprimer le contenu de certaines balises ainsi que les balises elles-mêmes.

Dans mon cas, et des balises ont été jetées dans ce mélange. Certains peuvent trouver mon implémentation (très légèrement) moins naïve un point de départ utile.

   /// <summary>
    /// Removes all html tags from string and leaves only plain text
    /// Removes content of <xml></xml> and <style></style> tags as aim to get text content not markup /meta data.
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string HtmlStrip(this string input)
    {
        input = Regex.Replace(input, "<style>(.|\n)*?</style>",string.Empty);
        input = Regex.Replace(input, @"<xml>(.|\n)*?</xml>", string.Empty); // remove all <xml></xml> tags and anything inbetween.  
        return Regex.Replace(input, @"<(.|\n)*?>", string.Empty); // remove any tags but not there content "<p>bob<span> johnson</span></p>" becomes "bob johnson"
    }
CountZero
la source
1
Mis à part les problèmes évidents de saut de ligne entre plates-formes, le fait d'avoir un quantificateur sans consistance est lent lorsque le contenu est délimité. Utilisez des choses comme <xml>.*(?!</xml>)</xml>avec le RegexOptions.SingleLinemodificateur pour les deux premiers et <[^>]*>pour le dernier. Les premiers peuvent également être combinés par une alternance capturée dans le premier nom de balise et des renvois à celui-ci dans l'anticipation négative et la balise finale.
ChrisF
5

essayez la méthode d'expression régulière à cette URL: http://www.dotnetperls.com/remove-html-tags

/// <summary>
/// Remove HTML from string with Regex.
/// </summary>
public static string StripTagsRegex(string source)
{
return Regex.Replace(source, "<.*?>", string.Empty);
}

/// <summary>
/// Compiled regular expression for performance.
/// </summary>
static Regex _htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);

/// <summary>
/// Remove HTML from string with compiled Regex.
/// </summary>
public static string StripTagsRegexCompiled(string source)
{
return _htmlRegex.Replace(source, string.Empty);
}
Owidat
la source
3

utilisez ceci..

@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>"
Swaroop
la source
-1

Utilisez cette méthode pour supprimer les balises:

public string From_To(string text, string from, string to)
{
    if (text == null)
        return null;
    string pattern = @"" + from + ".*?" + to;
    Regex rx = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
    MatchCollection matches = rx.Matches(text);
    return matches.Count <= 0 ? text : matches.Cast<Match>().Where(match => !string.IsNullOrEmpty(match.Value)).Aggregate(text, (current, match) => current.Replace(match.Value, ""));
}
AnisNoorAli
la source