【问题标题】:Match Substring that contains separators in full string匹配包含完整字符串中分隔符的子字符串
【发布时间】:2017-05-26 11:13:04
【问题描述】:

我不知道如何表达这个问题。长话短说,我想从In: a (b) 行中提取两个字符串(ab)。在几乎所有情况下a=b,但以防万一,我已经将它们分开了。问题:两个字符串都可以包含任何字符,包括 Unicode、空格、标点符号和括号。

1: In: ThisName (ThisName) is in this list
2: In: OtherName (With These) (OtherName (With These)) is in this list
3: In: Really Annoying (Because) Separators (Really Annoying (Because) Separators) is in this list

第 1 行,简单:^\w+:\s(?'a'.+?)\s\((?'b'.+)\)a:ThisNameb:ThisName

第2行,和之前一样:a:OtherNameb: With These) (OtherName (With These)

第 2 行,懒惰:^\w+:\s(?'a'.+?)\s\((?'b'.+?)\)a:OtherNameb:With These

3 号线,总台

这可能吗?也许我需要走另一条路?我们知道需要一组括号。也许我必须走一条数学路线,计算括号的数量并找到那条路线来确定哪条路线实际上应该包含b?以某种方式计算每个打开和关闭。

我一直在玩什么:https://regex101.com/r/8YIweJ/2

顺便说一句,如果我可以更改输入格式,我肯定会的。

添加的问题:如果这不可能,是否一直假设a=b 会使这更容易?我想不出它会怎样。

【问题讨论】:

  • 不能用非递归正则表达式匹配括号,Java 的正则表达式不是递归的。您的行是否总是在“重要”括号后加上“在此列表中”?
  • 问题不清楚。您想在括号内分隔所有不同的字符串吗?即,带有标记 a(b)c(d(e))f 的字符串应该返回 a,b,c,d,e,f,其中每个 {x: [a,f]} 可以由一个或多个词?
  • @RealSkeptic "In:" 和 "is in this list" 将始终存在并以这种方式精确打印。
  • 你能保证 A 和 B 的值中的括号(如果存在的话)总是匹配的吗?也就是说,您无法获得Foo ( BarGreat :-) 之类的信息?
  • @Amal 我希望abIn: a (b) is in this list 行中,例如In: A (Has These) (B is different but (Has These Too)) 将导致a:A (Has These)b:B is different but (Has These Too) 并使其更复杂,@987654345 @ 和b 可以包含任何字符并且可以相同,所以会发生这种情况:In: A!@ (☢!) (A!@ (☢!))In: A!@ (☢!) (Other Entirely)

标签: java regex pattern-matching match


【解决方案1】:

我的 cmets 嵌入在 processInput 方法中。

public static void main(String[] args)
{
    String input = "1: In: ThisName (ThisName) is in this list\n" +
        "2: In: OtherName (With These) (OtherName (With These)) is in this list\n" +
        "3: In: Really Annoying (Because) Separators (Really Annoying (Because) Separators) is in this list\n" +
        "4: In: Not the Same (NotTheSame) is in this list\n" +
        "5: In: A = (B) (A = (B)) is in this list\n" +
        "6: In: A != (B) (A != B) is in this list\n";

    for (String line : input.split("\n"))
    {
        processInput(line);
    }
}


public static void processInput(String line)
{
    // Parse the relevant part from the input.
    Matcher inputPattern = Pattern.compile("(\\d+): In: (.*) is in this list").matcher(line);
    if (!inputPattern.matches())
    {
        System.out.println(line + " is not valid input");
        return;
    }
    String inputNum = inputPattern.group(1);
    String aAndB = inputPattern.group(2);

    // Check if a = b.
    Matcher aEqualsBPattern = Pattern.compile("(.*) \\(\\1\\)").matcher(aAndB);
    if (aEqualsBPattern.matches())
    {
        System.out.println("Input " + inputNum + ":");
        System.out.println("a = b = " + aEqualsBPattern.group(1));
        System.out.println();
        return;
    }

    // Check if a and b have no parentheses.
    Matcher noParenthesesPattern = Pattern.compile("([^()]*) \\(([^()]*)\\)").matcher(aAndB);
    if (noParenthesesPattern.matches())
    {
        System.out.println("Input " + inputNum + ":");
        System.out.println("a = " + noParenthesesPattern.group(1));
        System.out.println("b = " + noParenthesesPattern.group(2));
        System.out.println();
        return;
    }

    // a and b have one or more parentheses in them.
    // All you can do now is guess what a and b are.

    // There is at least one " (" in the string.
    String[] split = aAndB.split(" \\(");
    for (int i = 0; i < split.length - 1; i++)
    {
        System.out.println("Possible Input " + inputNum + ":");
        System.out.println("possible a = " + mergeParts(split, 0, i));
        System.out.println("possible b = " + mergeParts(split, i + 1, split.length - 1));
        System.out.println();
    }
}


private static String mergeParts(String[] aAndBParts, int startIndex, int endIndex)
{
    StringBuilder s = new StringBuilder(getPart(aAndBParts, startIndex));
    for (int j = startIndex + 1; j <= endIndex; j++)
    {
        s.append(" (");
        s.append(getPart(aAndBParts, j));
    }
    return s.toString();
}


private static String getPart(String[] aAndBParts, int j)
{
    if (j != aAndBParts.length - 1)
    {
        return aAndBParts[j];
    }
    return aAndBParts[j].substring(0, aAndBParts[j].length() - 1);
}

执行上述代码输出:

Input 1:
a = b = ThisName

Input 2:
a = b = OtherName (With These)

Input 3:
a = b = Really Annoying (Because) Separators

Input 4:
a = Not the Same
b = NotTheSame

Input 5:
a = b = A = (B)

Possible Input 6:
possible a = A !=
possible b = B) (A != B

Possible Input 6:
possible a = A != (B)
possible b = A != B

【讨论】:

  • 感谢您抽出宝贵的时间!该方法的最后一部分,试图猜测括号在哪里/应该在哪里也很直观。这里有几个项目我不会想到尝试。最终,我选择发送整个字符串 (a + b),以防括号无法破译并将其标记为无效。我相信我会在每一行添加一小部分额外的变量来进一步完善并创建一个类来处理它。再次感谢!
【解决方案2】:

我不会为此使用正则表达式。遵循这种算法:

  1. 如果我遵循您的问题,找到 ( 的第一个索引应该会给您"a" 字符串
  2. 从该索引开始,使用 charAt 逐个字符地遍历字符串。击中 a 时向上计数(到达 a 时向下计数)。一旦你在这个计数器中击中零,那么你的括号匹配并且你有你的“b”字符串结尾的位置。

看起来可能有多个字符串组成“B”(从第 3 行开始),因此您可以按照上面的步骤 2 继续迭代字符串,将字符串添加到列表或字符串构建器中.

【讨论】:

  • 那个算法不能解决问题。 “b”字符串不是从 first 左括号开始。它从匹配最后一个右括号的括号开始。据我了解,不能保证表达式中的括号是匹配的。
  • 是的。唯一可以保证匹配的括号是包含b 的括号。甚至我都没有想到流氓“(”可能在其中任何一个中。现在你已经启发了我,我真的开始怀疑这是可能的。或者,至少,制定一个流氓规则括号不能存在。也就是说,如果它们发生了,可以通过对他的建议进行一些补充来确定,为那场比赛制作一个标志,并弄清楚如何单独处理它。很可能只是在那些罕见的情况下完全结合 a 和 b。
【解决方案3】:

好吧,您可以解析文本,但不能使用正则表达式,并且至少满足以下条件之一:

  1. B 表达式中的括号保证正确匹配。也就是说,没有)) ((:-) 等。
  2. A 和 B 完全相同。在这种情况下,即使您在其中有不匹配的括号,例如Hello (-: (Hello (-:),您知道第二个Hello 之前的( 是“正确”的那个。

如果你不能做出这些保证,那么你应该编写一个isMatchedParenthesis(String) 方法来检查所有括号是否正确匹配。有一个计数器,从零开始,扫描字符串。

  • 对于字符串中的每个字符:
    • 如果当前字符是(counter++
    • 如果当前字符是)counter--
    • 如果计数器为负,则返回 false
  • 如果最后计数器为正,则返回 false。否则为真。

用那个方法测试你的字符串。如果它有效,您可以依靠使用括号匹配找到“重要”括号。如果它返回false,你可以尝试假设两个字符串相同的回退方法。

平衡时找到重要的括号

  • 找到最右边)的索引(使用lastIndexOf)。
  • counter=0
  • 对于从该索引下降到 4 的每个字符(In: 之后的字符:
    • 如果是)counter++
    • 如果是(counter--
    • 如果counter==0 停止,则返回当前索引。

现在您有了有效括号的索引。您的 A 是 4 和该索引 - 1 之间的子字符串(请记住 ( 之前的空格)。你的 B 是从那个索引+1 到你最先找到的右边) 的索引。

后备方法

假设你的括号不平衡。你能做点什么吗?

  • 列出字符串中(的所有索引。
  • 如果列表的长度是偶数 - 错误字符串,报告给用户。
  • 如果长度是奇数,取中间(的索引。假设 A 和 B 相同,则它们的 ( 编号应该相同,因此其左侧和右侧具有相同编号的 ( 就是您的候选人。
  • 像以前一样提取 A 和 B。如果它们不相等 - 错误字符串,请报告给用户。

【讨论】:

  • 非常感谢plain-jane 打破规则。它帮助我尝试将混乱重新拼凑起来。
猜你喜欢
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 2019-04-24
  • 2021-11-22
  • 1970-01-01
  • 2012-02-29
  • 1970-01-01
  • 2020-04-05
相关资源
最近更新 更多