通过拆分正则表达式从Java中的String中提取数字

时间:2014-10-06 21:34:42

标签: java regex

我想从字符串中提取数字:

String numbers[] = "M0.286-3.099-0.44c-2.901,-0.436,,,123,0.123,.34".split(PATTERN);

从这样的字符串中我想提取这些数字:

  • 0.286
  • -3.099
  • -0.44
  • -2.901
  • -0.436
  • 123
  • 0.123
  • 0.34

那是:

  • 可能有垃圾字符,如" M"," c"," c"
  • " - "标志是包含在号码中,而不是分开
  • A"数字"可以是Float.parseFloat可以解析的任何内容,因此.34有效

到目前为止我所拥有的:

String PATTERN = "([^\\d.-]+)|(?=-)";

这在某种程度上起作用,但显然远非完美:

  • 不会跳过起始垃圾" M"在示例中
  • 不会处理连续垃圾,例如中间的,,,

如何修复PATTERN以使其有效?

6 个答案:

答案 0 :(得分:3)

您可以使用这样的正则表达式:

([-.]?\d+(?:\.\d+)?)

<强> Working demo

enter image description here

匹配信息:

MATCH 1
1.  [1-6]   `0.286`
MATCH 2
1.  [6-12]  `-3.099`
MATCH 3
1.  [12-17] `-0.44`
MATCH 4
1.  [18-24] `-2.901`
MATCH 5
1.  [25-31] `-0.436`
MATCH 6
1.  [34-37] `123`
MATCH 7
1.  [38-43] `0.123`
MATCH 8
1.  [44-47] `.34`

<强>更新

Jawee 的方法

正如 Jawee 在他的评论中指出.34.34存在问题,因此您可以使用他的正则表达式解决此问题。感谢Jawee指出这一点。

(-?(?:\d+)?\.?\d+)

要了解此正则表达式背后发生的事情,您可以查看Debuggex  图像:

Regular expression visualization

引擎说明

1st Capturing group (-?(?:\d+)?\.?\d+)
   -? -> matches the character - literally zero and one time
   (?:\d+)? -> \d+ match a digit [0-9] one and unlimited times (using non capturing group)
   \.? matches the character . literally zero and one time
   \d+ match a digit [0-9] one and unlimited times

答案 1 :(得分:3)

试试这个(-?(?:\d+)?\.?\d+)
示例如下:

Demo Here

非常感谢 nhahtdh 的评论。这是真的,我们可以更新如下:

[-+]?(?:\d+(?:\.\d*)?|\.\d+)

Updated Demo Here

实际上,如果我们采用所有可能的浮点输入字符串格式(例如:Infinity-Infinity000xffp23d88F),那么它可能有点复杂。但是,我们仍然可以在Java代码下面实现它:

String sign = "[-+]?";
String hexFloat = "(?>0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?)";
String nan = "(?>NaN)";
String inf = "(?>Infinity)";

String dig = "(?>\\d+(?:\\.\\d*)?|\\.\\d+)";
String exp = "(?:[eE][-+]?\\d+)?";
String suf = "[fFdD]?";
String digFloat = "(?>" + dig + exp + suf + ")";

String wholeFloat = sign + "(?>" + hexFloat + "|" + nan + "|" + inf + "|" + digFloat + ")";

String s = "M0.286-3.099-0.44c-2.901,-0.436,,,123,0.123d,.34d.34.34M24.NaNNaN,Infinity,-Infinity00,0xffp23d,88F";

Pattern floatPattern = Pattern.compile(wholeFloat);
Matcher matcher = floatPattern.matcher(s);
int i = 0;
while (matcher.find()) {
    String f =  matcher.group();
    System.out.println(i++ + " : " + f + " --- " +  Float.parseFloat(f) );
}  

然后输出如下:

0 : 0.286 --- 0.286
1 : -3.099 --- -3.099
2 : -0.44 --- -0.44
3 : -2.901 --- -2.901
4 : -0.436 --- -0.436
5 : 123 --- 123.0
6 : 0.123d --- 0.123
7 : .34d --- 0.34
8 : .34 --- 0.34
9 : .34 --- 0.34
10 : 24. --- 24.0
11 : NaN --- NaN
12 : NaN --- NaN
13 : Infinity --- Infinity
14 : -Infinity --- -Infinity
15 : 00 --- 0.0
16 : 0xffp23d --- 2.13909504E9
17 : 88F --- 88.0

答案 2 :(得分:2)

使用您自己制作的正则表达式,您可以按如下方式解决:

String[] numbers = "M0.286-3.099-0.44c-2.901,-0.436,,,123,0.123,.34"
                          .replaceAll(PATTERN, " ")
                          .trim()
                          .split(" +");

另一方面,如果我是你,我会改为循环:

Matcher m = Pattern.compile("[.-]?\\d+(\\.\\d+)?").matcher(input);
List<String> matches = new ArrayList<>();
while (m.find())
    matches.add(m.group());

答案 3 :(得分:2)

你可以在一行中完成(但比aioobe的答案少一步!):

String[] numbers = "M0.286-3.099-0.44c-2.901,-0.436,,,123,0.123,.34"
    .replaceAll("^[^.\\d-]+|[^.\\d-]+$", "") // remove junk from start/end
    .split("[^.\\d-]+"); // split on anything not part of a number

虽然拨打电话的次数较少,但aioobe的答案更容易阅读和理解,这使他的代码更好。

答案 4 :(得分:1)

我认为这正是你想要的:

String pattern = "[-+]?[0-9]*\\.?[0-9]+";
String line = "M0.286-3.099-0.44c-2.901,-0.436,,,123,0.123,.34";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(line);
List<String> numbers=new ArrayList<String>();

while(m.find()) {
    numbers.add(m.group());         
}

答案 5 :(得分:0)

你很高兴为此付出了赏金。
不幸的是,正如您可能已经知道的那样,这不能使用
完成 直接使用Java的字符串拆分方法。

如果不能直接完成,那么就没有理由把它弄得一团糟,好吧......一块垃圾。

原因很多,有些相关,有些没有。

首先,你需要定义一个好的正则表达式作为基础 这是我所知道的唯一一个将验证并提取正确形式的正则表达式:

 # "((?=[+-]?\\d*\\.?\\d)[+-]?\\d*\\.?\\d*)"

 (                             # (1 start)
      (?= [+-]? \d* \.? \d )
      [+-]? \d* \.? \d* 
 )                             # (1 end)

所以,看看这个基础正则表达式,很明显你想要这个匹配的形式 在拆分的情况下,您不要想要这个匹配的表单,因为那个&#39; 你想要休息的地方。

当我看到Java的分裂时,我发现无论匹配什么,都会被排除在外 从结果数组。

因此,假设分割使用,匹配(和消费)的第一件事是所有不是的东西 这个。那部分将是这样的:

 (?:
      (?!
           (?= [+-]? \d* \.? \d )
           [+-]? \d* \.? \d* 
      )
      . 
 )+

由于唯一剩下的是有效十进制数字,下一个休息时间将在某处 有效数字之间。这部分添加到第一部分,将是这样的:

 (?:
      (?!
           (?= [+-]? \d* \.? \d )
           [+-]? \d* \.? \d* 
      )
      . 
 )+
 |         # or,
 (?<=
      (?= [+-]? \d* \.? \d )
      [+-]? \d* \.? \d* 
 )
 (?=
      (?= [+-]? \d* \.? \d )
      [+-]? \d* \.? \d* 
 )

突然之间,我们遇到了一个问题.. 一个可变长度的后视断言
所以,它的游戏结束了整个事情。

最后,不幸的是,Java(据我所知)并没有提供包含捕获的规定 组内容(在正则表达式中匹配)作为结果数组中的元素 Perl确实如此,但我无法在Java中找到这种能力。

如果Java有这样的规定,可以将break子表达式组合起来进行无缝拆分 像这样:

 (?:
      (?!
           (?= [+-]? \d* \.? \d )
           [+-]? \d* \.? \d* 
      )
      . 
 )*
 (
      (?= [+-]? \d* \.? \d )
      [+-]? \d* \.? \d* 
 )