【问题标题】:Regular Expression for separating strings enclosed in parentheses [duplicate]用于分隔括号中的字符串的正则表达式[重复]
【发布时间】:2018-05-08 09:58:42
【问题描述】:

我有一个String,其中包含 2 或 3 个公司名称,每个名称都用括号括起来。每个公司名称还可以包含括号中的单词。我需要使用正则表达式将它们分开,但没有找到方法。

我的inputStr

(Motor (Sport) (racing) Ltd.) (Motorsport racing (Ltd.)) (Motorsport racing Ltd.)
or 
(Motor (Sport) (racing) Ltd.) (Motorsport racing (Ltd.))

预期结果是:

str1 = Motor (Sport) (racing) Ltd.
str2 = Motorsport racing (Ltd.)
str3 = Motorsport racing Ltd.

我的代码:

String str1, str2, str3;
Pattern p = Pattern.compile("\\((.*?)\\)");
Matcher m = p.matcher(inputStr);
int index = 0;
while(m.find()) {

    String text = m.group(1);
    text = text != null && StringUtils.countMatches(text, "(") != StringUtils.countMatches(text, ")") ? text + ")" : text;

    if (index == 0) {
        str1= text;
    } else if (index == 1) {
        str2 = text;
    } else if (index == 2) {
        str3 = text;
    }

    index++;
}

这适用于str2str3,但不适用于str1

当前结果:

str1 = Motor (Sport)
str2 = Motorsport racing (Ltd.)
str3 = Motorsport racing Ltd.

【问题讨论】:

  • 你能告诉我们更多关于输入的信息吗?例如,我可以看到公司信息以(Ltd.)Ltd. 结尾是始终设置在那里还是可以更改?
  • 试试\(((?:[^()]+|\([^\)]*\))*)\)。现场演示(右图):regex101.com/r/ppnfjy/1
  • 您不应该将正则表达式用于嵌套结构。但如果你真的需要,请看这里:stackoverflow.com/questions/47162098/…
  • @ErwinBolwidt 您认为需要匹配有问题的嵌套括号吗?
  • @revo 你不是吗?

标签: java regex string


【解决方案1】:

你可以不用正则表达式来解决这个问题;参考这个关于how to find the outermost parentheses的问题。

这是一个例子:

import java.util.Stack;

public class Main {

    public static void main(String[] args) {
        String input = "(Motor (Sport) (racing) Ltd.) (Motorsport racing (Ltd.)) (Motorsport racing Ltd.)";
        for (int index = 0; index < input.length(); ) {
            if (input.charAt(index) == '(') {
                int close = findClose(input, index);  // find the  close parentheses
                System.out.println(input.substring(index + 1, close));
                index = close + 1;  // skip content and nested parentheses
            } else {
                index++;
            }
        }
    }
    private static int findClose(String input, int start) {
        Stack<Integer> stack = new Stack<>();
        for (int index = start; index < input.length(); index++) {
            if (input.charAt(index) == '(') {
                stack.push(index);
            } else if (input.charAt(index) == ')') {
                stack.pop();
                if (stack.isEmpty()) {
                    return index;
                }
            }
        }
        // unreachable if your parentheses is balanced
        return 0;
    }

}

输出:

Motor (Sport) (racing) Ltd.
Motorsport racing (Ltd.)
Motorsport racing Ltd.

【讨论】:

  • -1 这最多应该是一条评论,将其视为对不同方法的建议。用“使用方法 B”回答问题“我需要方法 A 的帮助”对于消除错误没有帮助
  • @ifloop 即使方法 B 比方法 A 更有效?
  • @ifloop 在语法层面上,你是对的。关于解决问题的方法,我不同意。替代方法很有用。您可能想查看这篇关于 SO 不受欢迎的元帖子:meta.stackoverflow.com/questions/366692/…
  • @ifloop 你是对的,除了如果他不想要任何不同的方法,OP 会提到的部分。
  • 解决问题的不同方法可以拓宽您的视野并帮助您更好地理解问题。如果该替代方法不能解决 OPs 问题,那么他可以轻松地将其作为回应。
【解决方案2】:

所以我们可以假设括号最多可以嵌套两层。所以我们可以在没有太多魔法的情况下做到这一点。我会使用这段代码:

List<String> matches = new ArrayList<>();
Pattern p = Pattern.compile("\\([^()]*(?:\\([^()]*\\)[^()]*)*\\)");
Matcher m = p.matcher(inputStr);
while (m.find()) {
    String fullMatch = m.group();
    matches.add(fullMatch.substring(1, fullMatch.length() - 1));
}

解释:

  • 首先我们匹配一个括号:\\(
  • 然后我们匹配一些非括号字符:[^()]*
  • 然后是零次或多次:(?:...)* 我们会在括号中看到一些内容,然后再次看到一些非括号中的内容:
  • \\([^()]*\\)[^()]* - 重要的是我们不允许内括号内有任何括号
  • 然后是右括号:\\)
  • m.group(); 返回实际的完全匹配。
  • fullMatch.substring(1, fullMatch.length() - 1) 删除开头和结尾的括号。你也可以和另一个小组一起做。我只是不想让正则表达式更丑。

【讨论】:

  • 你非常感谢这个
【解决方案3】:

为什么不直接使用堆栈来解决呢?它只有 O(n) 复杂度

  1. 只需解析字符串,每次遇到 '(' 时,将其推送到堆栈中,每次遇到 ')' 时,从堆栈中弹出。 否则,将字符放入缓冲区。
  2. 如果在推送 '(' 时堆栈为空,则表示它位于公司名称中,因此也将其放入缓冲区中。
  3. 同样,如果堆栈在弹出后不为空,则将 ')' 放入缓冲区中,因为它是公司名称的一部分。
  4. 如果出栈后栈为空,则表示第一个公司名已经结束,缓冲区值为公司名,清空缓冲区。

    String string = "(Motor (Sport) (racing) Ltd.) (Motorsport racing (Ltd.)) (Motorsport racing Ltd.)";
    List<String> result = new ArrayList();
    StringBuffer buffer = new StringBuffer();
    
    Stack<Character> stack = new Stack<Character>();
    for (int j = 0; j < string.length(); j++) {
        if (string.charAt(j) == '(') {
            if (!stack.empty())
                buffer.append('(');
            stack.push('(');
        } else if (string.charAt(j) == ')') {
            stack.pop();
            if (stack.empty()) {
                result.add(buffer.toString());
                buffer = new StringBuffer();
            }else
                buffer.append(')');
        }else{
            buffer.append(string.charAt(j));
        }
    }
    
    for(int i=0;i<result.size();i++){
        System.out.println(result.get(i));
    }
    

【讨论】:

  • -1 这应该是一条评论,将其视为对不同方法的建议。用“使用方法 B”回答问题“我需要方法 A 的帮助”在消除错误的意义上没有帮助(参见 cmets of neng_liu 的回答)
  • @ifloop 很酷,你解释了你的 -1。但是,这种 cmets 和 -1 -s 使 SO 成为一个不受欢迎的地方。我认为可以发布开箱即用的答案。有时这些是流行的答案,即当 OP 想要使用正则表达式解析 xml 时。
  • 因为你唯一推送到堆栈的是'(',你不需要一个真正的堆栈,只需要一个int depth来跟踪堆栈深度,即未关闭的数量你有括号。
  • @Boann 你是对的。我当时没有想到。
【解决方案4】:

我看到每个左括号都有一个 右括号 对应物,我看不出有任何可能出现 嵌套 括号。因此,平衡括号而没有嵌套的括号会导致这样的正则表达式:

\(((?:[^()]*|\([^)]*\))*)\)

您只需要访问第一个捕获组。

Live demo

细分

  • \( 匹配左括号
    • ( 捕获组 1 开始
      • (?: 非捕获组 1 的开始
        • [^()]* 匹配/不在集合中的字符,可选
        • |或者
        • \([^\)]*\)匹配括号组
      • )*尽可能,非捕获组1结束
    • ) 捕获组 1 结束
  • \) 匹配右括号

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多