2009-05-07 6 views
1

Кто-нибудь знает, как разбить строку на символ с учетом его escape-последовательности?Разбиение строки (особенно на Java с помощью java.util.regex или что-то еще)

Например, если символ «:», «a: b» разделен на две части («a» и «b»), тогда как «a: b» вообще не разделяется.

Я думаю, что это трудно (невозможно?) Делать с регулярными выражениями.

Спасибо заранее,

Кидаре

+0

См. Также http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas. –

ответ

2

(?<=^|[^\\]): получает закрыть, но не решает сбежавшую косую черту. (Это буквальное регулярное выражение, конечно, вы должны избегать косых черт в нем, чтобы получить его в строку java)

(?<=(^|[^\\])(\\\\)*): Как насчет этого? Я думаю, что это должно удовлетворять любому «:», которому предшествует четное число косых черт.

Редактировать: не проголосуйте за это. Решение MizardX является лучше :)

+0

Ключом является конструкция (? <= Foo), положительный внешний вид. Вам нужно проверить, что предшествует «:», не сопоставляя его. –

+1

MizardX указывает, что внешний вид должен иметь конечную длину. Моя не так, я думаю, это не сработало (не проверено). Я считаю, что наши решения аналогичны. Вероятно, он лучше в том, что использует отрицательный внешний вид, чтобы проверить символ без косой черты, тогда как я использую «^ | [^ \\\]», который может или не может действовать по-разному в многострочных сценариях (не уверен) , –

+1

(^ | [^ \\\]) должен работать.^возможно, соответствует началу строки вместо строки. Это прекрасно, так как он все еще уверяет, что это не обратная косая черта. [^ \\\] также будет соответствовать новым строкам, поэтому никаких проблем при использовании многострочного режима тоже не будет. –

2

Поскольку Java поддерживает переменной длины просмотровых зады (до тех пор, как они конечны), вы могли бы сделать, это сделать так:

import java.util.regex.*; 

public class RegexTest { 
    public static void main(String[] argv) { 

     Pattern p = Pattern.compile("(?<=(?<!\\\\)(?:\\\\\\\\){0,10}):"); 

     String text = "foo:bar\\:baz\\\\:qux\\\\\\:quux\\\\\\\\:corge"; 

     String[] parts = p.split(text); 

     System.out.printf("Input string: %s\n", text); 
     for (int i = 0; i < parts.length; i++) { 
      System.out.printf("Part %d: %s\n", i+1, parts[i]); 
     } 

    } 
} 
  • (?<=(?<!\\)(?:\\\\){0,10}) выглядит сзади для четное количество обратных косых черт (включая ноль, максимум до 10).

Выход:

Input string: foo:bar\:baz\\:qux\\\:quux\\\\:corge
Part 1: foo
Part 2: bar\:baz\\
Part 3: qux\\\:quux\\\\
Part 4: corge

Другим способом будет соответствовать тем частям себя, вместо разделения на разделителях.

Pattern p2 = Pattern.compile("(?<=\\A|\\G:)((?:\\\\.|[^:\\\\])*)"); 
List<String> parts2 = new LinkedList<String>(); 
Matcher m = p2.matcher(text); 
while (m.find()) { 
    parts2.add(m.group(1)); 
} 

Странный синтаксис вытекает из того, что ему необходимо обрабатывать случай пустых предметов в начале и конце строки. Когда совпадение содержит ровно нулевые символы, следующая попытка запустит один символ за его конец. Если бы это не так, это соответствовало бы другой пустой строке, а другая, ad infinitum & hellip;

  • (?<=\A|\G:) будет выглядеть позади либо для начала строки (первая часть), или в конце предыдущего матча, после чего сепаратора. Если бы мы сделали (?:\A|\G:), это не получится, если первая часть пуста (ввод начинается с разделителя).
  • \\. соответствует любому экранированному персонажу.
  • [^:\\] соответствует любому символу, который не находится в escape-последовательности (потому что \\. уничтожил оба из них).
  • ((?:\\.|[^:\\])*) захватывает все символы до первого неэкранированного разделителя в группу захвата 1.