Hi,
ich habe einen String in dem beliebig Zahlen vorkommen können, z.B.:
* X001_Y1
* 001X1
* 001X1Y
* X_1_Y
Ziel ist es Zahlen davon nach bestimmten Kriterien zu finden und zu
inkrementieren.
Kriterien können z.B. sein:
* Alle Zahlen (pattern = @"\d+")
* Zahl am Anfang vom String
* Zahl am Ende vom String
* Erste Zahl im String
* Letzte Zahl im String (pattern = @"\d+(?!.*\d)")
* Zahl zwischen bestimmten Zeichen (pattern =
@"(?<=[_])[0-9]+(?=[_]|$)";)
Einige Kriterien möchte ich gerne als Vorauswahl anbieten.
Der Poweruser soll aber auch eigene Patterns definieren können.
Im Code sieht das dann so aus:
Nun scheitere ich aber schon am Pattern für die Erste Zahl im String.
Ich finde nur Lösungen mit Gruppen, wie: pattern = @"^[\D]*(\d+)"
Das findet aber auch im ersten Beispiel X001, was natürlich nicht
funktioniert.
Weiß jemand Rat?
Thomas W. schrieb:> Nun scheitere ich aber schon am Pattern für die Erste Zahl im String.
[...]
> Weiß jemand Rat?
Lerne selber zu denken! Lerne Doku zu lesen (im konkreten Fall: die Doku
zu den verwendendeten REs)
Also normalerweise wäre das
Non capturing group ?:
^(?:[\D])(\d+)
Wenn das nicht mit Replace funktioniert ist das weil das Ergebnis in der
Hauptgruppe trotzdem angezeigt wird.
Dann müsste man das mit einem Workaround angehen.
Ja, diese Seite habe ich auch genutzt und habe mit dem Pattern wie oben
beschrieben ein Match "X001" und eine Group "001".
Ich möchte aber nur die Zahl haben.
Thomas W. schrieb:> ein Match "X001" und eine Group "001".> Ich möchte aber nur die Zahl haben.
Ich kenne es von einigen Sprachen so, dass dort Match und Search
unterschieden wird, wobei Match immer den gesamten String matchen muss,
Search dagegen matches von Teilstrings sucht und findet.
Aber selbst wenn es ein Search in deiner unbekannten Umgebung nicht
gibt: Warum nutzt du nicht den match der Group?
Ansonsten hilft vielleicht ein non-greedy skip (".*?")?
LG, Sebastian
Ich nutze C#.
Danke für den Input (Submatches, Search, non-greedy), ich werde mich da
mal einlesen.
Am Ende müssen halt alle Patterns mit dem selben Code funktionieren.
Mit try{} catch{} fange ich fehlerhafte Patterns vom User ab. Dann
passiert halt einfach gar nichts.
Ja, das ist mir auch schon aufgefallen.
Anscheinend kann man in .NET diese Modifikatoren aber nur als Parameter
übergeben und nicht mit im Pattern einbauen. Dann wäre das in der Tat
eine gute Lösung.
\d+ alleine soll ja auch schon alle Nummern matchen.
Ok, auch in .NET lassen sich manche Modifier im Pattern übergeben, aber
leider keiner für "Global":
https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options
Das Problem ist, dass Lookbehind keine dynamischen Pattern erlaubt.
Lookahead kann das, und das nutze ich dann so für die letzte Nummer im
String:
\d+(?!.*\d)
Für die erste Nummer wäre das entsprechend:
(?<!\d.*)\d+
Das geht aber nicht, weil Das Lookbehind Pattern durch das "*" dynamisch
wird.
Also bleiben nur noch kompliziertere Pattern mit Gruppen, die aber
spezifische Outputs generieren welche ich nicht generisch auswerten
kann.
Es muss aber generisch bleiben, da ja auch alle anderen oben genannten
Kriterien funktionieren müssen.
Und darüber hinaus alle möglichen anwendugsspezifischen Pattern der
User.
Meine Lösung ist nun wie folgt.
* Es sind nur Patterns ohne Gruppen erlaubt
* Dafür gibt es eine weitere Variable "max" mit der Anzahl der Matches
die maximal ersetzt werden sollen
Der Code dazu schaut dann so aus:
1
Regex reg = new Regex(pattern);
2
string result = reg.Replace(input, delegate (Match m) {
Nur die erste Zahl inkrementiert man dann so:
* pattern = @"\d+"
* max = 1
Alle Zahlen so:
* pattern = @"\d+"
* max = -1
Und die letzte Zahl so:
* pattern = @"\d+(?!.*\d)"
* max = -1
Auch alle anderen oben gelisteten Kriterien lassen sich so abdecken.
Thomas W. schrieb:> Weiß jemand Rat?
Ja. Sowas programmiert man selbst. Mit RegEx suchst du dir den Wolf in
der Doku und musst dauernd ausprobieren. Spätestens nach einer Woche,
hast du den kryptischen Kram größtensteils wieder vergessen.
Für sowas nehme ich gerne Pascal und Structorizer für die
Nassi-Shneiderman Diagramme. Änderung im Diagramm verändert Code und
umgekehrt. Das geht ratzfatz und du blickst auch noch nach Jahren durch,
kannst es sogar in andere Sprachen übersetzen. Da kommt RegEx nicht mit.
Verstehe ich nicht.
Soll ich nun eine eigene RegEx Syntax für meine User designen und die
passende Engine dafür aus Nassi-Shneiderman Diagrammen generieren
lassen?
Ich gebe dir ja in soweit Recht, dass ich RegEx jedes Mal wenn ich es
benötige vorher erneut lernen muss, weil ich den kryptischen Kram nach
spätestens einer Woche größtenteils wieder vergessen habe.
Aber RegEx ist standardisiert und in jeder Sprache verfügbar.
Thomas W. schrieb:> Aber RegEx ist standardisiert und
Wenn dem wirklich so wäre, dann bräuchte regex101.com wohl kaum 8
Flavours zur Auswahl anbieten, oder?
> in jeder Sprache verfügbar.
Aber jede Implementierung bringt eben leider ihre eigenen Macken und
Sonderlocken mit, auch wenn die Gemeinsamkeiten natürlich dominieren.
Thomas W. schrieb:> Soll ich nun eine eigene RegEx Syntax für meine User designen
Nö, das ist eher keine gute Idee, außer Du machst es aus Spaß am
Syntax-Design und Doku dafür schreiben; vor allem letzteres ist dabei
ein nicht zu unterschätzender Aufwand und wird trotzdem zwangsläufig
mickrig bleiben im Vergleich zur bereits online Verfügbaren Breite an
Doku zu .NET RegEx.
Was Du aber ggf. machen könntest, wäre den passenden syntactic-sugar für
Deine konkrete Anwendung zu schaffen um z.B. Deinen max-Parameter direkt
in die RegEx schreiben zu können.
>> Aber RegEx ist standardisiert und> Wenn dem wirklich so wäre, dann bräuchte regex101.com wohl kaum 8> Flavours zur Auswahl anbieten, oder?
Vermutlich weil es 8 Standards gibt ;)
> Was Du aber ggf. machen könntest, wäre den passenden syntactic-sugar für> Deine konkrete Anwendung zu schaffen um z.B. Deinen max-Parameter direkt> in die RegEx schreiben zu können.
Hatte ich auch kurz drüber nachgedacht und schnell wieder verworfen.
Ich möchte nicht den 9ten Standard schaffen... ;)
Mit der aktuellen Lösung bin ich sehr zufrieden.
So wie Screenshot ist es voreingestellt.
Der versierte User kann das beliebig anpassen.
Die aktive Methode wird dann per Drop-Down im Ribbon ausgewählt.
Thomas W. schrieb:> Für die erste Nummer wäre das entsprechend:> (?<!\d.*)\d+> Das geht aber nicht,
Hmm... also auf regex101.com funktioniert das bestens.
> weil Das Lookbehind Pattern durch das "*" dynamisch wird.
In der von Dir verlinkten Doku finde ich keinen Hinweis darauf, daß es
für Lookbehind-Pattern andere Einschränkungen als für Lookahead-Pattern
geben würde; im Gegenteil, in allen vier Varianten findet sich die
Aussage:
1
Here, subexpression is any regular expression pattern.
Bei meinen eigenen Versuchen habe ich erstmal folgendes Pattern
gebastelt, das wunderbar geklappt hat:
1
(?<=^\D*)\d+
Erst dann ist mir aufgefallen, daß hier ebenfalls der * im
Lookbehind-Pattern vorkommt, was lt. Deiner Aussage nicht sein dürfte.
Jedenfalls liefern beide Pattern bei jedem Deiner vier Teststrings
jeweils die erste Zahl als einzigen Match; zumindest auf regex101.com
Thomas W. schrieb:>>> Aber RegEx ist standardisiert und>> Wenn dem wirklich so wäre, dann bräuchte regex101.com wohl kaum 8>> Flavours zur Auswahl anbieten, oder?> Vermutlich weil es 8 Standards gibt
Mehr sogar. Ich weiß gar nicht, wie oft wir schon mit unserem
Projekleiter darüber diskutieren mussten.
Ganz ätzend sind regex patterns in OpenApi Schemata, wenn z.B. eine Java
EE Anwendung Dienste bereit stellt, die fünf andere Sprachen/Frameworks
konsumieren, mit drei Test Tools manuell aufgerufen werden und ein
Swagger Validator via Ci/Cd Pipeline überprüft. Ein herrliches Chaos ist
das.
Dann diskutiere mal mit dem Projektleiter darüber, welches Pattern nun
das "offizielle" für Email Adressen sein soll. Oder für Benutzernamen
mit Unicode.
Michi S. schrieb:> Hmm... also auf regex101.com funktioniert das bestens.
Oh mann... das ist jetzt natürlich peinlich.
Bei all meinen Versuchen auf regex101 bin ich nie auf die Idee gekommen
den Flavor umstellen.
Und in der Tat, in der .NET Implementierung geht es.
Bei den meisten anderen (und auch bei der Vorauswahl) geht es aus
besagtem Grund nicht.
Gerade auch in meiner Anwendung getestet: Funktioniert dort natürlich
auch.
Damit hätte ich mir den kompletten Thread hier sparen können, denn
dieses Pattern mit Lookbehind war auch mein erster Versuch.
Jetzt überlege ich, ob ich die MaxMatches Eigenschaft wieder raus werfe
und auf den ursprünglichen Ansatz zurück gehe.
Vermutlich mache ich das.
Danke!