使用正则表达式匹配字符串中的多个 URL

tsi*_*iki 0 java regex

我正在尝试使用此处的正则表达式匹配字符串中的 URL:Regular expression to match URLs in Java

它适用于一个 URL,但是当我在字符串中有两个 URL 时,它只匹配后者。

这是代码:

Pattern pat = Pattern.compile(".*((https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|])", Pattern.DOTALL);
Matcher matcher = pat.matcher("asdasd http://www.asd.as/asd/123 or http://qwe.qw/qwe");
// now matcher.groupCount() == 2, not 4
Run Code Online (Sandbox Code Playgroud)

编辑:我试过的东西:

// .* removed, now doesn't match anything // Another edit: actually works, see below
Pattern pat = Pattern.compile("((https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|])", Pattern.DOTALL);

// .* made lazy, still only matches one
Pattern pat = Pattern.compile(".*?((https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|])", Pattern.DOTALL);
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

Mar*_*der 5

那是因为.*贪心。它只会消耗尽可能多的(整个字符串)然后回溯。即它会一次丢弃一个字符,直到剩余的字符可以组成一个 URL。因此第一个 URL 将已经匹配,但不会被捕获。不幸的是,匹配不能重叠。修复应该很简单。删除.*模式开头的 。然后您还可以从您的模式中删除外括号 - 无需再捕获任何内容,因为整个匹配项将是您要查找的 URL。

Pattern pat = Pattern.compile("(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", Pattern.DOTALL);
Matcher matcher = pat.matcher("asdasd http://www.asd.as/asd/123 or http://qwe.qw/qwe");
while (matcher.find()) {
  System.out.println(matcher.group());
}
Run Code Online (Sandbox Code Playgroud)

顺便matcher.groupCount()说一句,它不会告诉您任何信息,因为它为您提供了模式中的组数,而不是目标字符串中的捕获数。这就是为什么您的第二种方法(使用.*?)没有帮助。您仍然有两个捕获组。在调用find或执行任何操作之前,matcher不知道它总共会找到多少个捕获。