【问题标题】:Iterating through String with .find() in Java regex在 Java 正则表达式中使用 .find() 遍历字符串
【发布时间】:2012-10-24 03:11:56
【问题描述】:

我目前正在尝试使用正则表达式解决来自 codingbat.com 的问题。

我是新来的,所以分步解释将不胜感激。我可以使用 String 方法相对容易地解决这个问题,但我正在尝试使用正则表达式。

这里是提示: 给定一个字符串和一个非空单词字符串,返回一个由字符串中每个单词出现之前和之后的每个字符组成的字符串。忽略单词前后没有字符的情况,如果字符在两个单词之间,它可能会被包含两次。

wordEnds("abcXY123XYijk", "XY") → "c13i"
wordEnds("XY123XY", "XY") → "13"
wordEnds("XY1XY", "XY") → "11"

到目前为止我的代码:

String regex = ".?" + word+ ".?";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);

String newStr = "";
while(m.find())
    newStr += m.group().replace(word, "");

return newStr;

问题是当连续有多个单词实例时,程序会错过单词前面的字符,因为 m.find() 超出了它。

例如:wordEnds("abc1xyz1i1j", "1")应该返回"cxziij",但是我的方法返回"cxzij",而不是重复"i"

我希望有一个简洁的解决方案,并附上我可以应用于其他一般正则表达式问题的解释。

【问题讨论】:

  • 查看这个关于环视正则表达式的答案stackoverflow.com/a/2995621/324900
  • @user1796994 查看我未删除、已修复的单行解决方案答案
  • @user1796994 请参阅我的(已编辑)答案,了解如何在一行(包括测试代码)中执行此操作。您可能不会认为它“不杂乱”,但它肯定比多线解决方案 IMHO 更杂乱。

标签: java regex string


【解决方案1】:

这是一个单行解决方案:

String wordEnds = input.replaceAll(".*?(.)" + word + "(?:(?=(.)" + word + ")|(.).*?(?=$|." + word + "))", "$1$2$3");

这将您的边缘情况匹配为非捕获组中的前瞻,然后匹配通常的(消费)情况。

请注意,您的要求不需要迭代,只有您的问题标题假定它是必要的,但事实并非如此。

还请注意,为了绝对安全,您应该转义 word 中的所有字符,以防它们中的任何一个是特殊的“正则表达式”字符,所以如果您不能保证,您需要使用 Pattern.quote(word) 而不是word.

这是对通常情况和边缘情况的测试,表明它有效:

public static String wordEnds(String input, String word) {
    word = Pattern.quote(word); // add this line to be 100% safe
    return input.replaceAll(".*?(.)" + word + "(?:(?=(.)" + word + ")|(.).*?(?=$|." + word + "))", "$1$2$3");
}

public static void main(String[] args) {
    System.out.println(wordEnds("abcXY123XYijk", "XY"));
    System.out.println(wordEnds("abc1xyz1i1j", "1"));
}

输出:

c13i
cxziij

【讨论】:

  • 这不太对 - 我稍后会回到这个问题
  • @Bohemian 不正确,他需要cxziij 作为输出而不是cxzi..这就是我使用环视的原因...
  • @Fake.It.Til.U.Make.It 虽然我之前说过这不是解决方案,但我已经找到了真正(真正)有效的正则表达式- 查看完全正常工作单行解决方案的编辑答案。
【解决方案2】:

使用零宽度断言的正向lookbehind和正向lookahead

(?<=(.)|^)1(?=(.)|$)
    ^     ^     ^-looks for a character after 1 and captures it in group2
    |     |->matches 1..you can replace it with any word
    |
    |->looks for a character just before 1 and captures it in group 1..this is zero width assertion that doesn't move forward to match.it is just a test and thus allow us to capture the values

$1$2 包含你的价值..继续寻找直到最后

所以应该是这样的

String s1 = "abcXY123XYiXYjk";
String s2 = java.util.regex.Pattern.quote("XY");
String s3 = "";
String r = "(?<=(.)|^)"+s2+"(?=(.)|$)";
Pattern p = Pattern.compile(r);
Matcher m = p.matcher(s1);
while(m.find()) s3 += m.group(1)+m.group(2);
//s3 now contains c13iij

作品here

【讨论】:

  • -1 Waaaaaay 太复杂了,实际上是错误的。你不需要环顾四周!只需使用(.) - 他说“如果没有字符,则不匹配”,但是您通过匹配开始和结束来完成任务,这实际上 不是 OP 所说的他想要
  • @Bohemian 我喜欢你的原始答案,因为它很简单,所以如果你能发布它(使用 str.replace),我将不胜感激
  • @Bohemian 甚至可以告诉你这个正则表达式会失败的一个案例
  • @Fake.It.Til.U.Make.It 我已经平淡无奇,现在感觉好多了 - 我已经删除了我的 -1。那有点苛刻。我的批评是,尽管这太复杂了。
  • @Bohemian 怎么太复杂了?像“aaaX”这样的情况需要匹配开始和结束,而像“aXaXa”这样的情况需要环顾四周。删除它们将阻止正确处理边缘情况。
【解决方案3】:

使用正则表达式如下:

Matcher m = Pattern.compile("(.|)" + Pattern.quote(b) + "(?=(.?))").matcher(a);
for (int i = 1; m.find(); c += m.group(1) + m.group(2), i++);

检查this demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-30
    • 2017-10-23
    • 2019-12-02
    相关资源
    最近更新 更多