我正在使用Java 8 Streams从csv文件创建流。
我正在使用BufferedReader.lines()
,我阅读了BufferedReader.lines()
的文档:
执行终端流操作后,无法保证读者将处于读取下一个字符或行的特定位置。
public class Streamy {
public static void main(String args[]) {
Reader reader = null;
BufferedReader breader = null;
try {
reader = new FileReader("refined.csv");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
breader = new BufferedReader(reader);
long l1 = breader.lines().count();
System.out.println("Line Count " + l1); // this works correctly
long l2 = breader.lines().count();
System.out.println("Line Count " + l2); // this gives 0
}
}
看起来第一次读取文件后,读者无法访问文件的开头。解决这个问题的方法是什么
答案 0 :(得分:7)
看起来第一次读取文件后,读者无法进入文件的开头。
不 - 而且我不知道为什么你会期望它给你引用的文件。基本上,lines()
方法在开始之前不会“回放”阅读器,甚至可能无法进行。 (想象一下,BufferedReader
包裹了一个包裹网络连接InputStreamReader
的{{1}} - 一旦您读取了数据,它就消失了。)
解决此问题的方法是什么
两个选项:
将InputStream
的结果保存到lines()
,这样您就不会第二次读取该文件了。例如:
List<String>
顺便说一句,我强烈建议使用List<String> lines = breader.lines().collect(Collectors.toList());
代替Files.newBufferedReader
- 后者始终使用平台默认编码,这通常不是一个好主意
就此而言,要将所有行加载到列表中,如果您希望将行作为流而不是列表,则可以使用Files.readAllLines
...或Files.lines
。 (但请注意评论中的警告。)
答案 1 :(得分:1)
可能需要澄清来自JavaDoc的引用片段。通常你会期望在阅读完整个文件后,读者会指向文件的末尾。但是使用流取决于是否使用短路终端操作以及流是否是并行的。例如,如果您使用
String magicLine = breader.lines()
.filter(str -> str.startsWith("magic"))
.findAny()
.orElse(null);
您的读者可能会在第一个找到的行之后停止(因为无需进一步阅读)或读取整个输入文件(如果找不到这样的行)。如果在并行流中进行相同的操作,则结果位置将是不可预测的,因为输入将被拆分为一些依赖于实现的块,其中将执行搜索。这就是它在文档中以这种方式编写的原因。
至于解决方法,请阅读@JonSkeet答案。并考虑通过try-with-resource构造来关闭你的流。
答案 2 :(得分:0)
如果无法保证读者会在某一特定行,那为什么不创建两个读者?
reader1=new FileReader("refined.csv");
reader2=new FileReader("refined.csv");