如何不匹配此正则表达式中的第一个空字符串?

时间:2019-07-09 23:21:57

标签: java regex

免责声明:这个问题的标题可能太笼统了,对以后遇到相同问题的读者没有帮助。可能是因为我无法正确说出我没有能够找到任何尚未解决我问题的东西...我从事修改标题的工作,或者一旦有人将帮助我弄清楚真正的问题是什么,就结束问题:))。

高级描述

我在输入中收到一个包含我感兴趣的两个信息的字符串:

  • 版本名称,它是3.1.build,以后是其他名称
  • 内部版本号somenumbers-somenumbers-eitherwordsornumbers-somenumbers

我需要分别提取它们。

有关输入的更多详细信息

我输入的内容可能有4种:

示例1 v3.1.build.dev.12345.team 12345-12345-cici-12345(中间的空格先是\t,然后是空白)。

示例2 v3.1.build.dev.12345.team 12345-12345-12345-12345(这与第一个示例非常相似,只不过在第二部分中,我们只有数字,-,没有字母字符) 。

示例3

v3.1.build.dev.12345.team
12345-12345-cici-12345

(以上内容与示例1极为相似,不同之处在于,除了\t和空格之外,只是换行了。

示例4

v3.1.build.dev.12345.team
12345-12345-12345-12345

(与上面相同,第二行只有数字和破折号)。

请注意,在示例3和示例4中,两个字符串后面都有一些尾随空格(此处不可见)。

总结一下,这些是4种可能的输入:

    String str1 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-cici-12345";
    String str2 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-12345-12345";
    String str3 = "v3.1.build.dev.12345.team   \n12345-12345-cici-12345   ";
    String str4 = "v3.1.build.dev.12345.team   \n12345-12345-12345-12345   ";

我的当前代码

我编写了以下代码来提取所需信息(此处仅报告相关信息,请访问fiddle link以获取完整且可运行的示例):

    String versionPattern = "^.+[\\s]";
    String buildIdPattern = "[\\s].+";

    Pattern pVersion = Pattern.compile(versionPattern);
    Pattern pBuildId = Pattern.compile(buildIdPattern);

    for (String str : possibilities) {
        Matcher mVersion = pVersion.matcher(str);
        Matcher mBuildId = pBuildId.matcher(str);
        while(mVersion.find()) {
            System.out.println("Version found: \"" +  mVersion.group(0).replaceAll("\\s", "") + "\"");
        }
        while (mBuildId.find()) {
            System.out.println("Build-id found: \"" +  mBuildId.group(0).replaceAll("\\s", "") + "\"");
        }
    }

我面临的问题

上面的代码几乎可以正常工作。但是,在示例3和示例4(通过编号\n将版本号分隔为build-id的示例)中,我得到了两个匹配项:第一个匹配项仅为"",第二个是我希望的那个。

我不觉得这段代码是稳定的,而且我认为正则表达式模式在匹配build-id方面做错了:

    String buildIdPattern = "[\\s].+";

有人想出一些想法来排除示例3和示例4的build-id上的第一个空匹配项,同时保留所有其他匹配项吗? 还是一些更好的方式自己编写正则表达式(我愿意接受改进,而不是正则表达式的专家)?

4 个答案:

答案 0 :(得分:1)

(^v\w.+)\s+(\d+-\d+-\w+-\d+)\s*

它将捕获2个组。一个将捕获第一部分(v3.1.build.dev.12345.team),第二个将捕获最后一部分(12345-12345-cici-12345)

它的分解方式如下:(^v\w.+)确保字符串以av开头,然后捕获数字或字母的所有字符(在空格选项卡等处停止。)\s+匹配任何空格或标签/换行符等。 (\d+-\d+-\w+-\d+)会读入它,确保它符合您指定的格式。请注意,这仍然会显示在破折号中,这使您更容易在以后分割字符串以获取所需的信息。如果您愿意,甚至可以将它们设置为自己的捕获组,从而更轻松地获取信息。

然后以\s*结尾,以确保不会因尾随空格而弄乱。它使用*而不是+,因为我们不希望它在没有尾随空白的情况下中断。

答案 1 :(得分:1)

我认为这对生产很有用(除了字符串不能以任何空格开头的事实-这是可修复的,但我不确定这是否是您要使用的)。

public class Other {

    static String patternStr = "^([\\S]{1,})([\\s]{1,})(.*)";

    static String str1 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-cici-12345";
    static String str2 = "v3.1.build.dev.12345.team\t\t\t\t\t  12345-12345-12345-12345";
    static String str3 = "v3.1.build.dev.12345.team   \n12345-12345-cici-12345   ";
    static String str4 = "v3.1.build.dev.12345.team   \n12345-12345-12345-12345   ";

    static Pattern pattern = Pattern.compile(patternStr);

    public static void main(String[] args) {

        List<String> possibilities = Arrays.asList(str1, str2, str3, str4);

        for (String str : possibilities) {

            Matcher matcher = pattern.matcher(str);

            if (matcher.find()) {
                System.out.println("Version found:  \"" +  matcher.group(1).replaceAll("\\s", "") + "\"");

                System.out.println("Some whitespace found: \"" +  matcher.group(2).replaceAll("\\s", "") + "\"");

                System.out.println("Build-id found: \"" +  matcher.group(3).replaceAll("\\s", "") + "\"");
            } else {
                System.out.println("Pattern NOT found");
            }

            System.out.println();
        }
    }
}

Imo,它看起来与原始代码非常相似。如果您对正则表达式不熟悉,我将解释发生了什么。

[\\S]中的大写S基本上表示匹配[\\s]之外的所有内容。 .+在您的情况下效果很好,但实际上是要匹配所有不为空的内容-甚至是空格。这并不一定很糟糕,但是如果您不得不修改正则表达式,那就麻烦了。

{1,}简单意味着one or more occurrences。再举一个例子,{1,2}将出现1或2次。仅供参考,+通常表示0或1次出现(也许在Java中不是),而*表示1次或多次出现。

括号表示组。整个匹配项是组0。当添加括号时,从左到右的顺序代表组1 ..组N。所以我所做的就是使用组组合模式,并用一个或多个空格隔开。 (.*)用于组2,因为该组可以同时具有空格和非空格,只要它不以空格开头即可。

如有任何疑问,请随时提问。作为记录,如果您仅在buildId模式中添加'+',则当前代码就可以了:[\\s]+.+

否则,您的正则表达式将说:match the whitespace that is followed by no characters or a single character。由于所有空格后面都跟着更多空格,因此您只匹配一个空格。

答案 2 :(得分:1)

TLDR;

使用模式^(v\\S+)\\s+(\\S+),捕获组分别捕获 version build ,这是完整的代码段:

String unitPattern ="^(v\\S+)\\s+(\\S+)";

    Pattern pattern = Pattern.compile(unitPattern);

    for (String str : possibilities) {
        System.out.println("Analyzing \"" + str + "\"");
        Matcher matcher = pattern.matcher(str);


        while(matcher.find()) {
            System.out.println("Version found: \"" +  matcher.group(1) + "\"");
            System.out.println("Build-id found: \"" +  matcher.group(2) + "\"");
        }

    }

Fiddle to try it.

坚韧不拔

原因是输出中的空行

这是因为Matcher类如何解释.. 匹配换行符,它在\n之前停止匹配。为此,您需要使用Pattern.compile(String pattern, int flags)添加标志Pattern.DOTALL

尝试

但是即使使用Pattern.DOTALL,由于定义模式的方式仍然无法匹配。更好的方法是将完整的 build version 匹配为 unit ,然后提取必要的部分。

^(v\\S+)\\s+(\\S+)

这确实欺骗了:

  • ^(v\\S+)定义了单元的开头,还捕获了版本信息
  • \\s+匹配标签,换行符,空格等
  • (\\S+)捕获最终的连续构建ID

答案 3 :(得分:1)

根据您的描述,您的数据看起来像是

NonWhiteSpaces whiteSpaces NonWhiteSpaces (optionalWhiteSpaces)

,而您只想获得NonWhiteSpaces个零件。

这可以通过多种方式实现。其中一种方法是trim()除去字符串中可能存在的尾随空格,然后split除去空格(现在只应位于字符串的中间)。像

String[] arr = data.trim().split("\\s+");// \s also represents line separators like \n \r
String version = arr[0];
String buildID = arr[1];