RegEx führende Nullen

hallo,

ich habe gerade zum Entfernen führender Nullen in einem String den Code replaceFirst("0*", "") vor mir liegen

funktioniert:

public class Test2 {

	public static void main(String[] args) throws Exception {
		System.out.println("00004300".replaceFirst("0*", ""));
		System.out.println("4300".replaceFirst("0*", ""));
	}
}

replaceFirst heißt zwar nicht ‚am Anfang des Strings‘, aber durch das 0*, was ich normalerweise für sich nicht mag,
wird auch bei fehlenden Nullen am Anfang des Strings dort ein Leerstring fürs Pattern gefunden/ gematcht,
die Nullen weiter hinten sind nicht in Gefahr

"0*" ist einfacher als "^0+" a la
java - How to remove leading zeros from numeric text? - Stack Overflow
, ob nun mit replaceAll() oder replaceFirst()

ich werde mich vor Entscheidungen zur Wahl dazwischen drücken, indem ich Pattern lieber ganz einspare,
(bei jedem replace neu kompiliert oder aufwendig statisch abgelegt),
und eine Hilfsmethode schreibe, die nach Suchchar am Anfang schaut, und einmal subString() durchführt, wenn nötig

aber allgemein gesprochen, ist die Variante mit 0* sicher, könnte man das in wichtigen Code bedenkenlos einbauen?


wie ist generell festgelegt ob ein 0* selbst von „0000“-String kein, ein oder alle Nullen nimmt?
bei RegEx <tag>.*</tag> werden glaube ich evtl. auch innere Tags gerne mal übersprungen, zuviel gemacht

ich setze lieber nur zweifelsfreie Pattern ein, a0*b ist in „a0000b“ bisher noch immer fündig geworden,
<tag>[^<]*</tag> oder so macht auch weniger Sorgen

ist sicher irgendwo genau nachzulesen wie Greedy & Co. funktionieren,
aber falls das schon wer gemacht und eine schnelle Kurzversion zum Besten geben kann, bitte :wink:

Wenn Du Dir unsicher bist schreibe einen JUnitTest, der die “gefährlichen” Strings durch das Pattern jagt. Selbst wenn die aktuellen Java-Versionen das Pattern in Deinem Sinne interpetieren kann das bei Java-9.x schon anders sein und dieser JUnitTest würde das Problem direkt nach dem Wechsel der Java-Version aufdecken.

bye
TT

Du musst da reluctant rangehen, der zweite kann “greedy” bleiben:

0*?[1-9]*

mit Group:

(0*?)[1-9]*

das Fragezeichen beutetet im Prinzip, schaue, obs mit wem anders weitergeht.

Ist 0,5 ein Problem?

Ist 0.5 ein Problem?

Regex sind IMHO aber auch gut testbar. Warum hast du keinen JUnit-Test geschrieben? Ganz im Sinne von Timothy_Tuckle.

public void testReplace() {
  assertEquals("4300", "00004300".replaceFirst("0*", ""));
  assertEquals("4300", "4300".replaceFirst("0*", ""));
  // ... weitere Grenzfälle etc.
  assertEquals("4300", clean("00004300"));
}```

So ein Test macht natürlich noch mehr Sinn, wenn man einen Parameterized-Testrunner nutzt, der dass ganze dann gegen ein oder mehreren Funktionen nutzt in denen das ganze gekapselt ist.


Ansonsten ist ein Beweis im Mathematischen Sinne auf Fehlerfreiheit von wichtigem Code sehr schwierig, teils unmöglich.

Entfernen von 0 ist die Funktionalität, wer es mit 0.5 aufruft ist selber Schuld,
höhere Funktionalität müsste entsprechend anders programmiert werden


ich weiß ja dass es aktuell geht, Unit-Test für neue Versionen hilft wenig,

gar nicht bei Programm wer weiß wo lokal unter gerade installierter Java-Version
und selbst in echter Programmierumgebung keine schöne Sache für vielleicht andere Programmierer,
größere Umstellungen, womöglich an verschiedenen Stellem im Programm unterschiedlich verwendet…


Frage war aber auch weniger Beweisbarkeit als aktuelles Gefühl,
Verwendung und Vertrauen auf Funktionieren ok oder nicht :wink:

Verknüpfung mit JUnit nehme ich aber als eine Antwort dazu, ok

mein Gefühl ist eher nicht

Dass 0* nämlich IMMER matcht ist in dem Fall zwar gewollt, trotzdem beim Lesen irgendwie seltsam - und

System.out.println(" 00004300".replaceFirst("0*", ""));

ist eigentlich auch komisch, weil schon vor dem Leerzeichen getroffen wird (und also nix passiert). Ich würde

.replaceFirst("^\\s*0*", "")

oder so was in der Art bevorzugen (nur wegen der Lesbarkeit)

so, noch die Arbeit mehrerer Tage als Microbenchmark:

nein, so lange nicht, aber bei RegEx lohnen sich Optimierungen zumindest noch theoretisch,
Faktor 20 Unterschied,
praktisch hat natürlich beides keine zeitliche Bedeutung im Programmablauf

    static int n = 500000;
    static int k = 10;
    static List<String> list = new ArrayList<>();

    public static void main(String[] args)
    {
        Random r = new Random(42);
        String zero = "";
        for (int i = 0; i < k; i++)
        {
            zero += "0";
            String st = String.valueOf(r.nextInt(1000000));
            list.add(st);
            list.add(zero + st);
        }
        System.out.println(list);

        test1();
        test2();
        test1();
        test2();
    }

    static void test1()
    {
        long time = System.currentTimeMillis();
        int sum = 0;
        for (int i = 0; i < n; i++)
            for (String st : list)
                sum += st.replaceFirst("0*", "").length();
        System.out.println("time1 : " + (System.currentTimeMillis() - time) + ", " + sum);
    }

    static void test2()
    {
        long time = System.currentTimeMillis();
        int sum = 0;
        for (int i = 0; i < n; i++)
            for (String st : list)
                sum += cleanPrefix(st, '0').length();
        System.out.println("time 2: " + (System.currentTimeMillis() - time) + ", " + sum);
    }

    static String cleanPrefix(String st, char c)
    {
        if (st == null) return null;
        int length = st.length();
        int min = 0;
        for (; min < length; min++)
            if (st.charAt(min) != c) break;
        if (min == 0) return st;
        return st.substring(min);
    }
}

Hätte ich auch so geschrieben, mit charAt usw.

Du kannst, weil Zeichen konstant, 1 Parameter einsparen, mit Konstante vergleichen, das dürfte ein paar Nanosekunden rausholen. :slight_smile:

Edit: Und mit oben meinte ich folgendes:

(.?)[1-9]

group(1).start() usw. Das trimmt (Leerzeichen) gleichzeitig.

Also wenn es um Zahlenwerte aus Strings geht, ist mMn RegEx stets eine ganz schlechte Idee, zumal es ja fertige Parser in Java dafür gibt: String-/Streamtokenizer und diverse Parser-Methoden in den Wrapper-Klassen.

parsen musst du ohnehin, ein Matcher kann immer nur einen String liefern. hrhrhr :wink:

Hallo warum nicht einfach?

String w = "00040";
int parse = Integer.parseInt(w);
w = String.valueOf(parse);

Grüße

etwas zu beschränkt in der Länge, auch Long, und erlaubt keine vereinzelten Sonderzeichen,
bei ganz 0 wird eine 0 übrig gelassen, ziemlich frech :wink:

durchaus aber erwähnenswert, ja

Sry, binäre Suche?

Ich hab fälschlicherweise angenommen, d. hinten nicht gaaanz viele Nullen vorkommen. boing