java 使用Pattern.asPredicate()获取正则表达式匹配组

u1ehiz5o  于 2022-10-30  发布在  Java
关注(0)|答案(4)|浏览(220)

假设我有一个列表:

List<String> dest = Arrays.asList(
    "abc abd 2000",
    "idf owe 1200",
    "jks ldg 789",
    "ccc hhh 2000",
    "www uuu 1000"
);

我试着得到每个字符串末尾的数字,给定的列表中只有整数,但我也写了double的正则表达式:(\\d+\\.?\\d+)。在Java 1.8中,我编写了以下代码:

ArrayList<String> mylist = new ArrayList<>(
    dest.stream()
        .filter(Pattern.compile("\\D+\\s\\D+\\s(\\d+\\.?\\d+)").asPredicate())
        .collect(Collectors.toList())
);

我想做的是-从每个找到的字符串中得到(\\d+\\.?\\d+)组,我该怎么做呢?
我想对列表的每个元素应用一个Matcher,但是我不确定如何实现它。

kkbh8khc

kkbh8khc1#

我试着得到每一个字符串末尾的数字...

溶液1

也许你可以不使用正则表达式来解决它,就像这样:

List<String> response = dest.stream()
        .map(String::trim)
        .map(s -> s.split("\\s+"))
        .map(r -> r[r.length - 1])
        .toList();

溶液2

如果您坚持使用regex,您可以用途:

final String regex = "\\D+\\s\\D+\\s(\\d+\\.?\\d+)";
final Pattern compile = Pattern.compile(regex);
List<String> response = dest.stream()
        .map(compile::matcher)
        .filter(Matcher::find)
        .map(r -> r.group(1))
        .toList();

输出

[2000.55, 1200, 789, 2000, 1000]
kpbpu008

kpbpu0082#

filter保留或移除列表中的元素。如果你想转换流元素(当你提取数字时),使用map
然后,您可以使用正则表达式(沿着Matcher)来提取数据:

Pattern p = Pattern.compile("\\D+\\s\\D+\\s(\\d+\\.?\\d+)");
List<String> mylist = dest.stream()
        .map(s -> {
          Matcher matcher = p.matcher(s);
          matcher.find();
          return matcher.group(1); //error handling sold seperately
        })
        .collect(Collectors.toList());
System.out.println(mylist);

印刷品

[2000, 1200, 789, 2000, 1000]
mzsu5hc0

mzsu5hc03#

你应该是.map()而不是.filter()

ArrayList<String> mylist = new ArrayList<>(
    dest.stream()
        .map(s -> s.replaceAll("\\D+\\s\\D+\\s(\\d+\\.?\\d+)", "$1"))
        .collect(Collectors.toList()));
System.out.println(mylist);

输出:

[2000, 1200, 789, 2000, 1000]
ohfgkhjo

ohfgkhjo4#

首先,有几个问题值得强调:

  • 在中,您的代码从filter()操作开始,这是正确的步骤,因为map()不能从流中丢弃元素,它执行 * 一对一 * 转换。如果您需要确保元素有效,您需要首先应用filter()。另一种选择是使用flatMap()操作,它意味着执行 * 一对多 * 转换。也就是说,它将流元素转换为0+(* 零个或更多 *)个元素,并且可以像filter()map()一样工作。更重要的是,使用flatMap()可以提高性能,因为它允许避免使用regex引擎两次处理有效字符串(过滤时和Map时)。
  • 另一个需要考虑的重要问题是double有多个有效的表示:* 指数、十六进制等 *。所有选项都在Double.valueOf()的文档中列出。即使我们只讨论一个普通十进制浮点数1..9,它们不是您正在使用的正则表达式,也是有效的double注意valueOf()的Javadoc包含一个 ready-to-go 正则表达式,用于验证双精度数,这是所有可能的挖掘者(它真的很大,充满了评论,所以我不在这里发布它)。
  • 在正则表达式中,你不是在测试数字是否位于字符串的末尾,而是在描述中,你说:* “我正在尝试获取每个字符串末尾的数字”*。如果这很重要,您需要预先添加$,表示捕获的字符串应该位于最末尾。
  • 最后,用ArrayList Package Collectors.toList()返回的List是多余的。

为了简单起见,我将使用下面的正则表达式"(\\d+\\.?\\d*|\\.\\d+)$"来代替上面提到的用于检查所有形式的double的正则表达式。
第一部分\\d+\\.?\\d*将与整数(例如999)、不具有分数部分的浮点数(例如1.)和常规浮点数(例如99.999)匹配。
第二部分\\.\\d+匹配不带整数部分的浮点数,例如.995
下面介绍如何使用Stream API特性和java.util.regex.MatchResult来实现这一点。

***注意:**Matcher.results() * 是随Java 9引入的,尽管您提到了Java 8,但我想这个解决方案对其他读者可能有用。

List<String> strings = List.of(
    "abc abd 2000", "idf owe 1200", "jks ldg 789",
    "ccc hhh 2000", "www uuu 1000", "ccc hhh 2000.",
    "abc abd 2000.1", "idf owe 1200.0", "jks ldg 789.995", ".999",     // floated-point numbers
    "abc abd 2000y", "idf owe", "jks ldg 789.995%wtqop", "....ljsofo." // invalid strings
);

Pattern p = Pattern.compile("(\\d+\\.?\\d*|\\.\\d+)$");

List<String> numbers = strings.stream()
    .flatMap(str ->
        p.matcher(str).results().map(MatchResult::group)
    )
    .toList();

System.out.println(numbers);
  • 输出:*
[2000, 1200, 789, 2000, 1000, 2000., 2000.1, 1200.0, 789.995, .999]

另一种方法是使用Java 16 mapMulti(),它在高级别上的行为几乎与flatMap()一样,将一个流元素转换为零个或多个元素,但与flatMap()相反,它的内部机制不需要生成新的流。
在这种情况下,流中的每个字符串都需要转换为01字符串。如果元素的数量很大,则创建空流和单例流的开销可能会很大。因此,在这种情况下使用mapMulti()会更有好处。
这就是它可能的实现方式(这个想法归功于 @Holger):

Pattern p = Pattern.compile("(\\d+\\.?\\d*|\\.\\d+)$");

List<String> numbers = strings.stream()
    .map(p::matcher)
    .<String>mapMulti((matcher, consumer) -> {
        if (matcher.find()) consumer.accept(matcher.group());
    })
    .toList();

相关问题