【问题标题】:RegEx in Java: how to deal with newlineJava中的RegEx:如何处理换行符
【发布时间】:2010-08-10 01:09:38
【问题描述】:

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

www.foo.com/Archives/monkeys.htm
Monkey 网站说明。

www.foo.com/Archives/pigs.txt
Pig 网站的说明。

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

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

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

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

【问题讨论】:

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

标签: java regex line-breaks


【解决方案1】:

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

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

\r\n|[\r\n]

\r\n|\n|\r

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

\r\n|[\n\x0B\x0C\r\u0085\u2028\u2029]

以下是您可以使用的方法:

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

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

【讨论】:

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

为了将来参考,也可以使用 Pattern.DOTALL 标志来表示“。”甚至匹配 \r 或 \n。

例子:

假设我们正在解析这样的单个 http 标头行字符串(每行以 \r\n 结尾)

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)”解析位置值。

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

另外,你可以在模式中使用“?s”来达到同样的效果。

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

【讨论】:

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

试试这个

([^\r]+\r[^\r])+

【讨论】:

    【解决方案4】:
    String str="I am  a   "+"\n  Man    of  Peace"+"\t"+"   .";
    
    str=str.replaceAll("[\\s|\\t|\\r\\n]+"," ").trim();
    System.out.println(str);
    

    以上示例适用于 tabSpaces、newLines 和普通空格。 我已经使用了java.lang.String 的修剪方法来删除'str'中的所有额外空格。我希望这对你和这里的其他优秀的人有所帮助。

    【讨论】:

    • 最佳解决方案。您可以将 replaceAll 的第二个参数保留为 "\s" 而不是 " "
    【解决方案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\n"
                 + "Description of Monkey's website.\n"
                 + "\n"
                 + "www.foo.com/Archives/pigs.txt\n"
                 + "Description of Pig's website.\n"
                 + "\n"
                 + "www.foo.com/Archives/kitty.txt\n"
                 + "Description of Kitty's website.\n"
                 + "\n"
                 + "www.foo.com/Archives/apple.htm\n"
                 + "Description of Apple's website.\n";
        Matcher m = p.matcher(s);
        if (m.find()) {
          System.out.println(m.group());
        } else {
          System.out.println("ERR: no match");
        }
      }
    }
    

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

    【讨论】:

    • 这只有在行总是用 \n 格式化时才有效,就像在 unix 中一样
    【解决方案6】:

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

    Pattern p = Pattern.compile("(www.*)((\r\n)|(\n))(.*Pig.*)");
    String s = "www.foo.com/Archives/monkeys.htm\n"
               + "Description of Monkey's website.\n"
               + "\r\n"
               + "www.foo.com/Archives/pigs.txt\r\n"
               + "Description of Pig's website.\n"
               + "\n"
               + "www.foo.com/Archives/kitty.txt\n"
               + "Description of Kitty's website.\n"
               + "\n"
               + "www.foo.com/Archives/apple.htm\n"
               + "Description of Apple's website.\n";
    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");
    

    【讨论】:

      猜你喜欢
      • 2021-08-28
      • 1970-01-01
      • 2011-03-14
      • 2023-02-11
      • 2010-09-07
      • 2018-08-11
      • 1970-01-01
      相关资源
      最近更新 更多