本问题已经有最佳答案,请猛点这里访问。
我有一个大文件。 它包括大约3.000-20.000行。 如何使用Java获取文件中的行总数?
从你的评论到答案来判断,你要找的词是"有效的",而不是"有效的"。
是的,你是对的
@Firstthumb:请不要在人们回复评论后删除评论。 对于那些迟到演出的人来说,这让线程变得混乱。
为什么? 20,000线并不大。 数百万人很大。 为什么你认为你需要知道线的数量? 如果这样做,您可以在处理它们时对它们进行计数。 您必须读取整个文件才能计算行数。 你也可以同时做一些有用的事情。
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
int lines = 0;
while (reader.readLine() != null) lines++;
reader.close();
更新:为了回答这里提出的性能问题,我做了一个测量。第一件事:20.000行太少,以使程序运行一段时间。我创建了一个包含500万行的文本文件。这个解决方案(从没有像-server或-XX-options这样的参数的java开始)在我的盒子上需要大约11秒。与wc -l(UNIX命令行工具计数行)相同,为11秒。读取每个字符并寻找' n'的解决方案需要104秒,9-10倍。
你的意思是什么?性能?在这种情况下,你将没有更好的方法,因为行可以有不同的长度,你必须读取完整的文件,计算行数(wc也这样做)。如果你谈论编程效率,我肯定你可以把它放在一个实用程序方法(或者一些常见的库已经完成它)。
@Firstthumb。可能效率不高,但谁在乎呢。他只计算20k线,非常小。这段代码得到了我最简单的投票。
LineNumberReader的效率如何,因为它扩展了BufferedReader?
没有人说这比LineNumberReader好,至少我不这样做。
下一个问题?你为什么不这样做:D
我有点确定,BufferedReader的工作速度至少和FileReader一样快,并检查每一个字符。我通过测量时间证明了这一点(并且实际上表明检查每个字符的速度要慢得多)。但我认为LineNumberReader解决方案的效果与BufferedReader的解决方案一样好。这就是我赞成这个答案的原因。
检查每个字节应该肯定更快(使用缓冲区时),因为FileReader必须将字节解码为文本。
对于现代Java,Augustin的答案应该是可接受的答案。使用Files.lines。
Files.lines
使用Files.lines使用NIO时,Java 8+有一个非常好的简短方法。
Path path = Paths.get("./big_file.txt");
long lineCount = Files.lines(path).count();
UTF-8中的默认字符编码。您可以指定备用编码以匹配您的特定数据文件。
床解决方案。我们可以遇到charset的问题
charset默认为UTF-8
@Mikhail将特定数据文件的字符编码作为可选第二个参数中的Charset对象传递。见:Files.lines(Path path, Charset cs)。默认值为UTF-8;其他编码通过Charset。
Files.lines(路径).Count之间();不应该直接使用。相反,请尝试使用资源。示例:: long lineCount; try(Stream linesStream = Files.lines(path)){lineCount = linesStream.count(); }
使用LineNumberReader
就像是
public static int countLines(File aFile) throws IOException {
LineNumberReader reader = null;
try {
reader = new LineNumberReader(new FileReader(aFile));
while ((reader.readLine()) != null);
return reader.getLineNumber();
} catch (Exception ex) {
return -1;
} finally {
if(reader != null)
reader.close();
}
}
您可能还需要关闭()读者。
是的;谢谢:D
你可能要在finally块中检查读者!= null
@dfa谢谢,修复
我找到了一些解决方案,它可能对你有用
下面是代码片段,计算文件中的no.of行。
File file = new File("/mnt/sdcard/abc.txt");
LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(file));
lineNumberReader.skip(Long.MAX_VALUE);
int lines = lineNumberReader.getLineNumber();
lineNumberReader.close();
结果是行count - 1
实际上结果是lines + 1
结果是getLineNumber()加1,因为行索引从0开始
它也适用于其他文件吗?像.csv,.xls,.xlsx?
这大约和它可以获得的效率一样,缓冲二进制读取,没有字符串转换,
FileInputStream stream = new FileInputStream("/tmp/test.txt");
byte[] buffer = new byte[8192];
int count = 0;
int n;
while ((n = stream.read(buffer)) > 0) {
for (int i = 0; i < n; i++) {
if (buffer[i] == '
') count++;
}
}
stream.close();
System.out.println("Number of lines:" + count);
读取文件并计算换行符的数量。使用Java读取文件的简单方法是java.util.Scanner类,一次一行。
您需要精确的行数还是仅需要近似值?我碰巧并行处理大文件,通常我不需要知道确切的行数 - 然后我恢复采样。将文件拆分为10个1MB块并计算每个块中的行数,然后将其乘以10,您将获得非常好的行计数近似值。
在具有1380万行的文件上测试时,此解决方案比最高评级答案快3.6倍。它只是将字节读入缓冲区并计算字符。您可以使用缓冲区大小,但在我的机器上,任何超过8KB的内容都不会使代码更快。
private int countLines(File file) throws IOException {
int lines = 0;
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[BUFFER_SIZE]; // BUFFER_SIZE = 8 * 1024
int read;
while ((read = fis.read(buffer)) != -1) {
for (int i = 0; i < read; i++) {
if (buffer[i] == '
') lines++;
}
}
fis.close();
return lines;
}
我想知道使用预编译的RegEx模式是否会使其更快或更慢。我相信它能做的就是与所有线路结束一起工作。并且,我认为它也可能使它更快。
如果好处有帮助,上述一些解决方案也可以利用缓冲。例如,"new LineNumberReader(new FileReader(theFilePathStr),8096)"等等。
小心字符编码......
快速而肮脏,但它完成了工作:
import java.io.*;
public class Counter {
public final static void main(String[] args) throws IOException {
if (args.length > 0) {
File file = new File(args[0]);
System.out.println(countLines(file));
}
}
public final static int countLines(File file) throws IOException {
ProcessBuilder builder = new ProcessBuilder("wc","-l", file.getAbsolutePath());
Process process = builder.start();
InputStream in = process.getInputStream();
LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
String line = reader.readLine();
if (line != null) {
return Integer.parseInt(line.trim().split("")[0]);
} else {
return -1;
}
}
}
一方面效果,这个解决方案不是跨平台的。
之前的所有答案都建议读取整个文件并计算您在执行此操作时找到的换行数量。你评价一些"无效",但这是你能做到的唯一方法。"行"不是文件中的简单字符。要计算该字符,您必须查看文件中的每个字符。
对不起,你别无选择。 :-)
如果已经发布的答案不够快,您可能需要寻找特定于您的特定问题的解决方案。
例如,如果这些文本文件是仅附加到的日志,并且您经常需要知道其中的行数,则可以创建索引。此索引将包含文件中的行数,上次修改文件的时间以及文件的大小。这将允许您通过跳过已经看过的所有行并只读取新行来重新计算文件中的行数。
+1这可能是一个合适的在线算法。
尝试使用unix"wc"命令。我不是故意使用它,我的意思是下载源代码并看看它们是如何做到的。它可能在c中,但您可以轻松地将行为移植到java。制作自己的问题是考虑结束cr / lf问题。
旧帖子,但我有一个可以为下一个人提供帮助的解决方案。
为什么不直接使用文件长度来了解进展情况?当然,行必须几乎相同的大小,但它适用于大文件:
public static void main(String[] args) throws IOException {
File file = new File("yourfilehere");
double fileSize = file.length();
System.out.println("=======> File size =" + fileSize);
InputStream inputStream = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"iso-8859-1");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
int totalRead = 0;
try {
while (bufferedReader.ready()) {
String line = bufferedReader.readLine();
// LINE PROCESSING HERE
totalRead += line.length() + 1; // we add +1 byte for the newline char.
System.out.println("Progress ===>" + ((totalRead / fileSize) * 100) +" %");
}
} finally {
bufferedReader.close();
}
}
它允许在不对文件进行任何完整读取的情况下查看进度。我知道这取决于很多元素,但我希望它会有用:)。
[版]
这是一个估计时间的版本。我把一些SYSO用于显示进度和估算。我看到你处理足够的线后你有一个很好的时间估计误差(我尝试使用10M线,并且在1%的治疗后,时间估计精确到95%)。
我知道,有些值必须在变量中设置。这段代码写得很快,但对我来说很有用。希望它也适合你:)。
long startProcessLine = System.currentTimeMillis();
int totalRead = 0;
long progressTime = 0;
double percent = 0;
int i = 0;
int j = 0;
int fullEstimation = 0;
try {
while (bufferedReader.ready()) {
String line = bufferedReader.readLine();
totalRead += line.length() + 1;
progressTime = System.currentTimeMillis() - startProcessLine;
percent = (double) totalRead / fileSize * 100;
if ((percent > 1) && i % 10000 == 0) {
int estimation = (int) ((progressTime / percent) * (100 - percent));
fullEstimation += progressTime + estimation;
j++;
System.out.print("Progress ===>" + percent +" %");
System.out.print(" - current progress :" + (progressTime) +" milliseconds");
System.out.print(" - Will be finished in ===>" + estimation +" milliseconds");
System.out.println(" - estimated full time =>" + (progressTime + estimation));
}
i++;
}
} finally {
bufferedReader.close();
}
System.out.println("Ended in" + (progressTime) +" seconds");
System.out.println("Estimative average ===>" + (fullEstimation / j));
System.out.println("Difference:" + ((((double) 100 / (double) progressTime)) * (progressTime - (fullEstimation / j))) +"%");
如果您认为这是一个很好的解决方案,请随意改进此代码。
逐行读取文件并为每行增加一个计数器,直到您读完整个文件。
在我的测试中,其他答案在118.5k行文件上需要~150-300ms。
以下需要1ms,但只是近似值(报告117k行),并且取决于每条线具有相似的大小。
private static void countSize(File file) {
long fileLength = file.length();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
//Skip header as it is of different size
reader.readLine();
String text = reader.readLine();
int lineLength = text.length();
long lines = fileLength / lineLength;
System.out.println(lines);
} catch(IOException e) {
e.printStackTrace();
} finally {
if(reader != null) {
try {
reader.close();
} catch(IOException e) {
//no-op
}
}
}
}
纯Java中最快的解决方案可能是使用NIO Channel将文件作为字节读入大型ByteBuffer。然后根据相关的行分隔符约定,使用您对文件编码方案的了解,对编码的CR和/或NL字节进行计数。
最大化吞吐量的关键是:
确保以大块读取文件,
避免将字节从一个缓冲区复制到另一个缓冲区
避免将字节复制/转换为字符,以及
避免分配对象来表示文件行。
实际的代码太复杂了,我无法动态编写。此外,OP并不是要求最快的解决方案。
缓冲的阅读器过度 strike>
Reader r = new FileReader("f.txt");
int count = 0;
int nextchar = 0;
while (nextchar != -1){
nextchar = r.read();
if (nextchar == Character.getNumericValue('
') ){
count++;
}
}
我对一个简单例子的搜索创建了一个实际上很差的例子。对单个字符重复调用read()不是最佳的。请参阅此处的示例和测量。
BufferedReader可以很好地处理不同的行尾。您的解决方案忽略了Mac-line-endings(' r')。那可能没问题。无论如何,您的解决方案暂时无法从文件中实际读取。我想你忘了一条线。
什么会改变nextchar在这里?如果你要在每次迭代时调用read(),我强烈怀疑BufferedReader方法会快得多......
这个想法; - /我想写一个最简单的例子。我想知道速度差异会是什么?
BufferedReader在这里并不过分。这个答案中的代码将非常慢 - FileReader.read()将从文件中一次拉出一个字符。
答案是这里给出的'戏剧性'例子java.sun.com/developer/technicalArticles/Programming/PerfTuning
我在我的盒子上测量它,Jon Skeet是对的,差异很大。我在答案中添加了测量值。