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("),")
и вернуть )
после него.