Regex и экранированный и не экранированный - PullRequest
7 голосов
/ 26 октября 2011

вопрос, связанный с this

У меня есть строка

a\;b\\;c;d

, которая в Java выглядит как

String s = "a\\;b\\\\;c;d"

Мне нужно разделитьэто точка с запятой со следующими правилами:

  1. Если перед точкой с запятой стоит обратная косая черта, ее не следует рассматривать как разделитель (между a и b ).

  2. Если сам символ обратной косой черты экранирован и, следовательно, не выходит за точку с запятой, эта точка с запятой должна быть разделителем (между b и c ).

Таким образом, точку с запятой следует рассматривать как разделитель, если перед ней стоит нулевое или четное число обратных косых черт.

Например, выше, я хочу получить следующие строки (двойная обратная косая черта для компилятора Java):

a\;b\\
c
d

Ответы [ 5 ]

6 голосов
/ 26 октября 2011

Вы можете использовать регулярное выражение

(?:\\.|[^;\\]++)*

для сопоставления всего текста между точками с запятой без экранирования:

List<String> matchList = new ArrayList<String>();
try {
    Pattern regex = Pattern.compile("(?:\\\\.|[^;\\\\]++)*");
    Matcher regexMatcher = regex.matcher(subjectString);
    while (regexMatcher.find()) {
        matchList.add(regexMatcher.group());
    } 

Пояснение:

(?:        # Match either...
 \\.       # any escaped character
|          # or...
 [^;\\]++  # any character(s) except semicolon or backslash; possessive match
)*         # Repeat any number of times.

Притяжательное совпадение (++) важно во избежание катастрофического обратного отслеживания из-за вложенных квантификаторов.

0 голосов
/ 13 июля 2018

Это реальный ответ, я думаю.В моем случае я пытаюсь разделить, используя | и escape-символ &.

    final String regx = "(?<!((?:[^&]|^)(&&){0,10000}&))\\|";
    String[] res = "&|aa|aa|&|&&&|&&|s||||e|".split(regx);
    System.out.println(Arrays.toString(res));

В этом коде я использую Lookbehind для экранирования и символа.обратите внимание, что вид сзади должен иметь максимальную длину.

(?<!((?:[^&]|^)(&&){0,10000}&))\\|

это означает любой |, кроме тех, которые следуют за ((?:[^&]|^)(&&){0,10000}&)), и эта часть означает любое нечетное число & с.часть (?:[^&]|^) важна, чтобы убедиться, что вы подсчитываете все & s после | в начале или некоторых других символов.

0 голосов
/ 26 октября 2011

Этот подход предполагает, что ваша строка не будет иметь char '\0' в вашей строке. Если вы это сделаете, вы можете использовать другой символ.

public static String[] split(String s) {
    String[] result = s.replaceAll("([^\\\\])\\\\;", "$1\0").split(";");
    for (int i = 0; i < result.length; i++) {
        result[i] = result[i].replaceAll("\0", "\\\\;");
    }
    return result;
}
0 голосов
/ 26 октября 2011

Я не доверяю обнаруживать эти случаи с помощью каких-либо регулярных выражений.Я обычно делаю простой цикл для таких вещей, я набросаю его, используя C, так как давным-давно я в последний раз коснулся Java; -)

int i, len, state;
char c;

for (len=myString.size(), state=0, i=0; i < len; i++) {
    c=myString[i];
    if (state == 0) {
       if (c == '\\') {
            state++;
       } else if (c == ';') {
           printf("; at offset %d", i);
       }
    } else {
        state--;
    }
}

Преимущества :

  1. вы можете выполнять семантические действия на каждом шаге.
  2. его довольно просто перенести на другой язык.
  3. вам не нужно включать полный текстБиблиотека регулярных выражений как раз для этой простой задачи, которая добавляет переносимости.
  4. она должна быть намного быстрее, чем сопоставление регулярных выражений.
0 голосов
/ 26 октября 2011
String[] splitArray = subjectString.split("(?<!(?<!\\\\)\\\\);");

Это должно работать.

Объяснение:

// (?<!(?<!\\)\\);
// 
// Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind) «(?<!(?<!\\)\\)»
//    Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind) «(?<!\\)»
//       Match the character “\” literally «\\»
//    Match the character “\” literally «\\»
// Match the character “;” literally «;»

Таким образом, вы просто сопоставляете точку с запятой, не предшествующую ровно одной \.

РЕДАКТИРОВАТЬ:

String[] splitArray = subjectString.split("(?<!(?<!\\\\(\\\\\\\\){0,2000000})\\\\);");

Это будет заботиться о любом нечетном числе. конечно потерпит неудачу, если у вас более 4000000 номеров \.Объяснение отредактированного ответа:

// (?<!(?<!\\(\\\\){0,2000000})\\);
// 
// Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind) «(?<!(?<!\\(\\\\){0,2000000})\\)»
//    Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind) «(?<!\\(\\\\){0,2000000})»
//       Match the character “\” literally «\\»
//       Match the regular expression below and capture its match into backreference number 1 «(\\\\){0,2000000}»
//          Between zero and 2000000 times, as many times as possible, giving back as needed (greedy) «{0,2000000}»
//          Note: You repeated the capturing group itself.  The group will capture only the last iteration.  Put a capturing group around the repeated group to capture all iterations. «{0,2000000}»
//          Match the character “\” literally «\\»
//          Match the character “\” literally «\\»
//    Match the character “\” literally «\\»
// Match the character “;” literally «;»
...