Regex для разделения вложенных строк координат

У меня есть строка формата "[(1, 2), (2, 3), (3, 4)]" с произвольным количеством элементов. Я пытаюсь разбить его на запятые, разделяющие координаты, то есть извлекать (1, 2), (2, 3) и (3, 4).

Можно ли это сделать в Java regex? Я полный noob, но надеясь, что Java regex достаточно мощный для этого. Если это не так, можете ли вы предложить альтернативу?

Ответ 1

Вы можете использовать String#split() для этого.

String string = "[(1, 2), (2, 3), (3, 4)]";
string = string.substring(1, string.length() - 1); // Get rid of braces.
String[] parts = string.split("(?<=\\))(,\\s*)(?=\\()");
for (String part : parts) {
    part = part.substring(1, part.length() - 1); // Get rid of parentheses.
    String[] coords = part.split(",\\s*");
    int x = Integer.parseInt(coords[0]);
    int y = Integer.parseInt(coords[1]);
    System.out.printf("x=%d, y=%d\n", x, y);
}

(?<=\\)) положительный lookbehind означает, что ему должно предшествовать ). (?=\\() положительный прогноз означает, что его следует выполнить (. (,\\s*) означает, что он должен быть разделен на , и в любом месте после этого. \\ здесь просто для того, чтобы избежать регулярных выражений.

Тем не менее, конкретная строка распознается как результат List#toString(). Вы уверены, что делаете все правильно?;)

Обновить в соответствии с комментариями, вы также можете сделать обратный путь и избавиться от цифр:

String string = "[(1, 2), (2, 3), (3, 4)]";
String[] parts = string.split("\\D.");
for (int i = 1; i < parts.length; i += 3) {
    int x = Integer.parseInt(parts[i]);
    int y = Integer.parseInt(parts[i + 1]);
    System.out.printf("x=%d, y=%d\n", x, y);
}

Здесь \\D означает, что он должен быть разделен на любой не -digit (символ \\D обозначает цифру). . после означает, что он должен устранить любые пробелы после цифр. Однако я должен признать, что я не уверен, как устранить пробелы перед цифрами. Я еще не обученный гуру регулярных выражений. Эй, Барт К, можешь ли ты сделать это лучше?

В конце концов, для этого лучше использовать синтаксический анализатор . См. ответ Huberts в этом разделе.

Ответ 2

Из Java 5

Scanner sc = new Scanner();
sc.useDelimiter("\\D+"); // skip everything that is not a digit
List<Coord> result = new ArrayList<Coord>();
while (sc.hasNextInt()) {
    result.add(new Coord(sc.nextInt(), sc.nextInt()));
}
return result;

EDIT: мы не знаем, сколько координат передано в строке coords.

Ответ 3

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

\(\d+,\s\d+\)

Это выражение вернет несколько совпадений (три с помощью ввода из вашего примера).

В вашем вопросе вы заявляете, что хотите "восстановить" (1, 2), (2, 3) и (3, 4). В случае, когда вам действительно нужна пара значений, связанных с каждой координатой, вы можете отменить круглые скобки и измените регулярное выражение, чтобы сделать некоторые захваты:

(\d+),\s(\d+)

Код Java будет выглядеть примерно так:

import java.util.regex.*;

public class Test {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("(\\d+),\\s(\\d+)");
        Matcher matcher = pattern.matcher("[(1, 2), (2, 3), (3, 4)]");

        while (matcher.find()) {
            int x = Integer.parseInt(matcher.group(1));
            int y = Integer.parseInt(matcher.group(2));
            System.out.printf("x=%d, y=%d\n", x, y);
        }
    }
}

Ответ 4

Всегда ли будет необходимо проанализировать 3 группы координат?

Вы можете попробовать:

\[(\(\d,\d\)), (\(\d,\d\)), (\(\d,\d\))\]

Ответ 5

Если вы используете регулярное выражение, вы получите отвратительную отчетность об ошибках, и все будет экспоненциально более сложным, если ваши требования изменятся (например, если вам нужно разобрать наборы в разных квадратных скобках в разные группы).

Я рекомендую вам просто написать парсер вручную, он вроде 10 строк кода и не должен быть очень хрупким. Отслеживайте все, что вы делаете, открываете парсеры, закрываете парсеры, открываете фигурные скобки и закрываете фигурные скобки. Это как оператор switch с 5 параметрами (и по умолчанию), действительно не так уж плохо.

Для минимального подхода открытые парсеры и открытые фигурные скобки можно игнорировать, поэтому действительно есть только 3 случая.


Это будет минимальный медведь.

// Java-like psuedocode
int valuea;
String lastValue;
tokens=new StringTokenizer(String, "[](),", true);

for(String token : tokens) {  

    // The token Before the ) is the second int of the pair, and the first should
    // already be stored
    if(token.equals(")"))
        output.addResult(valuea, lastValue.toInt());

    // The token before the comma is the first int of the pair
    else if(token.equals(",")) 
        valuea=lastValue.toInt();

    // Just store off this token and deal with it when we hit the proper delim
    else
        lastValue=token;
}

Это не лучше, чем минимальное решение на основе регулярного выражения EXCEPT, которое будет намного проще поддерживать и улучшать. (добавьте проверку ошибок, добавьте стек для сопоставления парных и квадратных скобок и проверки на неулокальные запятые и другой недопустимый синтаксис)

В качестве примера расширяемости, если вам нужно было разместить разные наборы групп с разделителями с квадратной скобкой в ​​разные выходные наборы, то добавление будет таким же простым, как:

    // When we close the square bracket, start a new output group.
    else if(token.equals("]"))
        output.startNewGroup();

И проверка на parens так же просто, как создание стека символов и нажатие каждого [или (в стек, затем когда вы получаете) или), поместите стек и утвердите, что он соответствует. Кроме того, когда вы закончите, убедитесь, что ваш stack.size() == 0.

Ответ 6

В регулярных выражениях вы можете разделить на (?<=\)),, которые используют Позитивный Lookbehind:

string[] subs = str.replaceAll("\[","").replaceAll("\]","").split("(?<=\)),");

В строковых функциях simpe вы можете отказаться от [ и ] и использовать string.split("),") и вернуть ) после него.