【问题标题】:Regular Expression: match everything up to an optional capture group正则表达式:将所有内容匹配到可选的捕获组
【发布时间】:2016-04-24 20:27:13
【问题描述】:

我有以下正则表达式:

(.*)(?:([\+\-\*\/])(-?\d+(?:\.\d+)?))

其目的是以(左表达式)(运算符)(右操作数)形式捕获数学表达式,例如1+2+3 将被捕获为 (1+2)(+)(3)。它还将处理单个操作数,例如1+2 将被捕获为 (1)(+)(2)。

我遇到的问题是这个正则表达式不会匹配没有运算符的单个操作数,例如5 应该在第一个捕获组中匹配,而第二个和第三个 (5)()() 中没有任何内容。如果我将最后一部分设为可选:

(.*)(?:([\+\-\*\/])(-?\d+(?:\.\d+)?))?

那么初始组将始终捕获整个表达式。有什么办法可以让第二部分成为可选的,但它是否优先于第一组所做的贪婪匹配?

【问题讨论】:

  • 为什么不使用解析器?
  • 你尝试过 lazy 匹配吗? (.+?)(?:([-+*\/])(-?\d+(?:\.\d+)?))?
  • 使第一组惰性匹配不起作用,如果我这样做,我无法完全解释我得到的结果,但这不是我想要的。
  • 那么请显示你拥有的不能用惰性匹配匹配的字符串。
  • 1+2+3+4 不起作用。

标签: java regex


【解决方案1】:

说明

此正则表达式将:

  • 捕获直到最后一个操作的数学表达式
  • 捕获最后一次操作
  • 捕获数学表达式中的最后一个数字
  • 假设每个数字可能有一个加号或减号表示该数字是正数还是负数
  • 假设每个数字可能是非整数
  • 假设数学表达式可以包含任意数量的运算,例如:1+21+2+31+2+3+41+2+3+4...
  • 验证字符串是否为数学表达式。这里没有考虑一些边缘情况,例如使用括号或其他复杂的数学符号。

原始正则表达式

注意这是 Java,您需要转义此正则表达式中的反斜杠。要逃避它们,只需将所有 \ 替换为 \\

^(?=(?:[-+*/^]?[-+]?\d+(?:[.]\d+)?)*$)([-+]?[0-9.]+$|[-+]?[0-9.]+(?:[-+*/^][-+]?[0-9.]+)*(?=[-+*/^]))(?:([-+*/^])([-+]?[0-9.]+))?$

说明

概述

在这个表达式中,我首先验证字符串是否仅由操作 -+/*^、可选符号 -+ 以及整数或非整数数字组成。由于已经过验证,表达式的其余部分可以简单地将数字称为[0-9.]+,这提高了可读性。

捕获组

0 获取整个字符串 1 获取整个字符串,但不包括最后一个操作,如果没有操作,则第 1 组将拥有整个字符串 2 获取最后一个操作,如果存在的话 3 获取最后一次操作后的数字和符号

NODE                     EXPLANATION
----------------------------------------------------------------------
  ^                        the beginning of the string
----------------------------------------------------------------------
  (?=                      look ahead to see if there is:
----------------------------------------------------------------------
    (?:                      group, but do not capture (0 or more
                             times (matching the most amount
                             possible)):
----------------------------------------------------------------------
      [-+*/^]?                 any character of: '-', '+', '*', '/',
                               '^' (optional (matching the most
                               amount possible))
----------------------------------------------------------------------
      [-+]?                    any character of: '-', '+' (optional
                               (matching the most amount possible))
----------------------------------------------------------------------
      \d+                      digits (0-9) (1 or more times
                               (matching the most amount possible))
----------------------------------------------------------------------
      (?:                      group, but do not capture (optional
                               (matching the most amount possible)):
----------------------------------------------------------------------
        [.]                      any character of: '.'
----------------------------------------------------------------------
        \d+                      digits (0-9) (1 or more times
                                 (matching the most amount possible))
----------------------------------------------------------------------
      )?                       end of grouping
----------------------------------------------------------------------
    )*                       end of grouping
----------------------------------------------------------------------
    $                        before an optional \n, and the end of
                             the string
----------------------------------------------------------------------
  )                        end of look-ahead
----------------------------------------------------------------------
  (                        group and capture to \1:
----------------------------------------------------------------------
    [-+]?                    any character of: '-', '+' (optional
                             (matching the most amount possible))
----------------------------------------------------------------------
    [0-9.]+                  any character of: '0' to '9', '.' (1 or
                             more times (matching the most amount
                             possible))
----------------------------------------------------------------------
    $                        before an optional \n, and the end of
                             the string
----------------------------------------------------------------------
   |                        OR
----------------------------------------------------------------------
    [-+]?                    any character of: '-', '+' (optional
                             (matching the most amount possible))
----------------------------------------------------------------------
    [0-9.]+                  any character of: '0' to '9', '.' (1 or
                             more times (matching the most amount
                             possible))
----------------------------------------------------------------------
    (?:                      group, but do not capture (0 or more
                             times (matching the most amount
                             possible)):
----------------------------------------------------------------------
      [-+*/^]                  any character of: '-', '+', '*', '/',
                               '^'
----------------------------------------------------------------------
      [-+]?                    any character of: '-', '+' (optional
                               (matching the most amount possible))
----------------------------------------------------------------------
      [0-9.]+                  any character of: '0' to '9', '.' (1
                               or more times (matching the most
                               amount possible))
----------------------------------------------------------------------
    )*                       end of grouping
----------------------------------------------------------------------
    (?=                      look ahead to see if there is:
----------------------------------------------------------------------
      [-+*/^]                  any character of: '-', '+', '*', '/',
                               '^'
----------------------------------------------------------------------
    )                        end of look-ahead
----------------------------------------------------------------------
  )                        end of \1
----------------------------------------------------------------------
  (?:                      group, but do not capture (optional
                           (matching the most amount possible)):
----------------------------------------------------------------------
    (                        group and capture to \2:
----------------------------------------------------------------------
      [-+*/^]                  any character of: '-', '+', '*', '/',
                               '^'
----------------------------------------------------------------------
    )                        end of \2
----------------------------------------------------------------------
    (                        group and capture to \3:
----------------------------------------------------------------------
      [-+]?                    any character of: '-', '+' (optional
                               (matching the most amount possible))
----------------------------------------------------------------------
      [0-9.]+                  any character of: '0' to '9', '.' (1
                               or more times (matching the most
                               amount possible))
----------------------------------------------------------------------
    )                        end of \3
----------------------------------------------------------------------
  )?                       end of grouping
----------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string
----------------------------------------------------------------------

示例

示例文本

1+2+-3

样本捕获组

[0] = 1+2+-3
[1] = 1+2
[2] = +
[3] = -3

在线演示:http://fiddle.re/b2w5wa

示例文本

-3

样本捕获组

[0] = -3
[1] = -3
[2] = 
[3] = 

在线演示:http://fiddle.re/07kqra

Java 代码示例

import java.util.regex.Pattern;
import java.util.regex.Matcher;
class Module1{
  public static void main(String[] asd){
  String sourcestring = "source string to match with pattern";
  Pattern re = Pattern.compile("^(?=(?:[-+*/^]?[-+]?\\d+(?:[.]\\d+)?)*$)([-+]?[0-9.]+$|[-+]?[0-9.]+(?:[-+*/^][-+]?[0-9.]+)*(?=[-+*/^]))(?:([-+*/^])([-+]?[0-9.]+))?$",Pattern.CASE_INSENSITIVE);
  Matcher m = re.matcher(sourcestring);
  int mIdx = 0;
    while (m.find()){
      for( int groupIdx = 0; groupIdx < m.groupCount()+1; groupIdx++ ){
        System.out.println( "[" + mIdx + "][" + groupIdx + "] = " + m.group(groupIdx));
      }
      mIdx++;
    }
  }
}

【讨论】:

  • 实际上,这与输入只是单个操作数(例如单个数字或小数,正数或负数 -3.1)的情况不匹配。在这种情况下,我希望在捕获组 1 中匹配它,而组 2 和组 3 将是空的。知道这是否可能吗?我可以通过在提取其余部分之前先执行一个额外的步骤来检查单个操作数来轻松地在代码中执行此操作,但是在一个正则表达式中执行此操作会很好且干净。
  • @DaveJohnston,当然。我已更新我的答案以匹配您的条件,即表达式可能只有一个项目。
  • 我发现随着更改,它停止将任何内容提取到第 2 组和第 3 组中。它现在确实匹配简单数字,但所有内容都匹配到第 1 组中。
  • Ugg,你是对的。好的,我发现并解决了这个错误,在此过程中,我对表达式进行了大规模更改,以提高可读性和debugging。很抱歉造成混乱。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-17
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 2020-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多