Regex-Problemchen


#1

Ich stehe gerade auf dem Schlauch: Ich habe einen String, der sowohl "\." wie auch nackte "." (ohne Backslash davor) enthalten kann. Jetzt will ich jeden "\." in einen "." und jeden "." in einen "\" übersetzen.

Da das irgendwie “kreuzweise” ist, ist mir nur eine Lösung mit drei replace- Operationen eingefallen, wo ich das eine Pattern erst einmal in ein Hilfszeichen übersetze, dann das zweite Pattern direkt, und dann das Hilfszeichen (so ähnlich wie man zwei Variablen über eine Temp-Variable tauscht):

String neu = alt.replace("\\.", "§")
                .replace('.','\\')
                .replace('§','.');

Geht das irgendwie mit zwei Operationen, oder sogar irgendwie “simultan”? Ist nicht schlimm, wenn ich bei meiner hässlichen Lösung bleiben muss, aber es interessiert mich wirklich, ob es nicht doch besser geht.


#2

Hi,
Wenn ich die Doku richtig verstehe, dann würde die Lösung so aussehen. Ob das jetzt schöner ist, musst du entscheiden:

public static void main(String[] args) {
    Pattern p = Pattern.compile("\\\\\\.|\\.");
    Matcher m = p.matcher("foo.bar\\.baz");
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        if (m.group().equals(".")) {
            m.appendReplacement(sb, "\\\\");
        } else if (m.group().equals("\\.")) {
            m.appendReplacement(sb, ".");
        }
    }
    m.appendTail(sb);
    System.out.println(sb.toString());
}

#3

Danke, sehr interessant! Hübscher ist es ehrlich gesagt nicht, aber wenn ich das Risiko vermeiden kann, dass mein “Ersatzzeichen” doch im String vorkommt, dann nehme ich lieber diese Version.


#4

Im Zweifelsfall kannst du bei deiner ersten Lösung ja auch in "### DIESER UNSINN KOMMT NICHT VOR ###" ersetzen und wieder zurück.

Man sieht wieder mal, wie schade es ist, dass Java in Zeichenketten die doppelten Backslashes verwendet und dass es nicht Perls s~regex~ersetzung~ kennt. Ich hab mir zwar eine kleine Klasse dafür geschrieben, aber eingebaut wäre es schöner.

C++ kennt wiederum nicht interpretierte Zeichenketten mit Klammeraffen davor, etwa @"\d+\.".


#5
"### DIESER UNSINN KOMMT NICHT VOR ##\."

Wird zu

"### DIESER UNSINN KOMMT NICHT VOR ##### DIESER UNSINN KOMMT NICHT VOR ###"

Wird zu

".## DIESER UNSINN KOMMT NICHT VOR ###"

obwohl

"### DIESER UNSINN KOMMT NICHT VOR ##."

erwartet wurde und selbst

"### DIESER UNSINN KOMMT NICHT VOR ##\.".contains("### DIESER UNSINN KOMMT NICHT VOR ###") == false

was einen Theoretisch hätte schützen können.

Aber dies ist wohl nur rein theoretischer Natur.


#6

verläßliches Ersetzen ist ja schon ein Thema, welches bei solchen String-Operationen bekannt ist,

das hier sollte gehen, ob das Optimum oder ein offiziellerer Weg vorhanden…

    String alt = " abc\\...\\...\\\\\\..cd§e§§1§§§2§§.§\\.§";
    String neu = alt.replace("§", "§1")
                    .replace("\\.", "§2")
                    .replace('.', '\\')
                    .replace("§2", ".")
                    .replace("§1", "§");
    System.out.println(alt);
    System.out.println("   " + neu);
    // abc\...\...\\\..cd§e§§1§§§2§§.§\.§
    //    abc.\\.\\\\.\cd§e§§1§§§2§§\§.§

Effizienzvorteil der Schleifenvariante steigt freilich…,
noch mehr vielleicht ganz manuell mit Indexof, StringBuilder usw., falls nur feste Texte zu ersetzen


#7

So ähnlich wie es ButAlive macht funktioniert das auch in C[meine neue Tastatur hat kein Hashtag merke ich gerade], bloß dass sich die Funktion in Matcher befindet. Das wäre auch mein Vorschlag - Matcher zu erweitern. Am besten sogar mit Lambdas (return Replacement_String_For_Match).