从字符串中提取两个数字

3

我有一个类似以下的字符串:

"some value is 25 but must not be bigger then 12"

我想从字符串中提取两个数字。

这两个数字都是整数。

第一个数字前可能没有文本,第二个数字后可能有其他文本。

我尝试使用正则表达式和分组来提取数字,但失败了:

public MessageParser(String message) {
    Pattern stringWith2Numbers = Pattern.compile(".*(\\d?).*(\\d?).*");
    Matcher matcher = stringWith2Numbers.matcher(message);
    if (!matcher.matches()) {
        couldParse = false;
        firstNumber = 0;
        secondNumber = 0;
    } else {
        final String firstNumberString = matcher.group(1);
        firstNumber = Integer.valueOf(firstNumberString);
        final String secondNumberString = matcher.group(2);
        secondNumber = Integer.valueOf(secondNumberString);

        couldParse = true;
    }
}

任何帮助都是值得赞赏的。

3个回答

8

您的模式应该更像:

Pattern stringWith2Numbers = Pattern.compile("\\D*(\\d+)\\D+(\\d+)\\D*");

你需要接受\\d+,因为它可以是一个或多个数字。


\D*(\d+)^\D](\d+)^\D会不会更合适一些呢?因为我们明确不想要数字,而"."有可能在我们到达\d之前匹配一个数字。 - Benjamin Autin
1
第二个 \\D* 应该改为 \\D+。现在的正则表达式可以匹配字符串 "42",将 "4" 匹配到第一组,将 "2" 匹配到第二组。 - Alan Moore

3
你的".*"模式是贪婪的,它们会尽可能地匹配尽可能多的内容 - 这将导致第一个".*"匹配整个字符串,使其余部分无效。此外,你的"\\d?"子句表示一个可选的单个数字,这都不是你想要的。
以下是更符合你要求的内容: Pattern stringWith2Numbers = Pattern.compile(".*?(\\d+).*?(\\d+).*?"); 当然,既然你不在意数字前面或后面的内容,为什么还要加呢? Pattern stringWith2Numbers = Pattern.compile("(\\d+).*?(\\d+)"); 这应该就可以了。
编辑:从写绝妙漫画的时间中抽出来,Alan Moore在评论中指出了我的解决方案存在一些问题。首先,如果字符串中只有一个多位数,我的解决方案会得到错误的结果。将其应用于"This 123 is a bad string"将导致它返回"12"和"3",而实际上应该失败。更好的正则表达式应该规定两个数字之间必须至少有一个非数字字符,如下所示: Pattern stringWith2Numbers = Pattern.compile("(\\d+)\\D+(\\d+)"); 此外,matches()将模式应用于整个字符串,基本上是将其括在^$中。虽然find()可以解决问题,但这不是OP所使用的方法。因此,我们需要在两个数字前后加上那些“无用”的子句。(尽管将它们明确地匹配为非数字比通配符更好。)所以它看起来像这样: Pattern stringWith2Numbers = Pattern.compile("\\D*(\\d+)\\D+(\\d+)\\D*"); ... 值得注意的是,这与jjnguy的答案几乎完全相同。

“.*” 会不会在数字模式之间“吃掉”第二个数字? - MartinStettner
1
不是。星号后面的问号表示它应该匹配最短可能的字符串,因此它将匹配第二个数字之前的所有内容。 - BlairHippo
虽然现在两位数字子句不再是可选的,问号变得不那么重要了--如果你绝对确定字符串中只会有两个数字而且仅有两个数字,那么你就不需要它。但是如果有更多数字,则问号很重要。以“1和2和3”作为示例字符串:使用问号,你可以得到1和2被提取出来。没有问号,1和3将是两个提取出的值。 - BlairHippo
1
你可以像@jinguy一样使用\\D*而不是.*来避免这个问题。但是中间的那个应该是\\D+,就像我在他的答案评论中解释的那样。 - Alan Moore
我没有考虑到这一点,但你是对的;如果输入文本中确实有两个数字,我的解决方案就没问题了。但是,如果文本是“我有一个42数字”,它会返回“4”和“2”,而实际上应该简单地失败。我甚至没有想到匹配/查找的事情,在过去曾经把我绊倒过。会编辑答案,既然它尽管存在这些缺陷还被接受了。 :-) - BlairHippo

2
你的正则表达式匹配了,但是第一个 .* 把所有内容都吃掉了,剩下的匹配空字符串。
请将你的正则表达式改为 "\\D*(\\d+)\\D+(\\d+)\\D*"
这个正则表达式应该这样理解:至少有一个数字后跟至少一个非数字字符,再跟至少一个数字。

2
如果您使用 OP 所做的 matches() 方法,则前导和尾随的 .* 是必要的。如果您使用执行更传统的“它在那里某个地方”类型的正则表达式匹配的 find() 方法,则您的正则表达式将与其一起工作。 - Alan Moore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接