RegEx, wieder einmal ... :-(

  • Themenstarter Themenstarter tuxedo
  • Beginndatum Beginndatum
Status
Nicht offen für weitere Antworten.
T

tuxedo

Gast
Hallo,

ich habe folgenden String:

Code:
param1=value1;param2=Hallo\;Welt;param3=value3

Diesen will ich mit
Code:
split()
aufsplitten, so dass ich nachher einzelne key-value Paare habe.

Getrennt sind die einzelnen key-value Paare wie man sehen kann mit
Code:
;
. Der key ist vom value mit
Code:
=
getrennt. Ergänzend kommt hinzu dass ein Value durchaus ein
Code:
;
beinhalten darf. Dieses muss also mit einem
Code:
\
escaped werden.

Mein Problem ist nun:

Ich muss den String an den Stellen splitten, an denen ein
Code:
;
vorkommt, aber nicht wo
Code:
\;
steht.

In Pseudo-Regex wäre das dann:
Code:
NICHT(\);

In der ApiDoc hab ich als einziges "NICHT" das "^" Symbol in verbindung mit Character-Classes "[....]" gefunden.

Also hab ich's so probier:

Java:
[^\\];

Aber da krieg ich

Java:
Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed character class near index 4
[^\];
    ^

Habs dann mit

Java:
[^\\\\];

probiert, das compiliert, aber gibt seltsame Ergebnisse ...
Kann mir jemand auf die Sprünge helfen?

- Alex
 
seltsame Ergebnisse.., klasse Fehlerbeschreibung,

na dann eben eine seltsame Antwort:
Java:
public class Test
{
    static String st = "param2=Hallo\\;Welt;param3=value3";

    public static void main(String[] args)
    {
        find(";.{4}");
        find("t;.{4}");
        find("[^\\\\];.{4}");

    }

    static void find(String pattern)
    {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(st);
        if (m.find())
        {
            System.out.println(m.group());
        }
    }
}
 
Naja, soo einseitig kann man meinen Post ja nicht interpretieren. Ich hab ne Exception und das verursachende RegEx angegeben, sowie ein weiteres regex das die anforderung die ich beschrieben hatte nicht erfüllt.

Aber gut, ich hab mit dein Sample mal angeschaut:

Java:
;.{4}

Okay, das leuchtet mir ein, passt aber nicht. Er würde das ; nehmen und dann 4 weitere Zeichen beliebiger Art.

Java:
t;.{4}

gleiches wie beim ersten, nur dass das ganze mit einem t beginnt.

Java:
[^\\\\];.{4}

Da kommt das gleiche raus wie beim letzten?!

Vielleicht hab ich meine Anforderung an den String nicht genau genug definiert. Also nochmal im Detail:

1) Ich hab Key-Value Paare. Die String-Länge des Key und des Values sind nicht festgelegt und können somit von Paar zu Paar variieren.
2) Key und Value sind mittels
Code:
=
voneinander getrennt
2) Die Paare selbst sind mittels
Code:
;
voneinander getrennt
3) Der Value-String kann ebenfalls
Code:
;
und
Code:
=
enthalten, welche dann aber mit
Code:
\
escaped werden müssen, so dass im Value-String dann ein oder mehrere
Code:
\;
oder auch
Code:
\=
vorkommen können.


Hier mal ein Beispielcode:

Java:
public class Test
{
 
    public static void main(String[] args)
    {
        String st = "p1=va\\=l1;para2=Hallo\\;Welt;parameter3=value3";
        
        String[] pairs = st.split("[^\\\\];"); // splitte in Paare
        
        for (String pair : pairs)
        {
            System.out.println("Pair.: "+pair);
            
            String[] keyValue = pair.split("[^\\\\]="); // splitte in Key-Value
            
            System.out.println("Key..: "+keyValue[0]);
            System.out.println("Value: "+keyValue[1]);
            System.out.println();
        }
    }
}

Gewünschter Output:
Code:
Pair.: p1=va\=l1
Key..: p1
Value: va\=l1

Pair.: para2=Hallo\;Welt
Key..: para2
Value: Hallo\;Welt

Pair.: parameter3=value3
Key..: parameter3
Value: value3

Stattdessen bekomme ich aber mit obigen RegEx Werten folgendes:

Code:
Pair.: p1=va\=l
Key..: p
Value: va\=l

Pair.: para2=Hallo\;Wel
Key..: para
Value: Hallo\;Wel

Pair.: parameter3=value3
Key..: parameter
Value: value3

Wie man sieht gehen hier und da Zeichen verloren. Die Frage ist jetzt: Wie müssen die RegEx Werte aussehen damit das gewünschte Ergebnis rauskommt?

Das Rückgängig machen der Escape-Sequenzen (
Code:
\=
->
Code:
=
und
Code:
\;
->
Code:
;
) krieg ich hin. Nur das entsprechende splitten und die dafür nötige RegEx nicht :-(

- Alex
 
Zuletzt bearbeitet von einem Moderator:
Naja, soo einseitig kann man meinen Post ja nicht interpretieren. Ich hab ne Exception und das verursachende RegEx angegeben,
war kein wirklich offener Teil der Frage 😉
sowie ein weiteres regex das die anforderung die ich beschrieben hatte nicht erfüllt.
aber warum? mein Beispiel hat gezeigt, das es geht, wobei 'das' eben nicht besonders genau beschrieben war

Aber gut, ich hab mit dein Sample mal angeschaut:

Java:
;.{4}

Okay, das leuchtet mir ein, passt aber nicht. Er würde das ; nehmen und dann 4 weitere Zeichen beliebiger Art.
Ziel war hier, dass das erste, falsche ; ausgewählt wird, die 4 Zeichen um das zu erkennen
Java:
t;.{4}

gleiches wie beim ersten, nur dass das ganze mit einem t beginnt.
hier nun das zweite richtige ;, durch die recht unfaire Vorgabe des genau passenden Buchstabens

Java:
[^\\\\];.{4}

Da kommt das gleiche raus wie beim letzten?!
genau, hier nun aber mit dem allgemein-richtigen Pattern ^\\\\, welches du angezweifelt hattest,
-> also funktioniert es, das war die Aussage meines Programms

kann natürlich sein, dass du ein bestimmte Problemstellung meinst, wo das nicht dann nicht anwendbar ist


Vielleicht hab ich meine Anforderung an den String nicht genau genug definiert. Also nochmal im Detail:
[..]
die schicke split-Methode kann man mit [^\\\\]; schlecht benutzen, da sonst das Zeichen vor dem ; verschluckt wird

neues Programm:
Java:
public class Test
{
    static String st = "p1=va\\=l1;para2=Hallo\\;Welt;parameter3=value3";

    public static void main(String[] args)
    {
        find("[^\\\\];");
    }

    static void find(String pattern)
    {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(st);
        while (m.find())
        {
            System.out.println(m.start());
        }
    }
}
liefert 8 + 28, mit diesen beiden Infos kann mit subString usw. zum Ziel kommen,
evtl. noch Spezialfälle beachten, z.B. ; am Anfang als erstes Zeichen


-----

vielleicht kann man alternativ zu subString auch irgendwas mit den Matcher-Methoden basteln,
die Original-split-Methode, die auch String.split() benutzt, ist
Java:
public String[] split(CharSequence input, int limit) {
        int index = 0;
        boolean matchLimited = limit > 0;
        ArrayList<String> matchList = new ArrayList<String>();
        Matcher m = matcher(input);

        // Add segments before each match found
        while(m.find()) {
            if (!matchLimited || matchList.size() < limit - 1) {
                String match = input.subSequence(index, m.start()).toString();
                matchList.add(match);
                index = m.end();
            } else if (matchList.size() == limit - 1) { // last one
                String match = input.subSequence(index,
                                                 input.length()).toString();
                matchList.add(match);
                index = m.end();
            }
        }

        // If no match was found, return this
        if (index == 0)
            return new String[] {input.toString()};

        // Add remaining segment
        if (!matchLimited || matchList.size() < limit)
            matchList.add(input.subSequence(index, input.length()).toString());

        // Construct result
        int resultSize = matchList.size();
        if (limit == 0)
            while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
                resultSize--;
        String[] result = new String[resultSize];
        return matchList.subList(0, resultSize).toArray(result);
    }
 
Grundsätzlich findest du es mit der Regex [c]"[^\\\\];"[/c] oder - falls \ mit \\ escaped werden muss - schöner [c]"[^\\\\](\\\\{2})*;"[/c]. Das Problem dabei ist, dass er dann bis zum letzten nicht-\ abschneidet.
 
die schicke split-Methode kann man mit [^\\\\]; schlecht benutzen, da sonst das Zeichen vor dem ; verschluckt wird

Gibts hierfür eine Erklärung? Das mit dem verschlucken hab ich ja bereits selbst rausgefunden :-( Aber so richtig einleuchten will's mir nicht warum das Zeichen hopps gehen...

- Alex
 
wenn du foo-bar-baz auf - splittest, erhälst du auch foo, bar und baz. Die gematchten Zeichen werden bei split() einfach geschluckt.
 
wieso formatierst du den string mit key/value nicht wie bei csv dateien?

"key=value";"key2=cval;bl";"key3=halld="

dann musst du auch nicht escapen ...
 
wenn du foo-bar-baz auf - splittest, erhälst du auch foo, bar und baz. Die gematchten Zeichen werden bei split() einfach geschluckt.

Das leuchtet mir ein. Aber bei mir werden ja Zeichen geschluckt die nicht auf die RegEx matchen:

Code:
Pair.: p1=va\=l
Key..: p
Value: va\=l


@Vayu

Ja leider kann ich mir den String nicht aussuchen. Der ist schon so. Ich muss ihn nur parsen.


- Alex
 
> Aber bei mir werden ja Zeichen geschluckt die nicht auf die RegEx matchen:
?
das Pattern ist
[^\\\\]=

'irgendein Zeichen, nicht \' sowie '='

also wird "p1=va\=1" angeschaut, 'irgendein Zeichen, nicht \' ist Zeichen 2, die 1, '=' ist Zeichen 3,
Zeichen 2 und 3 fliegen raus, die sind ja die Split-Zeichen,

was übrig bleibt, siehst du ja
 
Ah, okay, jetzt leuchtet's ein ... Okay, dann werde ich ne eigene split() Methode basteln müssen. Danke mal soweit.

- Alex
 
sollte auch ausreichend schnell sein, ohne regex etc ...

Java:
public static HashMap<String, String> parse(String text) {
        char[] c = text.toCharArray();
        boolean key = true;
        HashMap<String, String> ret = new HashMap<String, String>();
        String tmpKey = "";
        String tmpVal = "";
        for(int i=0; i < c.length; i++) {
            if(c[i] == '=' && i != 0 && c[i-1] != '\\') {
                key = false;
                continue;
            }
            if(c[i] == ';' && i != 0 && c[i-1] != '\\') {
                ret.put(tmpKey, tmpVal);
                tmpVal = tmpKey = "";
                key = true;
                continue;
            }
            if(key)
                tmpKey += c[i];
            else
                tmpVal += c[i];
        }
        ret.put(tmpKey, tmpVal);
        return ret;
    }
 
was ist an continue schlimm?

Ist übrigens super nur zu schreiben "gott bist du blöd" statt gleich noch zu sagen wieso ... DAS ist schlechter Stil bygones ...
 
Zuletzt bearbeitet von einem Moderator:
was ist an continue schlimm?

Ist übrigens super nur zu schreiben "gott bist du blöd" statt gleich noch zu sagen wieso ... DAS ist schlechter Stil bygones ...

hey war kein persoenlicher Angriff... nicht gleich den Knueppel rausholen, ich habe nicht mal ansatzweise das wort "bloed" geschrieben... fuer eigeninterpretationen bin ich nicht verantwortlich !

ich mag continue einfach nicht - umkehrung der condition und gut is
ich mag auch kein break - schreit nach auslagern in eine methode


ich muss aber auch ehrlich sagen ich versteh deine methode nicht wirklich... trifft das eine oder andere zu wird irgendwas gesetzt und dann weitergegangen... wenn keins trifft wird der zustand von key genommen, der irgendwann mal gesetzt wurde und was gemacht.

ich finds einfach nicht gut zu lesen bzw zu verstehen.

hoffe das hat nun deine sensibilitaet nicht zu sehr gestresst
 
überleg dir einfach wie du drauf reagiert hättest, hätte ich unter einen post von dir so etwas geantwortet ... ich kann mit kritik umgehen, wenn sie sachlich rüberkommt ...

und was tue ich in der methode?

erste if -> schau ob ein = gefunden wurde, welches nicht escaped ist, dann ist der key im string am ende
wird ein ; gefunden, welches nicht escaped ist, ist auch der value am ende angelangt und ich kann beide in die hashmap übertragen (key -> value)

der boolean wird einfach gesetzt, damit ich weiss an welchen string ich den eben gecheckten char anhänge.

wieso ich keinen regex benutze? Weil regex in Java um den faktor 10 langsamer ist, als einen string in ner for-schleife durchzulaufen.
 
Was hältst du von einem Lookbehind?
[Java]public class Test
{

public static void main(String[] args)
{
String st = "p1=va\\=l1;para2=Hallo\\;Welt;parameter3=value3";

String[] pairs = st.split("(?<!\\\\);"); // splitte in Paare

for (String pair : pairs)
{
System.out.println("Pair.: "+pair);

String[] keyValue = pair.split("(?<!\\\\)="); // splitte in Key-Value

System.out.println("Key..: "+keyValue[0]);
System.out.println("Value: "+keyValue[1]);
System.out.println();
}
}
}[/Java]
 
überleg dir einfach wie du drauf reagiert hättest, hätte ich unter einen post von dir so etwas geantwortet ... ich kann mit kritik umgehen, wenn sie sachlich rüberkommt ...
ich haette einfach deinen ersten satz geschrieben... die interpretation danach war fehl am platze...

bygones - themenfremd
 
cool, so geht das! Danke Illuvatar!

@bygones
das würde ich jetzt auch sagen 😉
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben