Wie greife ich in einem .NET Regex auf benannte Erfassungsgruppen zu?

255

Es fällt mir schwer, eine gute Ressource zu finden, die erklärt, wie benannte Erfassungsgruppen in C # verwendet werden. Dies ist der Code, den ich bisher habe:

string page = Encoding.ASCII.GetString(bytePage);
Regex qariRegex = new Regex("<td><a href=\"(?<link>.*?)\">(?<name>.*?)</a></td>");
MatchCollection mc = qariRegex.Matches(page);
CaptureCollection cc = mc[0].Captures;
MessageBox.Show(cc[0].ToString());

Dies zeigt jedoch immer nur die vollständige Zeile:

<td><a href="/path/to/file">Name of File</a></td> 

Ich habe mit mehreren anderen "Methoden" experimentiert, die ich auf verschiedenen Websites gefunden habe, aber ich erhalte immer das gleiche Ergebnis.

Wie kann ich auf die benannten Erfassungsgruppen zugreifen, die in meinem regulären Ausdruck angegeben sind?

UnkwnTech
quelle
3
Rückreferenz sollte im Format (? <Link>. *) Und nicht (? <Link>. *?) Sein
SO User
11
Zu Ihrer Information: Wenn Sie versuchen, eine benannte Erfassungsgruppe in einer XML-Datei zu speichern, <>wird diese beschädigt . Sie können (?'link'.*)stattdessen in diesem Fall verwenden. Nicht ganz relevant für diese Frage, aber ich bin hier von einer Google-Suche nach ".net
Named
1
StackOverflow-Link mit schönem Beispiel: stackoverflow.com/a/1381163/463206 Auch @rtpHarry, Nein, das <>wird es nicht brechen. Ich konnte die myRegex.GetGroupNames()Sammlung als XML-Elementnamen verwenden.
Radarbob

Antworten:

263

Verwenden Sie die Gruppensammlung des Match-Objekts und indizieren Sie es mit dem Namen der Erfassungsgruppe, z

foreach (Match m in mc){
    MessageBox.Show(m.Groups["link"].Value);
}
Paolo Tedesco
quelle
10
Nicht verwenden var m, da das ein wäre object.
Thomas Weller
111

Sie geben die benannte Erfassungsgruppenzeichenfolge an, indem Sie sie an den Indexer der GroupsEigenschaft eines resultierenden MatchObjekts übergeben.

Hier ist ein kleines Beispiel:

using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        String sample = "hello-world-";
        Regex regex = new Regex("-(?<test>[^-]*)-");

        Match match = regex.Match(sample);

        if (match.Success)
        {
            Console.WriteLine(match.Groups["test"].Value);
        }
    }
}
Andrew Hare
quelle
10

Das folgende Codebeispiel entspricht dem Muster auch bei Leerzeichen dazwischen. dh:

<td><a href='/path/to/file'>Name of File</a></td>

ebenso gut wie:

<td> <a      href='/path/to/file' >Name of File</a>  </td>

Die Methode gibt true oder false zurück, je nachdem, ob die eingegebene htmlTd-Zeichenfolge mit dem Muster übereinstimmt oder nicht. Wenn es übereinstimmt, enthalten die out-Parameter den Link bzw. den Namen.

/// <summary>
/// Assigns proper values to link and name, if the htmlId matches the pattern
/// </summary>
/// <returns>true if success, false otherwise</returns>
public static bool TryGetHrefDetails(string htmlTd, out string link, out string name)
{
    link = null;
    name = null;

    string pattern = "<td>\\s*<a\\s*href\\s*=\\s*(?:\"(?<link>[^\"]*)\"|(?<link>\\S+))\\s*>(?<name>.*)\\s*</a>\\s*</td>";

    if (Regex.IsMatch(htmlTd, pattern))
    {
        Regex r = new Regex(pattern,  RegexOptions.IgnoreCase | RegexOptions.Compiled);
        link = r.Match(htmlTd).Result("${link}");
        name = r.Match(htmlTd).Result("${name}");
        return true;
    }
    else
        return false;
}

Ich habe dies getestet und es funktioniert richtig.

SO Benutzer
quelle
1
Vielen Dank, dass Sie mich daran erinnert haben, dass geschweifte Klammern auf die Gruppen zugreifen können. Ich bleibe lieber dabei, ${1}um die Dinge noch einfacher zu halten.
Magnus Smith
Dies beantwortet die Frage vollständig, hat aber einige Probleme, die zu lang sind, um hier erklärt zu werden, aber ich habe diese in meiner Antwort unten
Mariano Desanze
1

Wenn jemand einen Anwendungsfall hat, in dem er Gruppennamen benötigt, bevor er eine Suche nach einem Regex-Objekt ausführt, kann er Folgendes verwenden:

var regex = new Regex(pattern); // initialized somewhere
// ...
var groupNames = regex.GetGroupNames();
Tinamou
quelle
1

Diese Antwort verbessert die Antwort von Rashmi Pandit , die in gewisser Weise besser ist als die anderen, da sie das genaue Problem, das in der Frage aufgeführt ist, vollständig zu lösen scheint.

Der schlechte Teil ist, dass es ineffizient ist und die IgnoreCase-Option nicht konsistent verwendet.

Ein ineffizienter Teil ist, dass die Erstellung und Ausführung von Regex teuer sein kann und in dieser Antwort nur einmal erstellt werden konnte (beim Aufrufen Regex.IsMatchwurde die Regex nur hinter den Kulissen erneut erstellt). Und MatchMethode nur einmal aufgerufen worden sein könnte und in einer Variablen gespeichert und dann linkund namerufen Sie sollte Resultvon diesen Variablen.

Die Option IgnoreCase wurde nur im MatchTeil verwendet, nicht jedoch im Regex.IsMatchTeil.

Ich habe auch die Regex-Definition außerhalb der Methode verschoben, um sie nur einmal zu erstellen (ich denke, dies ist der sinnvolle Ansatz, wenn wir die Assembly mit der RegexOptions.CompiledOption speichern ).

private static Regex hrefRegex = new Regex("<td>\\s*<a\\s*href\\s*=\\s*(?:\"(?<link>[^\"]*)\"|(?<link>\\S+))\\s*>(?<name>.*)\\s*</a>\\s*</td>",  RegexOptions.IgnoreCase | RegexOptions.Compiled);

public static bool TryGetHrefDetails(string htmlTd, out string link, out string name)
{
    var matches = hrefRegex.Match(htmlTd);
    if (matches.Success)
    {
        link = matches.Result("${link}");
        name = matches.Result("${name}");
        return true;
    }
    else
    {
        link = null;
        name = null;
        return false;
    }
}
Mariano Desanze
quelle