в Java регулярные выражения разбить строку

Я немного застрял, пытаясь придумать регулярное выражение, чтобы разбить строки со следующими свойствами:

  1. разделено символом | (pipe)
  2. если отдельное значение содержит канал, экранированный (обратная косая черта)
  3. если отдельное значение заканчивается обратной косой чертой, экранируется обратной косой чертой

так, например, вот некоторые строки, которые я хочу разбить:

  1. One|Two|Three должны уступать: ["One", "Two", "Three"]
  2. One|Two|Three должно получиться: ["One|Two|Three"]
  3. One|Two|Three должно получиться: ["One", "Two|Three"]

теперь, как я могу разделить это с одним регулярным выражением?

UPDATE: как многие из вас уже предлагали, это не хорошее приложение регулярного выражения. Кроме того, решение регулярных выражений на порядок медленнее, чем просто итерация по символам. Я закончил перебирать символы:

public static List<String> splitValues(String val) {
    final List<String> list = new ArrayList<String>();
    boolean esc = false;
    final StringBuilder sb = new StringBuilder(1024);
    final CharacterIterator it = new StringCharacterIterator(val);
    for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
        if(esc) {
            sb.append(c);
            esc = false;
        } else if(c == '') {
            esc = true;
        } else if(c == '|') {
            list.add(sb.toString());
            sb.delete(0, sb.length());
        } else {
            sb.append(c);
        }
    }
    if(sb.length() > 0) {
        list.add(sb.toString());
    }
    return list;
}

1 ответов


хитрость не использовать split() метод. Это заставляет вас использовать lookbehind для обнаружения экранированных символов, но это не удается, когда эскейпы сами экранируются (как вы обнаружили). Вам нужно использовать на маркеры вместо разделителей:

public static List<String> splitIt(String source)
{
  Pattern p = Pattern.compile("(?:[^|\\]|\\.)+");
  Matcher m = p.matcher(source);
  List<String> result = new ArrayList<String>();
  while (m.find())
  {
    result.add(m.group().replaceAll("\\(.)", ""));
  }
  return result;
}

public static void main(String[] args) throws Exception
{
  String[] test = { "One|Two|Three", 
                    "One\|Two\|Three", 
                    "One\\|Two\|Three", 
                    "One\\\|Two" };
  for (String s :test)
  {
    System.out.printf("%n%s%n%s%n", s, splitIt(s));
  }
}

выход:

One|Two|Three
[One, Two, Three]

One\|Two\|Three
[One|Two|Three]

One\|Two\|Three
[One\, Two|Three]

One\\|Two
[One\|Two]