正则表达式可选组捕获 JAVA

2022-09-03 07:07:02

我有一个模式,其中用户指定:

1998-2010:Make:model:trim:engine

trim并且是可选的,如果存在,我应该捕获它们;如果不是,匹配器至少应验证 YMM。engine

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

如果所有三个字段都存在,则此字段匹配,但是如何使最后两个且只有两个字段可选?


答案 1

使用正则表达式和 ,“零或一个量词”?

您可以使用来匹配零个或其中一个,这就是您要对最后一个位执行的操作。但是,您的模式需要稍作修改才能更像 而不是 .下面是一些示例代码及其输出。我最终得到的正则表达式是:?[^:]*.*

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

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

每个都匹配一个非冒号序列(尽管您希望修改第一个序列以匹配年份),并且是一个非捕获组(因此它以 开头),并且匹配零次或一次(因为它具有最终量词)。这意味着第四个和第五个字段是可选的。示例代码显示,在存在三个、四个或五个字段的情况下,此模式匹配,如果存在五个以上或少于三个字段,则此模式不匹配。ab?:?

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 ===

虽然使用正则表达式当然可以匹配这种字符串,但似乎只需拆分字符串并检查返回的值可能更容易。这不一定能进行其他类型的检查(例如,每个字段中的字符),因此,在任何非最小情况下,拆分可能都不是那么有用。:

使用 String.split 和 limit 参数

我注意到您对另一篇建议使用String.split(String)的帖子的评论(着重号是后加的):

是的,我知道这个函数,但它对我有用,因为我有一个字符串,它是a:b:c:d:e:f:g:h.。但我只想将数据分组为a:b:c:d:e(如果有的话)作为一个,字符串的其余部分作为另一个组

值得注意的是,有一个版本的split需要另一个参数,String.split(String,int)。。第二个参数是限制,描述为:

该参数控制应用模式的次数,因此会影响结果数组的长度。如果限制 n 大于零,则模式将最多应用 n - 1 次,数组的长度将不大于 n,数组的最后一个条目将包含除最后匹配的分隔符之外的所有输入。如果 n 为非正数,则模式将尽可能多地应用,并且数组可以具有任何长度。如果 n 为零,则模式将尽可能多地应用,数组可以具有任何长度,并且将丢弃尾随的空字符串。limit

这意味着您可以使用 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]

答案 2

为什么不跳过正则表达式并使用 .似乎直截了当。从结果数组的长度,您将知道是否提供了模型和引擎等。split(":")

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

编辑:正如其他人所提到的,也使用正则表达式模式。在我看来,这并不重要。要从apache共享资源(根本不使用正则表达式)使用真正的无正则表达式解决方案:)String.splitStrwingUtils.split