构建 java.util.Scanner 的意外行为
Unexpected behavior constructing a java.util.Scanner
我有以下文件lines.txt
Line1
Line2
Line3
我正在使用扫描仪逐行解析此文件的内容。
我在 LinesReader.java
中有以下设置
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class Line {
Line(String content) {
this.content = content;
}
public String content;
public String toString() {
return content;
}
}
public class LinesReader {
public static Line buildLine(InputStream is) {
Scanner scanner = new Scanner(is);
if (scanner.hasNextLine()) {
return new Line(scanner.nextLine());
}
return null;
}
public static Line buildLine(Scanner scanner) {
if (scanner.hasNextLine()) {
return new Line(scanner.nextLine());
}
return null;
}
public static void main(String[] args) throws FileNotFoundException {
List<Line> lines = new ArrayList<>();
Line line = null;
FileInputStream is = new FileInputStream("lines.txt");
// buildLine(scanner) works as expected
while ((line = buildLine(is)) != null) {
lines.add(line);
}
System.err.println(lines);
}
}
输出为
[Line1]
预期输出为
[Line1, Line2, Line3]
我了解 Scanner 实现了 AutoCloseable,但是根据 documentation 这只会申请一个
try-with-resources 构造而不是这里。另外,当我调试时,它说底层流是打开的。第二次调用 scanner.hasNextLine()
意外失败。
如果我在 main() 中构造一次扫描器,它会按预期工作。
我的java版本是1.8.0_275
作为对@Sweeper 的评论的回应,扫描仪似乎缓冲了比消耗的更多的东西,文档有点矛盾。
对于hasNextLine()
The scanner does not advance past any input.
对于nextLine()
Since this method continues to search through the input looking for a line separator, it may buffer all of the input searching for the line to skip if no line separators are present.
强调我的。
试试这个。
...
FileInputStream is = new FileInputStream("lines.txt");
while (true) {
System.err.println(is.available());
Scanner scanner = new Scanner(is);
if (scanner.hasNextLine()) {
lines.add(new Line(scanner.nextLine()));
} else {
break;
}
}
我得到了以下内容。
18
0 <---- FileInputStream is not avaliable for the 2nd Scanner
[Line1]
但是如果我移动这条线
while (true) {
FileInputStream is = new FileInputStream("lines.txt");
System.err.println(is.available());
...
}
循环不断打印出 18
。
的文档
The scanner does not advance past any input.
有点误导。它不会推进扫描器的内部缓冲区,这是显而易见的,但会读取几千字节的流。
在这种情况下,整个流被 hasNextLine()
使用。
个人认为这是Scanner实现的缺陷。 Scanner 的设计目的是为了方便和简单,而不是为了性能。将 InputStream 包装在 BufferedInputStream 中是明智的,并使使用更简单。
A Scanner
被缓冲,并且不能期望底层 (File)InputStream
不会比 nextLine
返回的内容读得更远。事实上,底层的 FileInputStream 可以推进到文件末尾。所以第一个 Scanner 实例可以让 FileInputStream 位于文件末尾。
从 java8 开始,使用 Path、Files、Stream 更容易。
Path path = Paths.get("lines.txt");
try (Stream<String> in = Files.lines(path, Charset.defaultCharset())) {
List<Line> lines = in.map(Line::new)
.collect(Collectors.toList());
...
}
上面也是自动关闭文件,try-with-resources语法。
新 类 代码更小。
我有以下文件lines.txt
Line1
Line2
Line3
我正在使用扫描仪逐行解析此文件的内容。 我在 LinesReader.java
中有以下设置import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class Line {
Line(String content) {
this.content = content;
}
public String content;
public String toString() {
return content;
}
}
public class LinesReader {
public static Line buildLine(InputStream is) {
Scanner scanner = new Scanner(is);
if (scanner.hasNextLine()) {
return new Line(scanner.nextLine());
}
return null;
}
public static Line buildLine(Scanner scanner) {
if (scanner.hasNextLine()) {
return new Line(scanner.nextLine());
}
return null;
}
public static void main(String[] args) throws FileNotFoundException {
List<Line> lines = new ArrayList<>();
Line line = null;
FileInputStream is = new FileInputStream("lines.txt");
// buildLine(scanner) works as expected
while ((line = buildLine(is)) != null) {
lines.add(line);
}
System.err.println(lines);
}
}
输出为
[Line1]
预期输出为
[Line1, Line2, Line3]
我了解 Scanner 实现了 AutoCloseable,但是根据 documentation 这只会申请一个
try-with-resources 构造而不是这里。另外,当我调试时,它说底层流是打开的。第二次调用 scanner.hasNextLine()
意外失败。
如果我在 main() 中构造一次扫描器,它会按预期工作。
我的java版本是1.8.0_275
作为对@Sweeper 的评论的回应,扫描仪似乎缓冲了比消耗的更多的东西,文档有点矛盾。
对于hasNextLine()
The scanner does not advance past any input.
对于nextLine()
Since this method continues to search through the input looking for a line separator, it may buffer all of the input searching for the line to skip if no line separators are present.
强调我的。
试试这个。
...
FileInputStream is = new FileInputStream("lines.txt");
while (true) {
System.err.println(is.available());
Scanner scanner = new Scanner(is);
if (scanner.hasNextLine()) {
lines.add(new Line(scanner.nextLine()));
} else {
break;
}
}
我得到了以下内容。
18
0 <---- FileInputStream is not avaliable for the 2nd Scanner
[Line1]
但是如果我移动这条线
while (true) {
FileInputStream is = new FileInputStream("lines.txt");
System.err.println(is.available());
...
}
循环不断打印出 18
。
The scanner does not advance past any input.
有点误导。它不会推进扫描器的内部缓冲区,这是显而易见的,但会读取几千字节的流。
在这种情况下,整个流被 hasNextLine()
使用。
个人认为这是Scanner实现的缺陷。 Scanner 的设计目的是为了方便和简单,而不是为了性能。将 InputStream 包装在 BufferedInputStream 中是明智的,并使使用更简单。
A Scanner
被缓冲,并且不能期望底层 (File)InputStream
不会比 nextLine
返回的内容读得更远。事实上,底层的 FileInputStream 可以推进到文件末尾。所以第一个 Scanner 实例可以让 FileInputStream 位于文件末尾。
从 java8 开始,使用 Path、Files、Stream 更容易。
Path path = Paths.get("lines.txt");
try (Stream<String> in = Files.lines(path, Charset.defaultCharset())) {
List<Line> lines = in.map(Line::new)
.collect(Collectors.toList());
...
}
上面也是自动关闭文件,try-with-resources语法。
新 类 代码更小。