【问题标题】:RegEx in Java: how to deal with newlineJava 中的正则表达式:如何处理换行符
【发布时间】:2023-02-11 20:21:32
【问题描述】:

我目前正在尝试学习如何使用正则表达式,所以请耐心等待我的简单问题。例如,假设我有一个输入文件,其中包含一堆由换行符分隔的链接:

www.foo.com/Archives/monkeys.htm
Monkey网站的描述。

www.foo.com/Archives/pigs.txt
猪的网站的描述。

www.foo.com/Archives/kitty.txt
Kitty 网站的描述。

www.foo.com/Archives/apple.htm
Apple 网站的说明。

如果我想获得一个网站及其描述,这个正则表达式似乎适用于测试工具:.*www.*\\s.*Pig.*

但是,当我尝试在我的代码中运行它时,它似乎不起作用。这个表达是否正确?我尝试用“\n”替换“\s”,但它似乎仍然无法正常工作。

【问题讨论】:

  • 只是提醒可能更简单的解决方案:对于我自己的显式 \n 的案例,即使有 Pattern.DOTALL / (?s) 和如下所述的双重转义 (\\) 的建议,我发现这足够繁琐只是退回到非正则表达式字符串方法。 str.contains("\n") 工作正常。 str.replaceAll("\n", replacement) 也有效。不过,在 Java 11 中,我找不到返回 true 的 String.matchesPattern.compile 的变体。(与下面的解决方案不同,如果您需要捕获各种换行符,这将无济于事。)

标签: java regex line-breaks


【解决方案1】:

在您的文件中,这些行可能由 分隔。 (回车)和 (换行)在 Java 正则表达式中被视为换行符,. 元字符与它们中的任何一个都不匹配。 s 将匹配这些字符,因此它会消耗 ,但会留下 .* 来匹配 ,但失败了。您的测试人员可能只使用 来分隔行,s 消耗了这些行。

如果我是对的,将 s 更改为 s+[ ]+ 应该可以正常工作。在这种情况下,这可能就是您需要做的所有事情,但有时您必须准确匹配一个行分隔符,或者至少跟踪您匹配的行数。在这种情况下,您需要一个与三种最常见的行分隔符类型中的任何一种完全匹配的正则表达式: (Windows/DOS)、 (Unix/Linus/OSX)和 (旧版 Mac)。这些中的任何一个都可以:


|[
]


|
|

更新:从 Java 8 开始,我们有另一个选择,R。它匹配任何行分隔符,不仅包括 ,还包括Unicode standard定义的其他几个。它等同于:


|[

u0085u2028u2029]

以下是您可能如何使用它:

(?im)^.*www.*R.*Pig.*$

i 选项使其不区分大小写,m 使其处于多行模式,允许^$ 在行边界匹配。

【讨论】:

  • java 8 final 不允许使用原始“R”:/
  • @Davinder Singh 的回答有双反斜杠来补偿 java 编译器对字符串文字的解码。也许,Joe 的观察与使用单个反斜杠后跟新的正则表达式字母的尝试有关。这可能会在编译时变成非法的 Java 字符串文字。按照 Davinder 的示例,我想使用双反斜杠应该适用于 Joe。
【解决方案2】:

为了将来参考,也可以使用 Pattern.DOTALL 标志作为“.”。甚至匹配 或者 .

例子:

假设我们正在像这样解析一串 http 标头行(每行以 )

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: SAMEORIGIN
Location: http://localhost:8080/blah.htm
Content-Length: 0

这种模式:

final static Pattern PATTERN_LOCATION = Pattern.compile(".*?Location\: (.*?)\r.*?", Pattern.DOTALL);

可以使用“matcher.group(1)”解析位置值。

这 ”。”在上面的模式中将匹配 和 ,因此上述模式实际上可以从 http 标头行解析“位置”,目标行之前或之后可能还有其他标头(这并不是解析 http 标头的推荐方法)。

此外,您可以在模式中使用“?s”来达到相同的效果。

如果您这样做,您最好使用 Matcher.find()。

【讨论】:

  • DOTALL 在这种情况下并不是很有用。 OP 需要知道正则表达式何时使用行分隔符,以便他可以确定他只匹配其中一个。在您的示例中,它的用处甚至更少,因为所有感兴趣的内容都包含在一行中。我几乎从不使用 DOTALL 模式;它似乎造成的问题多于它解决的问题。
  • 你可能是对的,但它在我的示例中很有用,我要解析的单个字符串实际上包含所有行。
  • 关于 DOTALL 模式的事情是它极大地扩大了恶作剧的范围。例如,当我将你的正则表达式应用于你的示例数据时,第一个 .*? 消耗了 Location 标头上方列出的所有标头。我知道您只关心您在第 1 组中捕获的 URL,但是您仍然可以在关闭 DOTALL 模式的情况下获取它,并且您将为正则表达式节省很多不必要的工作。
  • 不,没有 DOTALL,它将无法匹配“。”和 或者 .因此无法解析该位置。如果我根据行边界拆分字符串并将位置行馈送到没有 DOTALL 的正则表达式中,它将起作用。
  • 不,我的意思是你不必匹配任何行分隔符。 "Location: (.*)" 可能会正常工作,但为了安全起见,我会使用锚点:"(?m)^Location: (.*)$"
【解决方案3】:
String str="I am  a   "+"
  Man    of  Peace"+"	"+"   .";

str=str.replaceAll("[\s|\t|\r\n]+"," ").trim();
System.out.println(str);

上面的示例适用于 tabSpaces、newLines 和普通空格。 并且我使用了java.lang.String 的 trim 方法来删​​除'str'中的所有额外空格。我希望这对您和这里其他了不起的人有所帮助。

【讨论】:

  • 最佳解决方案。您可以将 replaceAll 的第二个参数保留为“s”而不是“”
【解决方案4】:

尝试这个

([^
]+
[^
])+

【讨论】:

    【解决方案5】:

    为我工作:

    import java.util.regex.Pattern;
    import java.util.regex.Matcher;
    public class Foo {
      public static void main(String args[]) {
        Pattern p = Pattern.compile(".*www.*\s.*Pig.*");
        String s = "www.foo.com/Archives/monkeys.htm
    "
                 + "Description of Monkey's website.
    "
                 + "
    "
                 + "www.foo.com/Archives/pigs.txt
    "
                 + "Description of Pig's website.
    "
                 + "
    "
                 + "www.foo.com/Archives/kitty.txt
    "
                 + "Description of Kitty's website.
    "
                 + "
    "
                 + "www.foo.com/Archives/apple.htm
    "
                 + "Description of Apple's website.
    ";
        Matcher m = p.matcher(s);
        if (m.find()) {
          System.out.println(m.group());
        } else {
          System.out.println("ERR: no match");
        }
      }
    }
    

    也许问题出在您使用 Pattern 和 Matcher 对象的方式上?

    【讨论】:

    • 这仅在行总是格式化为 , 就像在 unix 中一样
    【解决方案6】:

    此版本匹配的换行符可能是 Windows ( ) 或 Unix ( )

    Pattern p = Pattern.compile("(www.*)((
    )|(
    ))(.*Pig.*)");
    String s = "www.foo.com/Archives/monkeys.htm
    "
               + "Description of Monkey's website.
    "
               + "
    "
               + "www.foo.com/Archives/pigs.txt
    "
               + "Description of Pig's website.
    "
               + "
    "
               + "www.foo.com/Archives/kitty.txt
    "
               + "Description of Kitty's website.
    "
               + "
    "
               + "www.foo.com/Archives/apple.htm
    "
               + "Description of Apple's website.
    ";
    Matcher m = p.matcher(s);
    if (m.find()) {
      System.out.println("found: "+m.group());
      System.out.println("website: "+m.group(1));
      System.out.println("description: "+m.group(5));
    }
    System.out.println("done");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-29
      • 2019-04-29
      • 2017-09-28
      相关资源
      最近更新 更多