用于分隔括在括号中的字符串的正则表达式

时间:2018-05-08 09:58:42

标签: java regex string

我有一个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.

4 个答案:

答案 0 :(得分:9)

你可以在没有正则表达式的情况下解决这个问题;请参阅有关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 :(得分:7)

因此我们可以假设括号最多可以嵌套两个级别。所以我们可以毫不费力地做到这一点。我会使用这段代码:

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)从开头和结尾删除括号。你也可以和另一个团队一起做。我只是不想让正则表达式更加丑陋。

答案 2 :(得分:6)

为什么不用堆栈解决它?它只有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));
    }
    

答案 3 :(得分:4)

我看到每个左括号都有一个结束对应物,我没有看到嵌套括号发生的任何可能性。因此,没有嵌套的括号的平衡括号会产生这样的正则表达式:

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

您只需拥有第一个捕获组的访问权限。

Live demo

<强>击穿

  • \(匹配左括号
    • (开始捕获第1组
      • (?:开始非捕获组1
        • [^()]*匹配未设置的字符,可选
        • |
        • \([^\)]*\)匹配括号组
      • )*尽可能地结束非捕获组1
    • )结束捕获第1组
  • \)匹配右括号