Wie trenne ich eine von Kommas getrennte Schnur, während er entkommene Kommas ignoriert?

StackOverflow https://stackoverflow.com/questions/820172

  •  03-07-2019
  •  | 
  •  

Frage

Ich muss eine erweiterte Version der Stringutils.CommadelimitedListToStringArray -Funktion schreiben, die einen zusätzlichen Parameter erhält: das Escape char.

Also rufe mein:

commaDelimitedListToStringArray("test,test\\,test\\,test,test", "\\")

sollte zurückkehren:

["test", "test,test,test", "test"]



Mein aktueller Versuch ist es, String.split () zu verwenden, um die Zeichenfolge mit regulären Ausdrücken zu teilen:

String[] array = str.split("[^\\\\],");

Aber das zurückgegebene Array ist:

["tes", "test\,test\,tes", "test"]

Irgendwelche Ideen?

War es hilfreich?

Lösung

Der reguläre Ausdruck

[^\\],

bedeutet "Übereinstimmung mit einem Charakter, der kein Backslash ist, gefolgt von einem Komma" - deshalb Muster wie Muster wie t, passen, weil t ist ein Charakter, der kein Backslash ist.

Ich denke, Sie müssen eine Art verwenden negatives Aussehen, um a zu erfassen , dem ist a nicht voraus \ ohne den vorhergehenden Charakter zu erfassen, so etwas wie

(?<!\\),

(Übrigens, beachten Sie, dass ich die Backslashes absichtlich nicht doppelt geweiht habe, um dies lesbarer zu machen)

Andere Tipps

Versuchen:

String array[] = str.split("(?<!\\\\),");

Grundsätzlich heißt es, dass ein Komma geteilt wird, außer wo diesem Komma zwei Rückstand vorausgeht. Dies wird a genannt Negative Aussicht.

Als zukünftige Referenz finden Sie hier die vollständige Methode, mit der ich gesprochen habe:

public static String[] commaDelimitedListToStringArray(String str, String escapeChar) {
    // these characters need to be escaped in a regular expression
    String regularExpressionSpecialChars = "/.*+?|()[]{}\\";

    String escapedEscapeChar = escapeChar;

    // if the escape char for our comma separated list needs to be escaped 
    // for the regular expression, escape it using the \ char
    if(regularExpressionSpecialChars.indexOf(escapeChar) != -1) 
        escapedEscapeChar = "\\" + escapeChar;

    // see http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas
    String[] temp = str.split("(?<!" + escapedEscapeChar + "),", -1);

    // remove the escapeChar for the end result
    String[] result = new String[temp.length];
    for(int i=0; i<temp.length; i++) {
        result[i] = temp[i].replaceAll(escapedEscapeChar + ",", ",");
    }

    return result;
}

Wie Matt B sagte, [^\\], wird den Charakter vor dem Komma als Teil des Trennzeichens interpretieren.

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,tes" , "test"]

Wie DRVDIJK sagte, (?<!\\), Wird entkommene Backslashes falsch interpretieren.

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,test" , "test"]
  -(unescape commas)->
["test\\\\,test\\,test,test" , "test"]

Ich würde erwarten, auch Backslashes zu entkommen ...

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\" , "test\\,test" , "test"]
  -(unescape commas and backslashes)->
["test\\,test\\" , "test,test" , "test"]

DRVDIJK schlug vor (?<=(?<!\\\\)(\\\\\\\\){0,100}), Was gut für Listen mit Elementen funktioniert, die mit bis zu 100 Backslashes enden. Das ist weit genug ... aber warum eine Grenze? Gibt es einen effizienteren Weg (ist nicht gierig aussehen)? Was ist mit ungültigen Zeichenfolgen?

Ich habe nach einer Weile nach einer generischen Lösung gesucht, dann habe ich das Ding selbst geschrieben ... die Idee ist, ein Muster zu spalten, das den Listenelementen entspricht (anstatt dem Trennzeichen zu entsprechen).

Meine Antwort nimmt den Fluchtzeichen nicht als Parameter.

public static List<String> commaDelimitedListStringToStringList(String list) {
    // Check the validity of the list
    // ex: "te\\st" is not valid, backslash should be escaped
    if (!list.matches("^(([^\\\\,]|\\\\,|\\\\\\\\)*(,|$))+")) {
        // Could also raise an exception
        return null;
    }
    // Matcher for the list elements
    Matcher matcher = Pattern
            .compile("(?<=(^|,))([^\\\\,]|\\\\,|\\\\\\\\)*(?=(,|$))")
            .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        // Unescape the list element
        result.add(matcher.group().replaceAll("\\\\([\\\\,])", "$1"));
    }
    return result;
}

Beschreibung für das Muster (unabgeordnet):

(?<=(^|,)) Vorwärts ist Beginn der String oder a ,

([^\\,]|\\,|\\\\)* das Element, das aus \,, \\ oder Charaktere sind weder \ Noch ,

(?=(,|$)) dahinter ist Ende der String oder a ,

Das Muster kann vereinfacht werden.

Auch mit den 3 Paruren (matches + find + replaceAll) Diese Methode erscheint schneller als die von DRVDIJK vorgeschlagene Methode. Es kann immer noch optimiert werden, indem ein bestimmter Parser geschrieben wird.

Auch was ist die Notwendigkeit, einen Fluchtcharakter zu haben, wenn nur ein Charakter etwas Besonderes ist, es könnte einfach verdoppelt werden ...

public static List<String> commaDelimitedListStringToStringList2(String list) {
    if (!list.matches("^(([^,]|,,)*(,|$))+")) {
        return null;
    }
    Matcher matcher = Pattern.compile("(?<=(^|,))([^,]|,,)*(?=(,|$))")
                    .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        result.add(matcher.group().replaceAll(",,", ","));
    }
    return result;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top