正则表达式可选组捕获JAVA

时间:2014-01-21 20:02:57

标签: java regex

我有一个用户指定的模式:

1998-2010:Make:model:trim:engine

trimengine是可选的,如果存在,我应该抓住它们;如果没有,匹配者至少应该验证YMM。

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

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

2 个答案:

答案 0 :(得分:8)

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

您可以使用?来匹配零或一个东西,这是您想要对最后一位做的事情。但是,您的模式需要进行一些修改,使其更像[^:]*而不是.*。一些示例代码及其输出如下。我最后得到的正则表达式是:

([^:]*):([^:]*):([^:]*)(?::([^:]*))?(?::([^:]*))?
|-----| |-----| |-----|    |-----|      |-----|
   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 ===

虽然通过使用正则表达式来匹配这种字符串当然是可能的,但似乎可能更容易在:上拆分字符串并检查你得到多少值。这不一定会进行其他类型的检查(例如,每个字段中的字符),因此在任何非最小的情况下,分裂都不是很有用。

使用String.split和限制参数

我在另一篇建议使用your comment的帖子中注意到String.split(String)(强调添加):

  

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

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

  

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

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

答案 1 :(得分:0)

为什么不跳过正则表达式并使用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也使用正则表达式模式。在我的观点中,尽管并不重要。要拥有一个真正的无正则表达式解决方案,请使用apache commons中的StrwingUtils.split(根本不使用正则表达式):)