Laden...

[Regex] Alle Untergruppen einer Gruppe finden

Erstellt von cynos vor 17 Jahren Letzter Beitrag vor 17 Jahren 2.833 Views
C
cynos Themenstarter:in
7 Beiträge seit 2004
vor 17 Jahren
[Regex] Alle Untergruppen einer Gruppe finden

Hallo,
ich habe da so einen langen und sehr schönen Regex geschrieben und bin eigentlich auch durchaus fit mit Regex, nur irgendwie sehe ich gerade den Wald vor lauter Bäumen nicht mehr.

Es geht da um folgende Sache (beispielsweise):

(:question:(?<data>(?<name>\w+): (?<value1>\d+) (?<value2>\w+)? ?(?<value3>\d+))\s*)+

Das Ganze angewendet auf

foo: 12 test 56
bar: 21 65

Das Problem ist, ich möchte jetzt durch die einzelnen Zeilen (bzw. data-captures) gehen und die Werte der value-Gruppen (aber nur in der Zeile) den Namen zuordnen, aber natürlich bringt mich ein einfaches

foreach (Capture c in m.Groups["data"].Captures)

nicht weiter. Auch "Zählen" ist nicht möglich, da ich Gruppen habe, die nicht vorkommen müssen (wie value2; es ist noch etwas komplizierter).

Kurz gesagt etwas wie (theoretisch): c.Groups["value1"].Captures 🙂

Was kann ich denn da machen?

Ich hoffe ihr könnt mich grade vom Holzweg runterholen, ich komme mir vor als hätte ich noch nie einen Regex benutzt. 🙂

Übrigens habe ich natürlich schon Lösungen wie die Gruppenanzahl immer gleich machen durch evtl. leere Gruppen (value2) oder den Regex aufsplitten im Kopf aber ich bin mir 100%ig sicher, dass es so geht.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo cynos,

ich denke der Holzweg ist das letzte Plus in dem Pattern. Lass es weg und verwende Regex.Matches. Dann kannst du die Caputeres vergessen und alles mit den Gruppen machen.

herbivore

C
cynos Themenstarter:in
7 Beiträge seit 2004
vor 17 Jahren

Hm Mist, ich hätte erwähnen sollen, dass vor dem eigenltich Ausdruck noch einiges anderes kommt, was sich nicht wiederholt. In sofern muss das + da schon sein. Ich gehe schon jedes Match aus der MatchCollection durch. Vielleicht ist der Ausdruck doch zu lang und ich sollte ihn aufteilen...

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo cynos,

hm, ich denke aufteilen ist immer noch der beste Weg. Ansonsten hilft dir bestimmt der On-the-fly Regex-Tester: Regex-Lab. Siehe auch Regex Wiederholungen

herbivore

C
cynos Themenstarter:in
7 Beiträge seit 2004
vor 17 Jahren

Hallo herbivore,
durch das Aufteilen habe ich das Problem, dass dadurch die Erkennungsgenauigkeit und die Flexibilität sinken, da ich jedes mal mehrere Ausdrücke ändern müsste.

Ich habe das Beispiel nochmal verändert, prinzipell ist es jetzt das, was ich suche. Ich hoffe es ist noch verständlich, des folgende Text wahrscheinlich umso weniger.
Wie schon gesagt ist der Input nicht wirklich so streng formatiert wie unten, sondern wesentlich flexibler, deshalb hilft mir eine Veränderung am Ausdruck nicht viel.
Würde ich das ganze jetzt teilen, also im Beispiel in einen Ausdruck der nur jedes Datum + alle dazugehörigen Zeilen mit Namen findet und ein weiteres Ausdruck der dann die einzelnen Zeilen liest (basierden auf dem Capture vom ersten Ausdruck), hätte ich das Problem dass ich letzteren Ausdruck in den ersten teilweise (z. B. non-capturing) einbauen muss, um zu garantieren, dass auch die Datensätze korrekt erkannt werden.
Da das aber mehrmals der Fall ist, müsste ich im schlechtesten Falle für einen Ausdruck gesamt drei ändern.

Input:

2006-08-21
foo: 12 test 56
bar: 21 65
2006-08-22
foo: 22 test 56
bar: 22 65
2006-08-23
foo: 23 56
bar: 32 65

Mal ganz pseudo im Browser:

Regex r = new Regex(@"(?<date>\d{4}-\d{2}-\d{2})\s+(:question:(?<data>(?<name>\w+): (?<value1>\d+) (?<value2>\w+)? ?(?<value3>\d+))\s*)+");

MatchCollection mc = r.Matches(der_input_oben);

foreach(Match m in mc)
{
// Ein Match wäre z. B.
//2006-08-21
//foo: 12 test 56
//bar: 21 65

datum = m.Groups["date"].Value;

foreach(Capture c in m.Groups["name"].Captures)
{
name = c.Value;

// Wie ordne ich jetzt value2 eindeutig zu? Normale for mit iterator geht nicht, da es weniger value2s als z. B. namen gibt und value2 so ändern, dass es im Ausdruck immer da ist aber evtl. leer (für for + Iteration) nicht wirklich unübersichtlich und macht weitere Probleme (ich hatte es schon so).
}
}

Hoffe es ist nicht allzu unverständlich.

Gruß

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo cynos,

auch dein erneuter Einwand bringt mich nicht von meiner Meinung ab, das Aufteilen hier das beste ist.

herbivore

I
1.739 Beiträge seit 2005
vor 17 Jahren

@cynos:

Frage:
Erkennungsgenauigkeit: wieso sinkt die? Es wird doch nur ein "Levelsystem" eingeführt.
Flexibilität: seh ich eher anderherum. Wie meinst du das mit der Flexibilität?
Ich seh maximal ne Steigerung des Aufwandes.

C
cynos Themenstarter:in
7 Beiträge seit 2004
vor 17 Jahren

Angenommen ich teile das und wende einen zweiten Ausdruck auf die Gruppe des ersten an:

Ausdruck 1:
(?<date>\d{4}-\d{2}-\d{2})\s+(?<rows>(:question:(:question:(:question:\w+): (:question:\d+) (:question:\w+)? ?(:question:\d+))\s*)+)

Ausdruck 2:
(?<name>\w+): (?<value1>\d+) (?<value2>\w+)? ?(?<value3>\d+)

So wäre es ja fast perfekt. Ich habe im ersten Ausdruck immernoch eine feste Definition der Zeilen (anstatt einer Gruppe "bis-zum-nächsten-Datum" -> mangelnde Genauigkeit) und wende Ausdruck 2 dann auf <rows> an.
Nur das Problem ist, ändere ich den 2. Ausdruck (und das wird in meinem Fall öfter vorkommen), muss ich auch den ersten ändern. Allerdings ich werde den zweiten Ausdruck nun einfach in den ersten einbinden, auch wenn ich dann viele unnötige, benannte Gruppen habe.

Ich war jetzt nur so hartnäckig, weil ich mich wundere, dass ich gerade sowas nicht mit Regex-Magie hinbekomme, dabei habe ich wahre Wunder damit erlebt (eine 1 MB große Datei (in meinem Fall viel) parsed in unter 1 s). Ich kann mir mittlerweile fast den Fortschrittsbalken sparen. 🙂