regex необязательная группа захвата JAVA

у меня есть шаблон, где пользователь указывает:

1998-2010:Make:model:trim:engine

trim и engine являются необязательными, если присутствует, я должен захватить их; если нет, сопоставитель должен по крайней мере проверить YMM.

([0-9]+-*[0-9]+):(.*):(.*):(.*):(.*)

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

2 ответов


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

можно использовать ? чтобы соответствовать нулю или одному из чего-то, что вы хотите сделать с последним битом. Однако, ваш шаблон должен немного модификация больше нравится [^:]*, а не .*. Далее следует пример кода и его вывод. Регулярное выражение, с которым я закончил, было:

([^:]*):([^:]*):([^:]*)(?::([^:]*))?(?::([^:]*))?
|-----| |-----| |-----|    |-----|      |-----|
   a       a       a          a            a

                       |-----------||-----------|
                             b            b

каждого a соответствует последовательности не двоеточий (хотя вы хотите изменить первый, чтобы соответствовать годам), и b это незахватывающие группа (поэтому она начинается с ?:) и соответствует нулю или один раз (потому что он имеет окончательный ? Квантор). Это означает, что четвертое и пятое поля являются необязательными. Пример кода показывает, что этот шаблон соответствует в случае наличия трех, четырех или пяти полей и не совпадает, если их больше пяти или меньше трех.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class QuestionMarkQuantifier {
    public static void main(String[] args) {
        final String input = "a:b:c:d:e:f:g:h";
        final Pattern p = Pattern.compile( "([^:]*):([^:]*):([^:]*)(?::([^:]*))?(?::([^:]*))?" );
        for ( int i = 1; i <= input.length(); i += 2 ) {
            final String string = input.substring( 0, i );
            final Matcher m = p.matcher( string );
            if ( m.matches() ) {
                System.out.println( "\n=== Matches for: "+string+" ===" );
                final int count = m.groupCount();
                for ( int j = 0; j <= count; j++ ) {
                    System.out.println( j + ": "+ m.group( j ));
                }
            }
            else {
                System.out.println( "\n=== No matches for: "+string+" ===" );
            }
        }
    }
}
=== No matches for: a ===

=== No matches for: a:b ===

=== Matches for: a:b:c ===
0: a:b:c
1: a
2: b
3: c
4: null
5: null

=== Matches for: a:b:c:d ===
0: a:b:c:d
1: a
2: b
3: c
4: d
5: null

=== Matches for: a:b:c:d:e ===
0: a:b:c:d:e
1: a
2: b
3: c
4: d
5: e

=== No matches for: a:b:c:d:e:f ===

=== No matches for: a:b:c:d:e:f:g ===

=== No matches for: a:b:c:d:e:f:g:h ===

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

Через Строку.split и предельный параметр

я заметил:комментарий на другом сообщение, которое рекомендуется использовать строку.split (String) (Курсив мой):

Да, я знаю эту функцию, но она работает для меня, потому что у меня есть строка это a: b:c:d:e:f:g: h.. но я просто хочу сгруппировать данные как a:b:c:d: e если любой как один и остальная часть строки как другая группа

стоит отметить, что есть версия split, которая принимает еще один параметр,строку.split (String,int). Этот второй параметр-это ограничение, описываемое как:

на limit параметр контролирует количество раз, когда шаблон применяется и, следовательно, влияет на длину результирующего массива. Если предел n больше нуля, тогда шаблон будет применяться на большинство n - 1 раз, длина массива не будет больше, чем n, и последняя запись массива будет содержать все введенные за последние совпавшие ограничитель данных. Если n не является положительным, тогда шаблон будет применяться как много раз, насколько это возможно, и массив может иметь любую длину. Если n равна нулю затем шаблон будет применен как можно больше раз, массив может иметь любую длину, и конечные пустые строки будут отброшены.

это означает, что вы можете использовать split и limit 6, чтобы получить до пяти полей от вашего ввода, и у вас будет оставшийся ввод в качестве последней строки. Вы все еще нужно проверить, были ли у вас по крайней мере 3 элемента, чтобы убедиться, что было достаточно ввода, но в целом, это кажется, что это может быть немного проще.

import java.util.Arrays;

public class QuestionMarkQuantifier {
    public static void main(String[] args) {
        final String input = "a:b:c:d:e:f:g:h";
        for ( int i = 1; i <= input.length(); i += 2 ) {
            final String string = input.substring( 0, i );
            System.out.println( "\n== Splits for "+string+" ===" );
            System.out.println( Arrays.toString( string.split( ":", 6 )));
        }
    }
}
== Splits for a ===
[a]

== Splits for a:b ===
[a, b]

== Splits for a:b:c ===
[a, b, c]

== Splits for a:b:c:d ===
[a, b, c, d]

== Splits for a:b:c:d:e ===
[a, b, c, d, e]

== Splits for a:b:c:d:e:f ===
[a, b, c, d, e, f]

== Splits for a:b:c:d:e:f:g ===
[a, b, c, d, e, f:g]

== Splits for a:b:c:d:e:f:g:h ===
[a, b, c, d, e, f:g:h]

Почему бы не пропустить regex и использовать split(":"). Кажется, прямолинейно. Из длины результирующего массива вы узнаете, была ли предоставлена модель и двигатель и т. д.

String str = "1998-2010:Make:model:trim:engine";
String[] parts  = str.split(":");
//parts[0] == Y
//parts[1] == M
//parts[2] == M
//etc

изменить: Как уже говорили другие,String.split также использует шаблон регулярного выражения. В моем случае это не имеет значения. Чтобы иметь действительно regex-менее решение использовать StrwingUtils.split из Apache commons (который вообще не использует регулярное выражение):)