TL; DR Регулярное выражение:
(?!$)(?:(-?\d*)x\^2)?(?:(?<=2)\s*([+-])\s*(?!-|^))?(?:(?<!2)(-?\d*)x)?(?:(?<=[2x])\s*([+-])\s*(?!-|^))?(?:(?<![2x])(-?\d+))?
Полный синтаксис будет:
[ ['-'][number]'x^2' ] ['+' | '-'] [ ['-'][number]'x' ] ['+' | '-'] [ ['-']number ]
['-']
означает необязательный знак минусдля числа.
[number]
потому, что множитель 1
может быть опущен, даже если он -1
, например, -x
является допустимым сокращением для -1x
.
['+' | '-']
означает +
или -
, помеченные как необязательные для простоты, но фактически они требуются между частями и не допускаются сами по себе.Это сложная часть, которая увеличит размер регулярного выражения.
Итак, давайте построим его:
part1: -?\d*x\^2
part2: -?\d*x
part3: -?\d+
OP : \s*[+-]\s*(?!-)
(?!-)
- запрет знака минус после оператора.
Остается [part1] [OP] [part2] [OP] [part3]
с 3 дополнительными частями и двумя дополнительными операторами, за исключением того, что части должны быть разделены операторами, а операторы должны быть между частями, и должна присутствовать хотя бы одна часть.
Чтобы убедиться, что что-то присутствует, нам просто нужно предотвратить пустую строку, т.е. начать регулярное выражение с (?!$)
, ноль - с отрицательным предвкушением конца строки, иначе "в начале ввода, мы также нев конце ввода ".
В регулярном выражении мы делаем необязательное использование (?: xxx )?
, то есть группа без захвата помечена как необязательная, поэтому сделать каждую часть и оператор необязательным легко.
Пока что мыпоэтому:
(?!$)(?:part1)?(?:OP)?(?:part2)?(?:OP)?(?:part3)?
Теперь для сложных деталей:
Детали должны быть разделены операторами
Это проще всего проверить, если убедиться, что part2не сразу предшествует 2
(последнийсимвол part1), и этой части 3 непосредственно не предшествует 2
или x
(последний символ part1 или part2), поэтому мы будем использовать отрицательные взгляды нулевой ширины.
(?:(?<!2)part2)
(?:(?<![2x])part3)
Операторы должны находиться между частями
Операторы не могут быть первыми или последними
(?:(?<!^)OP(?!^))
Операторы не могут быть смежными, проще всего проверить, убедившись, что второму оператору непосредственно предшествует part1или part2, что делает избыточным «not at start».
(?:(?<=[2x])OP)
Для согласованности значение «not at start» для первого оператора может быть изменено на «must part1».
(?:(?<=2)OP)
Это теперь дает нам (показано в нескольких строках для пояснения):
(?!$)
(?:part1)?
(?:(?<=2)OP(?!^))?
(?:(?<!2)part2)?
(?:(?<=[2x])OP(?!^))?
(?:(?<![2x])part3)?
Все вместе, с добавленными группами захвата для захвата (подписанных) чисел и операторов
(?!$)(?:(-?\d*)x\^2)?(?:(?<=2)\s*([+-])\s*(?!-|^))?(?:(?<!2)(-?\d*)x)?(?:(?<=[2x])\s*([+-])\s*(?!-|^))?(?:(?<![2x])(-?\d+))?
Вот он, закодированный на Java, с логикой для поиска a
, b
и c
в формуле ax^2 + bx + c
:
public static void main(String[] args) {
System.out.println("part1 op1 part2 op2 part3 a b c input");
test("3x^2 + 4x + 5");
test("x^2 + x + 1");
test("x^2 - 4x");
test("-x^2 - 1");
test("-4x - 5");
test("-3x^2");
test("-4x");
test("-5");
test("");
test("-3x^2 + -1x");
}
private static void test(String input) {
String regex = "(?!$)" +
"(?:(-?\\d*)x\\^2)?" +
"(?:(?<=2)\\s*([+-])\\s*(?!-|^))?" +
"(?:(?<!2)(-?\\d*)x)?" +
"(?:(?<=[2x])\\s*([+-])\\s*(?!-|^))?" +
"(?:(?<![2x])(-?\\d+))?";
Matcher m = Pattern.compile(regex).matcher(input);
if (! m.matches()) {
System.out.printf("%-41s\"%s\"%n", "No match", input);
} else {
String part1 = m.group(1);
String op1 = m.group(2);
String part2 = m.group(3);
String op2 = m.group(4);
String part3 = m.group(5);
long a = parse(null, part1);
long b = parse(op1, part2);
long c = parse((op2 != null ? op2 : op1), part3);
System.out.printf("%-6s%-6s%-6s%-6s%-6s%3d%3d%3d \"%s\"%n",
(part1 == null ? "" : '"' + part1 + '"'),
(op1 == null ? "" : '"' + op1 + '"'),
(part2 == null ? "" : '"' + part2 + '"'),
(op2 == null ? "" : '"' + op2 + '"'),
(part3 == null ? "" : '"' + part3 + '"'),
a, b, c, input);
}
}
private static long parse(String operator, String signedNumber) {
long number;
if (signedNumber == null)
number = 0;
else if (signedNumber.isEmpty())
number = 1;
else if (signedNumber.equals("-"))
number = -1;
else
number = Long.parseLong(signedNumber);
if ("-".equals(operator))
number = -number;
return number;
}
Выход
part1 op1 part2 op2 part3 a b c input
"3" "+" "4" "+" "5" 3 4 5 "3x^2 + 4x + 5"
"" "+" "" "+" "1" 1 1 1 "x^2 + x + 1"
"" "-" "4" 1 -4 0 "x^2 - 4x"
"-" "-" "1" -1 0 -1 "-x^2 - 1"
"-4" "-" "5" 0 -4 -5 "-4x - 5"
"-3" -3 0 0 "-3x^2"
"-4" 0 -4 0 "-4x"
"-5" 0 0 -5 "-5"
No match ""
No match "-3x^2 + -1x"