Java Scanner.nextLine()方法会将换行符消耗掉。

5

我设置了一个扫描仪,它正在使用InputStream工作。

我使用Scanner.nextLine()方法来获取每一行,然后对每一行进行一些正则表达式处理。

我有一个正则表达式,基本上像是[\w\p{Z}]+?[;\n\r],可以捕获到该行末尾的任何内容,或者只捕获一个东西,如果它们以分号分隔。

所以,如果我的InputStream看起来像

abcd;
xyz

它将选择abcd;,但不会选择xyz。

我认为这是因为扫描器在调用.nextLine()函数时消耗了文本行末尾的换行符。有人能告诉我如何解决这个问题吗?

另外,对于我的正则表达式,我正在使用Pattern.DOTALL编译该模式。

谢谢!


1
你必须只使用正则表达式来完成这个任务吗?似乎 String.split 也可以实现? - Marnix
非常容易找出 - 停止在每行上使用正则表达式,检查 nextLine() 获取的内容即可... - hovanessyan
5个回答

7

实际上,问题出在您试图消耗最后一行结尾的换行符。 :-/ 最后一行突然结束且没有换行符是完全有效的,但是您的正则表达式要求有一个换行符。您可以用锚点或前瞻来替换换行符,但是还有更简单的方法。

其中一种方法是覆盖默认分隔符,并使用next()迭代字段:

Scanner sc1 = new Scanner("abcd;\nxyz");
sc1.useDelimiter("[;\r\n]+");
while (sc1.hasNext())
{
  System.out.printf("%s%n", sc1.next());
}

另一种方法是使用nextLine()按行遍历(使用默认分隔符),然后在每行上按分号拆分:
Scanner sc2 = new Scanner("abcd;\nxyz");
while (sc2.hasNextLine())
for (String item : sc2.nextLine().split(";"))
{
  System.out.printf("%s%n", item);
}

Scanner的API是我曾经使用过的最臃肿和不直观的API之一,但是如果您记住以下两个关键点,就可以大大减少使用它的痛苦:

  1. 按照匹配分隔符的方式思考,而不是按照字段(就像您使用字符串的split()方法一样)。
  2. 在调用一个nextXXX()方法之前,永远不要没有先调用相应的hasNextXXX()方法。

2

那么,为什么不在nextLine()结果中添加一个换行符呢?

难道没有一个正则表达式特殊字符^$代表字符串的边界吗?


虽然这个回答有点模糊,但我同意它。你可以在字符串末尾添加\r字符。或者对整个字符串进行正则表达式匹配(不要使用扫描器)。这将返回所有行结束符。 - Marnix
字符串太长了,一次性在整个字符串上进行正则表达式匹配不可行。 - Derek

1
正则表达式中的字符 $ 表示“模式结尾”。话虽如此,由于你没有行尾字符,所以很容易消耗掉第一个分号之前的所有内容;只需消耗除分号以外的所有内容即可。
[^;]+

Scanner 会将换行符作为其行为的一部分消耗掉,因为通常情况下您不想处理它,并且它是系统相关的。

编辑:在评论中,有人指出您可以使用 line.split(";") 并获取第一个值。这也可以工作。


1

0

在正则表达式模式中,您可以使用\z表示输入的结尾,或者使用$表示行的结尾。此外,默认情况下,Scanner.nextLine()返回不带换行符的行。另外,您可以使用useDelimiter方法更改Scanner使用的分隔符,包括;。最后,您的模式可能不会像您想象的那样工作,因为\p{Z}只能捕获字母'Z',请参考 Pattern文档


你误解了文档。请查看此链接:http://www.regular-expressions.info/posixbrackets.html - Derek
不,\p{Z}是一个真正的Unicode类别,只是完全没有用。它包括\p{Zl}(行分隔符,U+2028)、\p{Zp}(段落分隔符,U+2029)和\p{Zs}(空格分隔符,列表),但不包括\n\r。实际上人们用来分隔行和/或段落的字符没有Unicode类别。 - Alan Moore
嗯,是的,但你在Java中进行正则表达式匹配,对吧?使用Pattern时,应该使用Java正则表达式语法,不是吗? - fredo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接