【问题标题】:Multiple matches with delimiter带分隔符的多个匹配项
【发布时间】:2015-12-15 00:20:47
【问题描述】:

这是我的正则表达式:

([+-]*)(\\d+)\\s*([a-zA-Z]+)
  • 第 1 组 = 标志
  • 第 2 组 = 乘数
  • 第 3 组 = 时间单位

问题是,我想匹配给定的输入,但它可以被“链接”。因此,当且仅当整个模式重复且在这些事件之间没有任何内容(空格除外)时,我的输入才应该有效。 (只有一个匹配或多个匹配彼此相邻,它们之间可能有空格)。

有效示例:

1day
+1day
-1 day
+1day-1month
+1day +1month
   +1day  +1month    

无效示例:

###+1day+1month
+1day###+1month
+1day+1month###
###+1day+1month###
###+1day+1month###

在我的情况下,我可以使用 matcher.find() 方法,这可以解决问题,但它会接受这样的输入:+1day###+1month 这对我无效。

有什么想法吗?这可以通过多个 IF 条件和对开始和结束索引的多次检查来解决,但我正在寻找优雅的解决方案。

编辑

^\s*(([+-]*)(\d+)\s*([a-zA-Z]+)\s*)+$ 下面的 cmets 中建议的正则表达式将部分起到作用,但如果我在下面的代码中使用它,它返回的结果与我正在寻找的结果不同。 问题是我不能使用(*my regex*)+,因为它会匹配整个事情。

解决方案可能是将整个输入与^\s*(([+-]*)(\d+)\s*([a-zA-Z]+)\s*)+$ 匹配,然后使用([+-]*)(\\d+)\\s*([a-zA-Z]+)matcher.find()matcher.group(i) 提取每个匹配项及其组。但我一直在寻找更优雅的解决方案。

【问题讨论】:

    标签: java regex


    【解决方案1】:

    这应该适合你:

    ^\s*(([+-]*)(\d+)\s*([a-zA-Z]+)\s*)+$
    

    首先,通过添加开始和结束锚点(^$),该模式将不允许无效字符出现在匹配之前或之后的任何位置。

    接下来,我在重复模式 (\s*) 前后添加了可选空格。

    最后,整个模式被包含在一个重复器中,这样它就可以连续出现多次((...)+)。

    另一方面,请注意,我还建议将 [+-]* 更改为 [+-]?,这样它只能出现一次。

    Online Demo

    【讨论】:

    • 谢谢,回复。你是对的,但我没有正确描述它。如果我的输入有效,那么我需要为每个匹配项提取符号、乘数和时间单位值。使用您的解决方案,它将返回 '+1day+1month' 作为 1 个匹配而不是两个 '+1day' 和 '+1month'
    • 您当然可以遍历比赛中的所有组。如果这不能很好地满足您的需求,只需多次调用 matcher.find^\s*([+-]*)(\d+)\s*([a-zA-Z]+) 可能会更简单,每次都传入下一个起始索引。
    【解决方案2】:

    你可以使用 ^$ 来匹配字符串的开始/结束

    ^\s*(?:([+-]?)(\d+)\s*([a-z]+)\s*)+$
    

    https://regex101.com/r/lM7dZ9/2

    有关您的示例,请参阅Unit Tests。基本上,您只需要允许模式重复并强制在匹配之间除了空格之外什么都不会出现。

    结合行开始/结束匹配,你就完成了。

    【讨论】:

      【解决方案3】:

      您可以在Java中使用String.matchesMatcher.matches来匹配整个区域。

      Java 示例:

      public class RegTest {
      
          public static final Pattern PATTERN = Pattern.compile(
                  "(\\s*([+-]?)(\\d+)\\s*([a-zA-Z]+)\\s*)+");
      
          @Test
          public void testDays() throws Exception {
              assertTrue(valid("1 day"));
              assertTrue(valid("-1 day"));
              assertTrue(valid("+1day-1month"));
              assertTrue(valid("+1day -1month"));
              assertTrue(valid("   +1day  +1month   "));
      
              assertFalse(valid("+1day###+1month"));
              assertFalse(valid(""));
              assertFalse(valid("++1day-1month"));
          }
      
          private static boolean valid(String s) {
              return PATTERN.matcher(s).matches();
          }
      }
      

      【讨论】:

        【解决方案4】:

        你可以这样继续:

        String p = "\\G\\s*(?:([-+]?)(\\d+)\\s*([a-z]+)|\\z)";
        
        Pattern RegexCompile = Pattern.compile(p, Pattern.CASE_INSENSITIVE);
        
        String s = "+1day 1month";
        
        ArrayList<HashMap<String, String>> results = new ArrayList<HashMap<String, String>>(); 
        
        Matcher m = RegexCompile.matcher(s);
        boolean validFormat = false;        
        
        while( m.find() ) {
            if (m.group(1) == null) {
                // if the capture group 1 (or 2 or 3) is null, it means that the second
                // branch of the pattern has succeeded (the \z branch) and that the end
                // of the string has been reached. 
                validFormat = true;
            } else {
                // otherwise, this is not the end of the string and the match result is
                // "temporary" stored in the ArrayList 'results'
                HashMap<String, String> result = new HashMap<String, String>();
                result.put("sign", m.group(1));
                result.put("multiplier", m.group(2));
                result.put("time_unit", m.group(3));
                results.add(result);
            }
        }
        
        if (validFormat) {
            for (HashMap item : results) {
                System.out.println("sign: " + item.get("sign")
                                 + "\nmultiplier: " + item.get("multiplier")
                                 + "\ntime_unit: " + item.get("time_unit") + "\n");
            }
        } else {
            results.clear();
            System.out.println("Invalid Format");
        }
        

        \G 锚点匹配字符串的开头或上一次匹配之后的位置。在此模式中,它确保所有匹配项都是连续的。如果到达字符串的末尾,则证明该字符串从头到尾都是有效的。

        【讨论】:

          猜你喜欢
          • 2014-08-19
          • 1970-01-01
          • 2016-10-01
          • 2015-01-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-22
          相关资源
          最近更新 更多