RegEx für erste Zahl im String

OP #7543034
Lesenswert?

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:

1
string newName = Regex.Replace(oldName, pattern, m => (int.Parse(m.Value) + 1).ToString(new string('0', m.Value.Length)));

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?

#7543061
Lesenswert?

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

Beitrag #7543147 wurde von einem Moderator gelöscht.
OP #7543825
Lesenswert?

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) {
3
    try
4
    {
5
        return (int.Parse(m.Value) + 1).ToString(new string('0', m.Value.Length));
6
    }
7
    catch
8
    {
9
        return (m.Value);
10
    }
11
}, max);

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.

Gast #7544482
Lesenswert?

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.

OP #7545302
Lesenswert?

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.

#7549028
Lesenswert?

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.

OP #7549070
Lesenswert?

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.

Angehängte Dateien:
#7549115
Lesenswert?

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

Gast #7549120
Lesenswert?

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.

OP #7549161
Lesenswert?

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!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren