【问题标题】:How can I get the count of line in a file in an efficient way? [duplicate]如何以有效的方式获取文件中的行数? [复制]
【发布时间】:2020-11-09 01:31:55
【问题描述】:

我有一个大文件。它包括大约 3.000-20.000 行。如何使用 Java 获取文件中的总行数?

【问题讨论】:

  • 从您的 cmets 到答案来看,您要找的词是“高效”,而不是“有效”。
  • @Firstthumb: 人回复后,请不要删除 cmets。对于迟到的人来说,这会使线程感到困惑。
  • 为什么? 20,000 行并不大。百万很大。为什么你认为你需要知道行数?如果这样做,您可以在处理它们时计算它们。您必须阅读整个文件才能计算行数。您不妨同时做一些有用的事情。

标签: java file


【解决方案1】:
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
int lines = 0;
while (reader.readLine() != null) lines++;
reader.close();

更新:为了回答这里提出的性能问题,我进行了测量。第一件事:20.000 行太少,无法让程序运行很长时间。我创建了一个包含 500 万行的文本文件。这个解决方案(从 java 开始,没有 -server 或 -XX-options 等参数)在我的盒子上需要大约 11 秒。与 wc -l 相同(用于计算行数的 UNIX 命令行工具),11 秒。读取每个字符并查找“\n”的解决方案需要 104 秒,是 9-10 倍。

【讨论】:

  • 你的意思是什么效率?表现?在这种情况下,您将没有更好的方法,因为行可以有不同的长度,您必须阅读完整的文件来计算行号(wc 也这样做)。如果您谈论编程效率,我相信您可以将其放入实用程序方法中(或者某些通用库已经这样做了)。
  • @Firstthumb。也许效率不高,但谁在乎。他只计算了 20k 行,这非常小。我认为这段代码是最简单的。
  • LineNumberReader扩展了BufferedReader,效率如何?
  • 下一个问题?你为什么不这样做:D
  • 检查每个字节肯定会更快(使用缓冲区时),因为 FileReader 必须将字节解码为文本。
【解决方案2】:

Files.lines

Java 8+ 有一个使用NIOFiles.lines 的好方法。请注意 you have to close the stream 使用 try-with-resources:

long lineCount;
try (Stream<String> stream = Files.lines(path, StandardCharsets.UTF_8)) {
  lineCount = stream.count();
}

如果不指定字符编码,则默认使用UTF-8。您可以specify an alternate encoding 来匹配您的特定数据文件,如上例所示。

【讨论】:

  • 床解决方案。我们可能会遇到字符集问题
  • 字符集默认为 UTF-8
  • @Mikhail 在可选的第二个参数中将特定数据文件的字符编码作为Charset 对象传递。见:Files.lines(Path path, Charset cs)。默认为 UTF-8;对于其他编码,请传递CharSet
  • Files.lines(path).count();不应该直接使用。而是使用资源尝试。示例::长行数;尝试 (Stream linesStream =Files.lines(path)){ lineCount =linesStream.count(); }
  • 请注意,path 并未关闭。 :-/
【解决方案3】:

使用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();
    }
}

【讨论】:

  • 你可能还需要 close() 阅读器。
  • 你可能会在 finally 块中检查 reader!=null
【解决方案4】:

我找到了一些解决方案,它可能对你有用

下面是代码 sn-p 用于计算文件中的行数。

  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 ?
【解决方案5】:

通读文件并计算换行符的数量。在 Java 中一次一行地读取文件的一种简单方法是 java.util.Scanner 类。

【讨论】:

    【解决方案6】:

    这是尽可能高效,缓冲二进制读取,无字符串转换,

    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] == '\n') count++;
        }
    }
    stream.close();
    System.out.println("Number of lines: " + count);
    

    【讨论】:

      【解决方案7】:

      您需要精确的行数还是只需要近似值?我碰巧并行处理大文件,通常我不需要知道确切的行数 - 然后我恢复到采样。将文件拆分为 10 个 1MB 的块并计算每个块中的行数,然后将其乘以 10,您将获得相当不错的行数近似值。

      【讨论】:

        【解决方案8】:

        所有以前的答案都建议通读整个文件并计算您在执行此操作时找到的换行符的数量。您评论了一些“无效”,但这是您可以做到这一点的唯一方法。 “行”只是文件中的一个简单字符。要计算该字符,您必须查看文件中的每个字符。

        对不起,你别无选择。 :-)

        【讨论】:

          【解决方案9】:

          在包含 1380 万行的文件上进行测试时,此解决方案的速度比评分最高的答案快约 3.6 倍。它只是将字节读入缓冲区并计算\n 字符。您可以使用缓冲区大小,但在我的机器上,任何超过 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] == '\n') lines++;
                  }
              }
          
              fis.close();
          
              return lines;
          }
          

          【讨论】:

          • 我想知道使用预编译的 RegEx 模式是否会使其更快或更慢。我相信它会做的是与所有行尾一起工作。而且,我认为它也可能使它更快。
          • 如果好处有帮助,上述一些解决方案也可以利用缓冲。比如“new LineNumberReader(new FileReader(theFilePathStr), 8096)”什么的。
          • 注意字符编码...
          【解决方案10】:

          如果已经发布的答案不够快,您可能需要寻找针对您的特定问题的解决方案。

          例如,如果这些文本文件是仅附加到的日志,并且您经常需要知道其中的行数,则可以创建索引。该索引将包含文件中的行数、文件最后一次修改的时间以及当时文件的大小。这将允许您通过跳过您已经看到的所有行并仅读取新行来重新计算文件中的行数。

          【讨论】:

          • +1 这可能是一个合适的在线算法。
          【解决方案11】:

          旧帖子,但我有一个可能对下一个人有用的解决方案。 为什么不只使用文件长度来了解进度?当然,行的大小必须几乎相同,但它非常适合大文件:

          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))) + "%");
          

          如果您认为这是一个好的解决方案,请随时改进此代码。

          【讨论】:

            【解决方案12】:

            又快又脏,但它确实有效:

            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;
                    }
                }
            
            }
            

            【讨论】:

            • 一个副作用,这个解决方案不是跨平台的。
            【解决方案13】:

            逐行读取文件并为每一行增加一个计数器,直到您读取整个文件。

            【讨论】:

              【解决方案14】:

              试试 unix "wc" 命令。我不是说使用它,我的意思是下载源代码,看看他们是如何做到的。它可能在 c 中,但您可以轻松地将行为移植到 java。自己制作的问题是要解决结尾的 cr/lf 问题。

              【讨论】:

                【解决方案15】:

                缓冲的阅读器太过分了

                Reader r = new FileReader("f.txt");
                
                int count = 0;
                int nextchar = 0;
                while (nextchar != -1){
                        nextchar = r.read();
                        if (nextchar == Character.getNumericValue('\n') ){
                            count++;
                        }
                    }
                

                我对一个简单示例的搜索创建了一个实际上很差的示例。对单个字符重复调用 read() 不是最优的。有关示例和测量,请参阅 here

                【讨论】:

                • BufferedReader 可以很好地处理不同的行尾。您的解决方案忽略 Mac 行尾 ('\r')。那可能没问题。无论如何,您的解决方案目前并未实际从文件中读取。我想你忘了一行。
                • 这里的 nextchar 会发生什么变化?如果您要在每次迭代中调用 read(),我强烈怀疑 BufferedReader 方法会快得多...
                • 就是这个想法;-/我想写一个最简单的例子。我想知道速度差异是多少?
                • BufferedReader 在这里并不过分。此答案中的代码将非常缓慢 - FileReader.read() 将一次从文件中提取一个字符。
                • 答案是这里给出的“戏剧性”例子java.sun.com/developer/technicalArticles/Programming/PerfTuning
                猜你喜欢
                • 2019-06-24
                • 1970-01-01
                • 2012-04-29
                • 2022-10-23
                • 1970-01-01
                • 1970-01-01
                • 2022-10-04
                相关资源
                最近更新 更多