2009-03-17 10 views
4

J'ai du contenu du site qui contient des abréviations. J'ai une liste d'abréviations reconnues pour le site, ainsi que leurs explications. Je veux créer une expression régulière qui me permettra de remplacer toutes les abréviations reconnues trouvées dans le contenu par un balisage.Mettez en surbrillance une liste de mots en utilisant une expression régulière dans C#

Par exemple:

contenu:

This is just a little test of the memb to see if it gets picked up. 
Deb of course should also be caught here.

Abréviations:

memb = Member; deb = Debut;

Résultat:

This is just a little test of the [a title="Member"]memb[/a] to see if it gets picked up. 
[a title="Debut"]Deb[/a] of course should also be caught here.

(Ceci est juste un balisage exemple pour simplifier).

Merci.

EDIT:

CraigD de réponse est presque terminé, mais il y a des problèmes. Je veux seulement faire correspondre des mots entiers. Je veux également garder la bonne capitalisation de chaque mot remplacé, de sorte que deb est toujours deb, et Deb est toujours Deb selon le texte original. Par exemple, cette entrée:

 
This is just a little test of the memb. 
And another memb, but not amemba. 
Deb of course should also be caught here.deb! 

Répondre

10

Tout d'abord vous devez Regex.Escape() toutes les chaînes d'entrée.

Ensuite, vous pouvez chercher dans la chaîne, et itérativement les remplacer par le balisage vous avez à l'esprit:

string abbr  = "memb"; 
string word  = "Member"; 
string pattern = String.Format("\b{0}\b", Regex.Escape(abbr)); 
string substitue = String.Format("[a title=\"{0}\"]{1}[/a]", word, abbr); 
string output = Regex.Replace(input, pattern, substitue); 

EDIT: J'ai demandé si un String.Replace() simple, ne serait pas suffisant - mais je peux voyez pourquoi regex est souhaitable: vous pouvez l'utiliser pour appliquer des remplacements de "mot entier" uniquement en créant un motif qui utilise des ancres de délimitation de mots.

Vous pouvez aller aussi loin que la construction d'un modèle unique de toutes vos chaînes d'entrée échappées, comme ceci:

\b(?:{abbr_1}|{abbr_2}|{abbr_3}|{abbr_n})\b 

puis en utilisant un match evaluator pour trouver le remplacement droit. De cette façon, vous pouvez éviter d'itérer plus d'une fois la chaîne d'entrée.

+0

Je choisis cela comme réponse sélectionnée parce que cela fonctionne pour mes besoins étendus (dans l'édition). J'ai construit un modèle unique et utilisé un évaluateur de correspondance comme suggéré, et cela fonctionne très bien et sans boucle foreach aussi. Merci Tomalak! –

+0

(J'ai posté la solution finale ci-dessous) –

+0

J'ai trouvé cela utile pour javascript aussi, merci! – Stephen

1

Je doute qu'il fonctionnera mieux que de simplement faire une chaîne de caractères normale.replace, donc si la performance est critique mesure (refactoring un peu pour utiliser une regex compilé). Vous pouvez faire la version regex comme:

var abbrsWithPipes = "(abbr1|abbr2)"; 
var regex = new Regex(abbrsWithPipes); 
return regex.Replace(html, m => GetReplaceForAbbr(m.Value)); 

Vous devez mettre en œuvre GetReplaceForAbbr, qui reçoit le spécifique abbr étant adaptée.

4

Je ne sais pas si cela va évoluer vers une grande liste de mots, mais je pense que cela devrait donner la sortie que vous voulez (bien que dans votre question le 'résultat' semble identique à 'contenu')?

Quoi qu'il en soit, laissez-moi savoir si cela est ce que vous êtes après

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text.RegularExpressions; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var input = @"This is just a little test of the memb to see if it gets picked up. 
Deb of course should also be caught here."; 
      var dictionary = new Dictionary<string,string> 
      { 
       {"memb", "Member"} 
       ,{"deb","Debut"} 
      }; 
      var regex = "(" + String.Join(")|(", dictionary.Keys.ToArray()) + ")"; 
      foreach (Match metamatch in Regex.Matches(input 
       , regex /*@"(memb)|(deb)"*/ 
       , RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)) 
      { 
       input = input.Replace(metamatch.Value, dictionary[metamatch.Value.ToLower()]); 
      } 
      Console.Write (input); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

Merci - très utile! Savez-vous comment le modifier légèrement afin qu'il ne corresponde qu'à des mots entiers, c'est-à-dire qu'il corresponde à memb mais pas à amemba? –

+0

Je vois cela a été répondu ailleurs - heureux que vous ayez ce que vous recherchiez. Je pensais que la boucle avait l'air bizarre - maintenant que quelqu'un d'autre a posté 'MatchEvaluator', je me rappelle l'avoir déjà utilisé. Beaucoup plus agréable! – Conceptdev

1

que je fais assez exactement ce que vous cherchez dans mon application et cela fonctionne pour moi: le paramètre str est votre contenu:

public static string GetGlossaryString(string str) 
     { 
      List<string> glossaryWords = GetGlossaryItems();//this collection would contain your abbreviations; you could just make it a Dictionary so you can have the abbreviation-full term pairs and use them in the loop below 

      str = string.Format(" {0} ", str);//quick and dirty way to also search the first and last word in the content. 

      foreach (string word in glossaryWords) 
       str = Regex.Replace(str, "([\\W])(" + word + ")([\\W])", "$1<span class='glossaryItem'>$2</span>$3", RegexOptions.IgnoreCase); 

      return str.Trim(); 
     } 
1

Pour toute personne intéressée, voici ma solution finale. C'est pour un contrôle utilisateur .NET. Il utilise un seul modèle avec un évaluateur de correspondance, comme suggéré par Tomalak, donc il n'y a pas de boucle foreach. C'est une solution élégante, et elle me donne la sortie correcte pour l'entrée d'échantillon tout en préservant le bon boîtier pour les chaînes assorties.

public partial class Abbreviations : System.Web.UI.UserControl 
{ 
    private Dictionary<String, String> dictionary = DataHelper.GetAbbreviations(); 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     string input = "This is just a little test of the memb. And another memb, but not amemba to see if it gets picked up. Deb of course should also be caught here.deb!"; 

     var regex = "\\b(?:" + String.Join("|", dictionary.Keys.ToArray()) + ")\\b"; 

     MatchEvaluator myEvaluator = new MatchEvaluator(GetExplanationMarkup); 

     input = Regex.Replace(input, regex, myEvaluator, RegexOptions.IgnoreCase); 

     litContent.Text = input; 
    } 

    private string GetExplanationMarkup(Match m) 
    { 
     return string.Format("<b title='{0}'>{1}</b>", dictionary[m.Value.ToLower()], m.Value); 
    } 
} 

La sortie ressemble à ceci (ci-dessous). Notez qu'il ne fait que correspondre aux mots entiers, et que l'enveloppe est préservée de la chaîne originale:

This is just a little test of the <b title='Member'>memb</b>. And another <b title='Member'>memb</b>, but not amemba to see if it gets picked up. <b title='Debut'>Deb</b> of course should also be caught here.<b title='Debut'>deb</b>! 
Questions connexes