String auf bestimmte Zahlenfolge abfragen.

Javaman91

Bekanntes Mitglied
Hallo,

ich habe eine Frage zur Erkennung einer Zahlenfolge in einem String.

Wie kann ich mit der "matches" Methode folgende Zahlen erkennen in einem String erkennen?
Die Zahlen verändern sich aber immer wieder.

Java:
String text = "05,12";

Ich würde gerne die "05" erkennen und dann die "12", aber ohne der split-Methode.

Beim nachlesen im Netz, habe ich sowas ähnliches wie:

Code:
String text.matches(\\d+..... usw.);

gefunden.
Ich weiß leider nicht, was ich in die Klammern schreiben muss.

Darf ich euch um eure Hilfe bitten?

SG
 
Was Du da ansprichst, sind reguläre Ausdrücke.

Diese kannst Du Online testen:

Die einzelnen Möglichkeiten werden da auch erläutert. Aber es macht Sinn, sich da richtig einzulesen über Einführungen oder Tutorials.

Was Du da an Elementen hast:
\d steht für eine Ziffer und ist gleichbedeutend zu [0-9]
Das + steht für 1 bis beliebig viele Elemente ... \d steht also für eine bis hin zu beliebig vielen Ziffern
Wenn Du also einen String hast, der zwei Ziffern ein Komma und dann wieder zwei Ziffern hat, dann kannst Du das so darstellen:
\d{2},\d{2}

Das {2} steht dabei für genau 2 Vorkommen des Elements

Wenn davor und danach nicht kommen darf, dann kann man da noch ein ein ^ davor und $ dahinter setzen (Steht für Anfang und Ende der Zeile)

Je nach Implementation muss man auch aufpassen - es gibt implementierungen, die das direkt mit sich bringen. Wenn Du dann beliebige Zeichen davor oder danach erlauben willst, dann könntest Du .* für beliebige Zeichen nutzen. Oder wenn es beliebige Zeichen aber kein Ziffer sein darf, dann wäre es [^0-9] oder \D

Ansonsten wäre bei sowas oft auch noch die Nutzung von Gruppen interessant. Gerade wenn man Informationen herausschneiden möchte.

Aber Du merkst schon die enorme Komplexität. Daher erneut der Hinweis auf Tutorials, die einem das alls strukturiert beibringen.
 
So könnte es Codetechnisch gehen:

Java:
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    String text = "05,12";
    int[] numbers = extractNumbers(text);
    if (numbers != null) {
      System.out.println("First number: " + numbers[0]);
      System.out.println("Second number: " + numbers[1]);
    }
  }

  public static int[] extractNumbers(String input) {
    Pattern pattern = Pattern.compile("(-?\\d{2}),(-?\\d{2})");
    Matcher matcher = pattern.matcher(input);
    if (matcher.find()) {
      try {
        int firstNumber = Integer.parseInt(matcher.group(1));
        int secondNumber = Integer.parseInt(matcher.group(2));
        return new int[] {firstNumber, secondNumber};
      } catch (NumberFormatException e) {
        throw new RuntimeException("Unexpected number format, should never happen.", e);
      }
    }
    return null;
  }
}

Ich wusste jetzt nicht, ob auch Vorzeichen dabei sind.
 
Was mir bei dem Beispiel @grrr auffällt:
  • "5,12" oder "100,20" werden nicht erkannt.
  • RuntimeException zu werfen ist ein Unsitte, ich begründe das nicht weiter, kann man überall nachlesen.

Regex ist für so eine kleine Sache ein ganz schöner Overhead. Einem Anfänger würde ich, bei einer einmaligen Prüfung, folgendes raten:
Java:
public static int[] extractNumbers(String input) {
    try (Scanner sc = new Scanner(input).useDelimiter(",")) {
        return new int[] { sc.nextInt(), sc.nextInt() };
    } catch (Exception e) {
        return null;
    }
}
Anfänger sind oft auf Scanner trainiert.

Simple und effizient wäre:
Java:
public static int[] extractNumbers(String input) {
    String[] parts = input.split(",");
    if (parts.length != 2) return null;

    return new int[] {
        Integer.parseInt(parts[0]),
        Integer.parseInt(parts[1])
    };
}
Ob im Fehlerfall null oder eine Exception zurückkommt kannst du leicht ändern. Das Rückgabe-Array habe ich mal so übernommen, vermutlich wäre ein Record die besser Wahl:

Java:
public record NumberPair(int first, int second) {}

public static NumberPair extractNumbers(String input) {
        // Regex flexibler (\\d+ statt \\d{2})
        Pattern pattern = Pattern.compile("(-?\\d+),(-?\\d+)");
        Matcher matcher = pattern.matcher(input);

        if (matcher.find()) {
            return new NumberPair(
                Integer.parseInt(matcher.group(1)),
                Integer.parseInt(matcher.group(2))
            );
        }
        return null;
    }

Sauberes Vorgehen ohne null:
Java:
public static Optional<NumberPair> extractNumbers(String input) {

    Pattern pattern = Pattern.compile("(-?\\d+),(-?\\d+)");
    Matcher matcher = pattern.matcher(input);

    if (matcher.find()) {
        return Optional.of(new NumberPair(
            Integer.parseInt(matcher.group(1)),
            Integer.parseInt(matcher.group(2))
        ));
    }
    return Optional.empty();
}
 
RuntimeException zu werfen ist ein Unsitte, ich begründe das nicht weiter, kann man überall nachlesen.
Der Fall tritt eh nicht ein, war nur zur Verdeutlichung

Regex ist für so eine kleine Sache ein ganz schöner Overhead. Einem Anfänger würde ich, bei einer einmaligen Prüfung, folgendes raten:
Das will er aber nicht

"5,12" oder "100,20" werden nicht erkannt
Das war aber auch nicht seine Fragen

Wenn ich ihn verstanden habe, sind die Zahlen immer zweistellig und mit führender 0

Sauberes Vorgehen ohne null:
Käme nicht durch mein Review, weil es eben nicht "sauber" ist, sondern unnötige Variablen enthält
 
Käme nicht durch mein Review, weil es eben nicht "sauber" ist, sondern unnötige Variablen enthält
Variable != Instanz. Vermutlich stört Dich die zusätzliche Instanz von Optional ...Die Diskussion zu Optional bringe ich aber jetzt nicht erneut und wenn es Dich wirklich interessiert, dann kannst Du ja z.B. bei Effective Java von Joshua Bloch den Abschnitt nachlesen ... Oder einfach mal die KI befragen - ChatGPT kann dazu auch einiges sagen ...
 
Na ja, zu sagen, die null-Rückgabe wäre falsch und man müsse in jedem Fall `Optional`s verwenden, ist schon ein starkes Stück...

Und alle möglichen Integer sollte man nicht via RegEx parsen, das ist ein Anti-Pattern, deshalb macht es Sinn, zum Beispiel auf 2 Stellen zu begrenzen.

Hier noch einmal ohne die redundante Fehlerbehandlung:

Java:
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    String text = "05,12";
    int[] numbers = extractNumbers(text);
    if (numbers != null) {
      System.out.println("First number: " + numbers[0]);
      System.out.println("Second number: " + numbers[1]);
    }
  }

  public static int[] extractNumbers(String input) {
    // two-digits number pattern with leading zeros and eventually a minus sign
    Pattern pattern = Pattern.compile("(-?\\d{2}),(-?\\d{2})");
    Matcher matcher = pattern.matcher(input);
    if (matcher.find()) {
      int firstNumber = Integer.parseInt(matcher.group(1));
      int secondNumber = Integer.parseInt(matcher.group(2));
      // this parses cannot fail because of the ensured two-digits pattern
      return new int[] {firstNumber, secondNumber};
    }
    // no match found, return null, indicating failure
    return null;
  }
}
 
Na ja, zu sagen, die null-Rückgabe wäre falsch und man müsse in jedem Fall `Optional`s verwenden, ist schon ein starkes Stück...
Das war nicht seine Aussage. Evtl. liest Du seine Aussage noch einmal. Er hat es lediglich als ein "saubere Vorgehen ohne null" bezeichnet. Wenn man die Intention bei der Vermeidung von null sieht, dann ist es noch nicht einmal eine Bewertung des null Ansatzes. Es ist lediglich die saubere Lösung, wenn man keine null Rückgabe haben will....

Aber selbst eine Bevorzug des Optional Ansatzes wäre ok. Das wäre zum einen seine Meinung und zum anderen eine sehr weit verbreitete Meinung, die u.a. vom Java Framework benutzt wird (an Stellen wo möglich ohne die vorhandene API zu brechen).
Und alle möglichen Integer sollte man nicht via RegEx parsen, das ist ein Anti-Pattern,
Sorry, was willst Du hier mit Anti-Pattern? Es wird eine Anforderung geben. Und diese Anforderung wird umgesetzt. Da spielen keinerlei Pattern oder Anti-Pattern rein: Die Anforderung ist die Anforderung. Wenn die Anforderung wäre, dass da ganze Integer sein können, dann kannst Du keine Aussage machen a.la. "das ist ein Anti-Pattern". Und hier kennen wir keine genauen Anforderungen, Du selbst hast da einfach so noch das Minuszeichen mit ins Spiel gebracht ... Und einfach etwas hingeklatscht ohne jede Erläuterung.

Auch das Parsen in Integer ist eine Annahme von Dir. Evtl. spielen führende Nullen eine Rolle. Er spricht halt ausdrücklich davon, dass er "05" ermitteln möchte. Aber das sind Spekulationen und die möchte ich nicht einmal voran treiben ...
 
@KonradN hat eigentlich schon alles gesagt. Wenn ich spekulieren sollte, dann würde ich mich eher auf: aber ohne der split-Methode. beziehen. Also es soll kein Array zurückgegeben werden. Das ist zurecht ein wichtiger Punkt. Im ersten Moment scheint die Verwendung von Arrays eine einfache Lösung zu sein. Arrays sind quasi nur eine Liste von Werten, es ist nicht immer offensichtlich woher sie stammen und ob sie die Anforderungen des Kontextes wirklich erfüllen. Natürlich ist das in einem Programm von 20 Zeilen leicht überschaubar - aber stell die vor, man arbeitet an einer Software mit 20 anderen Entwicklern über mehrere Jahre ...
Objekte dagegen erlauben eine strikte Typisierung und legen ihre Eigenschaften komplett offen (Klassen, Interfaches, ...), was Fehler zur Laufzeit oder Missverständnisse bei Co-Entwicklern minimiert. Zuletzt spielt auch die Zeit eine Rolle. Mir selbst wurde kürzlich eine Software zur Wartung vorgelegt, welche ich 2013 ausgeliefert habe. Der Programmcode ist quasi für mich fremd. Ich erinnere mich nicht buchstabengetreu daran, was ich 2013 mal verzapft habe. Wenn da jetzt lauter so Arrays drin wären - dann gute Nacht ...
 
Und einfach etwas hingeklatscht ohne jede Erläuterung.
Ja, weil das Problem trivial ist.

dann ist es noch nicht einmal eine Bewertung des null Ansatzes
Natürlich war das eine bewertende und abwertende Aussage von ihm, und das lasse ich mir eben nicht gefallen. 🤷‍♂️

was willst Du hier mit Anti-Pattern?
Weil, gesetzt dem Fall, man wollen alle möglichen ints über ein RegEx parsen, dies nun mal ein Anti-Pattern ist - sprich die Anforderungen den Wertebereich zwingend einschränken müssen. Das muss ich dir doch nicht weiter erklären?

Du selbst hast da einfach so noch das Minuszeichen mit ins Spiel gebracht
Ja, und zusätzlich noch eine redundante Fehlerbehandlung, die ich später wieder entfernt habe. Ich gehe aber stark davon aus, dass der Fragesteller selber erkennen kann, ob er das Vorzeichen brauch oder nicht.
 
Ja, weil das Problem trivial ist.
Jemand, der bisher keine RegEx kannte, findet diese trivial? Sorry Tobias, das kann nicht dein Ernst sein ...
Natürlich war das eine bewertende und abwertende Aussage von ihm, und das lasse ich mir eben nicht gefallen. 🤷‍♂️
Sorry, aber wenn Du sachliche Auseinandersetzungen als pers. Angriff wertest, dann solltest Du endlich aufhören, immer neue Konten anzulegen. Die Aussage wurde klar erläutert, Deine Person war nie irgendwie bewertet. Daher an dieser Stelle noch einmal die Ausdrückliche Warnung, dass Du Dich zusammen reisst, denn dies ist keine Diskussion. Sonst beschränkt sich Deine Teilnahme vor allem an der Erstellung immer neuer Accounts (die dann zeitnah gesperrt und alle Beiträge gelöscht werden).

Weil, gesetzt dem Fall, man wollen alle möglichen ints über ein RegEx parsen, dies nun mal ein Anti-Pattern ist - sprich die Anforderungen den Wertebereich zwingend einschränken müssen. Das muss ich dir doch nicht weiter erklären?
Sorry, aber wenn die Anforderung eben ein int ist? Ich habe doch deutlich gemacht, dass die Anforderung zählt. Und ganz nebenbei: Oft ist eine anschliessende Validierung deutlich einfacher und besser. Zum einen hält es den Regulären Ausdruck einfacher und damit in der Regel auch performanter und zum anderen ist es deutlich lesbarer (gerade komplexe reguläre Ausdrücke sind oft nicht intuitiv lesbar).

So ist es durchaus üblich, dass wir den Formatierungen nachgehen um die Struktur zu erfassen. Dann werden die Werte im einzelnen später geprüft ...

Dein Anti-Pattern ist also kein Anti-Pattern. Natürlich grenzt man ein, soweit sinnvoll und einfach möglich, aber das ist kein Anti-Pattern.
 
Dann darf man den Anforderungstellenden mit fug und recht als Idioten bezeichnen. Aber ich lasse mich auf diese Spielchen nicht mehr ein, wenn hier schon wieder der letzte Bullshit als richtig dargestellt werden soll. Alles Gute 👋
Sorry, aber du kannst Dir so eine Anforderung nicht vorstellen? Niemals etwas gebaut, in dem Du int Werte verarbeiten musstest? Wo der ganze Zahlenraum erlaubt war? Thema Datenbanken? Simulation von CPU oder ganzen Rechnern ... Aber schön, dass Du das Forum freiwillig verlassen willst. Bitte bleibe dabei und erstelle. nicht schon wieder neue Accounts...

Edit:
  • Jeder sollte sich irgendwelche Szenarien vorstellen können, wo integer Werte (ganzer Zahlenbereich) vorkommen können.
  • Jeder kann sich irgendwelche Szenarien vorstellen können, wo die oben erdachten Werte plötzlich irgendwo, irgendwie auftauchen (z.B. Logfile oder so)
  • Niemand behauptet, dass Reguläre Ausdrücke dann unbedingt die beste Wahl sind... Aber es geht nur die generelle Möglichkeit einer solchen Anforderung. Und nur das war mein Punkt. (Wobei die Anforderung einfach sein kann: Lehrer oder

Generell ist das aber Off Topic. Es geht um eine Frage gezielt zu Regulären Ausdrücken. Und eine durch Tobias erzwungene Off Topic Diskussion ist einfach unsinnig und wird von mir aktuell nicht geduldet. Ich möchte mich hier explizit bei den Anderen entschuldigen, die sich hier beteiligen wollten und deren Post von mir auch gelöscht wurde. Ich hoffe hier aber auf euer Verständnis, denn ihr werden vermutlich ebensowenig Sinn in einer solchen Diskussion mit unserem Tobias sehen.
 
Zuletzt bearbeitet:
Eigentlich finde ich es schade, dass es etwas untergegangen ist, dass das Pattern "(-?\\d+),(-?\\d+)" möglicherweise suboptimal ist. Will man wirklich ein int erhalten, dann könnte es zu einer NumberFormatException kommen. Wenn man damit leben kann ist alles gut, andernfalls muss man evtl. einen geeigneteren Datentyp wählen.
Andererseits war gefragt: Erkennung einer Zahlenfolge in einem String - es könnte also sein, dass der int-Wert gar nicht interessiert, eine Zahlenfolge in einem String sagt nichts über den Datentyp - also könnte auch ein boolean die Bedingung erfüllen.
 
Eigentlich finde ich es schade, dass es etwas untergegangen ist, dass das Pattern "(-?\\d+),(-?\\d+)" möglicherweise suboptimal ist.
Nicht, dass man mich missversteht: Man kann diese Pattern alle gerne diskutieren. Gegen eine sachliche Diskussion spricht absolut nichts, vor allem falls der TE noch etwas mehr Kontext gibt.

Mir gehen auch einige Punkte bezüglich RegEx durch den Kopf, die ich gerne beachte und so. Aber ohne Kontext wäre das dann mehr ein schreiben eines Best Practice oder so. Daher habe ich mir das erspart.

Aber es sind ja ein paar Punkte angesprochen worden im Thread und wenn es da Bedarf gibt, dazu etwas zu schreiben, dann gerne. Ich habe jetzt den Thread nicht erneut im Detail geprüft, aber ich habe folgende Punkte im Kopf:

a) Umsetzung der Anforderungen: Wenn ein int gefordert ist, dann kann man das, was da genommen werden kann, durchaus weiter eingrenzen: die Länge, also etwas wie ein {1,10} statt einem + - aber man muss die Konsequenzen bedenken: Es müssen die Grenzen klar betrachtet werden, also es muss sicher gestellt sein, dass davor und danach keine weiteren Ziffern kommen (nicht dass es 11 Ziffern gibt und nur einfach 10 genommen werden und eins ignoriert wird).

b) exakte RegEx - keine zusätzliche nachfolgende Validierung. Das sehe ich nicht so. Beispiel: Ein RegEx für ein int ist einfach viel zu komplex - ein Bereich von -2147483648 bis 2147483647 wäre ein RegEx wie: ^-?(?:214748364[0-7]|21474836[0-9]|2147483[0-5][0-9]|214748[0-2][0-9]{2}|21474[0-7][0-9]{3}|2147[0-3][0-9]{4}|214[0-6][0-9]{5}|21[0-3][0-9]{6}|20[0-9]{7}|[1-9][0-9]{0,8}|0)$
Wenn ich mir nun einen regulären Ausdruck vorstelle, der womöglich in einem Ausdruck mehrere matches haben möchte, dann habe ich letzten Endes einen riesigen String, den man kaum noch überblicken kann ... Dann lieber deutlich kürzer halten und dann die einzelnen Gruppen-Ergebnisse noch einmal separat validieren. Das hält den Code deutlich lesbarer. Ich hatte sogar einmal ein Projekt, da habe ich dann reguläre Ausdrücke dynamisch gebaut. Das war dann eine Klasse, die dann stark vereinfach ${key} mit einem RegEx ersetzt hatte. So konnte man dann letzten Endes übersichtliche reguläre Ausdrücke haben wie ^${int}${ipv4}${ipv6}$ (Die Klasse konnte etwas mehr ... Es konnte auch eine Validierungsmethode gesetzt werden, hat Gruppen gesetzt und ausgewertet, diverse Schnittstellen angeboten (Streams API) und all sowas ... Da konnte man relativ schön einen IO Stream auslesen und Zeilenweise auswerten lassen ... und das mit sehr gut lesbaren JavaCode und die Pattern waren kurz und übersichtlich und Unit getestet (wir hatten projektspezifische Formate).

Aber - wenn ich mir die Fragestellung ansehe, dann war das alles nicht Thema des TE ..
 
Ich danke euch allen für die Hilfe!

Hab wieder einiges dazugelernt.

Folgendes habe ich in der zwischenzeit selbst rausgefunden, es gibt die Methode: "regionMatches".
Damit konnte ich es wunderbar auftrennen und es funktioniert einwandfrei.

Viele Grüße.
 
„regionMatches“ ist dafür tatsächlich eine sehr saubere Lösung. Gerade wenn man nur bestimmte Teilbereiche von Strings vergleichen will (z. B. ohne vorheriges substring()), ist die Methode effizient und übersichtlich.
 

Zurück
Oben