2013-06-06 7 views
1

J'ai du HTML avec des 'séquences de contrôle', j'ai déjà enlevé les balises html du contenu, maintenant je voudrais transformer les 'séquences de contrôle' en 'styles'.Capturer des groupes, Plusieurs groupes par ligne

Après les balises HTML sont supprimés ... Je cette

"<!C43!><!TG!>Some Characters" 

pour produire en fin de compte ... ce

<span class="C43 TG">Some Characters</span> 

et mon défaut C# jusqu'ici:

Regex reg = new Regex("<!([^<>]+?)!>"); 

Match matches = reg.Match(line); 
foreach (Group group in matches.Groups) 
{ 
    // finds both groups, 
    // and remove the control sequence 
} 

Je n'ai pas encore beaucoup dans mon 'foreach', car il renvoie incorrectement les groupes suivants quand je l'inspecte avec un breakboint ...

Group 1 : <!C43!> 
Group 2 : C43 
<it does not find second group :(> 

Toute aide serait appréciée, mais je suis surtout pour l'expression régulière correcte pour trouver ce que je cherche dans la chaîne, mais je suis aussi inutile avec la bibliothèque d'expression régulière, donc la plupart «Trouver un groupe, un groupe de magasins, retirer un groupe de la chaîne que je recherche» serait également très apprécié.

Répondre

2

Comme les autres ont dit, vous voulez faire une boucle sur Match es pas plus Group s que votre modèle a un seul groupe. La manière habituelle de faire est soit la boucle de Michael Gunter for ou tout simplement

Match m = reg.Match(line); 
while(m.Success) 
{ 
    // read class from m.Groups[1] 
    m = m.NextMatch(); 
} 

Cependant, pour résoudre votre problème ultime, obtenir toutes les données séparément et mettre la chaîne de retour ensemble pourrait être un peu ennuyeux - surtout si vous vouloir faire ce remplacement en plusieurs lignes à la fois. Par conséquent, vous voudrez peut-être regarder dans Regex.Replace (la version prenant un rappel). De cette façon, vous pouvez faire correspondre tout dans une seule correspondance, puis utiliser la capacité unique de .NET pour accéder à plusieurs captures d'un seul groupe.

var line = "<!C43!><!TG!>Some Characters"; 

MatchEvaluator evaluator = new MatchEvaluator(ReplaceCallback); 

string output = Regex.Replace(
    line, 
    @"(?:<!([^<>]+)!>)+(.+)", 
    evaluator 
); 

Et ailleurs dans votre classe:

static string ReplaceCallback(Match match) 
{ 
    var sb = new StringBuilder("<span class=\""); 
    sb.Append(match.Groups[1].Captures[0].Value); 
    for(int i = 1; i < match.Groups[1].Captures.Count; i++) 
    { 
     sb.Append(" "); 
     sb.Append(match.Groups[1].Captures[i].Value); 
    } 
    sb.Append("\">"); 
    sb.Append(match.Groups[2].Value); 
    sb.Append("</span>"); 
    return sb.ToString(); 
} 

Configuration de la chaîne est probablement plus facile avec String.Format mais je ne pouvais pas trouver un moyen au moment de l'String.JoinCaptureCollection.

Alors, que ce fait essentiellement:

Le motif @"(?:<!([^<>]+)!>)+(.+)" correspond à un ou plusieurs <!...!> « jetons », puis le reste de la ligne. Ce faisant, il capture le contenu du <!...!>. À chaque répétition, une autre capture est enregistrée et vous pouvez y accéder plus tard dans le rappel. Après les jetons <!...!>, nous correspondons et capturons le reste de la ligne avec (.+). Notez le @ devant la chaîne: il crée la chaîne verbatim, ce que vous devez toujours faire lorsque vous spécifiez des modèles regex - sinon vous aurez des problèmes quand il s'agit d'échapper. Notez également que ?: après la première parenthèse ouvrante. C'est pour supprimer la capture, car nous n'avons pas besoin d'une autre capture qui contient les délimiteurs <! et !>. Toujours utiliser des groupes non-capturant, sauf si vous avez réellement besoin de capture est également une bonne pratique.

La fonction de rappel est ensuite appelée pour chaque correspondance dans l'entrée. Il n'y a qu'une seule correspondance qui contient la ligne entière. Ce match a capturé deux jetons dans le groupe 1 et le reste de la ligne dans le groupe 2.

Ainsi, nous pouvons maintenant construire simplement une chaîne, qui commence par <span =", puis une liste limitée de toutes les captures du groupe 1, puis ">, puis le reste de la ligne capturé et enfin la fermeture </span>.

Comme je l'ai dit, si vous trouvez un moyen de String.Join le groupe de collecte, la fonction de rappel réduit à trois lignes ou plus.

Si la distinction entre Match, Group et Capture est encore un peu floue pour vous, je vous suggère de mettre un point d'arrêt dans la fonction de rappel et juste d'examiner l'objet il match.

+0

wow, réponse parfaite, votre code a fonctionné comme un régal! Je vous remercie! :-) – Nnoel

0

Je ne peux pas reproduire votre problème dans RegexHero:

http://www.regexhero.net/tester

Il capture les 2 groupes comme:

1: C43 
1: TG 

Êtes-vous sûr que votre entrée est ce que vous attendez que ce soit? Est-ce que vous itérez sur la collection prévue pour les résultats?

+0

http://regexpal.com/ dit aussi que mon expression régulière est correcte, C# problème spécifique? – Nnoel

+0

oh ... maintenant votre lien demande d'installer silverlight ... je vais le vérifier – Nnoel

+0

Je dirais que c'est lié à votre code. Déboguer la réponse reg.Match et inspecter les propriétés, etc – Haney

3

Vous faites une boucle sur la mauvaise chose. Essayez ceci:

string line = ...; 
Regex reg = ...; 
for (var match = reg.Match(line); match.Success; match = match.NextMatch()) 
{ 
    // in here, don't bother with .Groups... you don't need it 
} 
+0

Merci Micheal, cela a fonctionné quand j'ai essayé cela à la place. – Nnoel