1. 概述

在本教程中,我们将学习 如何使用 Java 查找文件中的行数,涵盖使用标准 Java IO API、Google Guava 以及 Apache Commons IO 等多种方式。

2. 使用 NIO2 的 Files 类

在本教程中,我们统一使用以下常量表示文件路径和预期行数:

static final String INPUT_FILE_NAME = "src/main/resources/input.txt";
static final int NO_OF_LINES = 45;

Java 7 引入了 NIO2(New IO 2),对原有 IO 类进行了增强。我们可以通过 Files.lines() 方法读取文件并统计行数:

@Test
public void whenUsingNIOFiles_thenReturnTotalNumberOfLines() throws IOException {
    try (Stream<String> fileStream = Files.lines(Paths.get(INPUT_FILE_NAME))) {
        int noOfLines = (int) fileStream.count();
        assertEquals(NO_OF_LINES, noOfLines);
    }
}

或者使用 Files.readAllLines() 方法,更直观:

@Test
public void whenUsingNIOFilesReadAllLines_thenReturnTotalNumberOfLines() throws IOException {
    List<String> fileStream = Files.readAllLines(Paths.get(INPUT_FILE_NAME));
    int noOfLines = fileStream.size();
    assertEquals(NO_OF_LINES, noOfLines);
}

优点:代码简洁,适合现代 Java 开发。
缺点:大文件下可能占用较多内存。

3. 使用 NIO 的 FileChannel

FileChannel 是 JDK 4 引入的 NIO 类,适用于高性能场景。我们可以通过读取字节并统计换行符来实现行数统计:

@Test
public void whenUsingNIOFileChannel_thenReturnTotalNumberOfLines() throws IOException {
    int noOfLines = 1;
    try (FileChannel channel = FileChannel.open(Paths.get(INPUT_FILE_NAME), StandardOpenOption.READ)) {
        ByteBuffer byteBuffer = channel.map(MapMode.READ_ONLY, 0, channel.size());
        while (byteBuffer.hasRemaining()) {
            byte currentByte = byteBuffer.get();
            if (currentByte == '\n')
                noOfLines++;
        }
    }
    assertEquals(NO_OF_LINES, noOfLines);
}

⚠️ 注意:该方法适用于 JDK 7 及以上版本,虽然 FileChannel 早在 JDK 4 就已存在。

优点:性能高,适用于大文件处理。
缺点:代码复杂,容易出错。

4. 使用 Google Guava 的 Files 类

Google Guava 是一个非常流行的 Java 工具类库,其 Files 类也提供了便捷的文件处理方法。

首先在 pom.xml 中添加依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

然后使用 Files.readLines() 方法获取行数:

@Test
public void whenUsingGoogleGuava_thenReturnTotalNumberOfLines() throws IOException {
    List<String> lineItems = Files.readLines(Paths.get(INPUT_FILE_NAME)
      .toFile(), Charset.defaultCharset());
    int noOfLines = lineItems.size();
    assertEquals(NO_OF_LINES, noOfLines);
}

优点:代码简洁,API 设计良好。
缺点:依赖第三方库。

5. 使用 Apache Commons IO 的 FileUtils 类

Apache Commons IO 提供了 FileUtils.lineIterator() 方法,可以一行一行读取文件内容。

添加依赖:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.15.1</version>
</dependency>

使用方式如下:

@Test
public void whenUsingApacheCommonsIO_thenReturnTotalNumberOfLines() throws IOException {
    int noOfLines = 0;
    LineIterator lineIterator = FileUtils.lineIterator(new File(INPUT_FILE_NAME));
    while (lineIterator.hasNext()) {
        lineIterator.nextLine();
        noOfLines++;
    }
    assertEquals(NO_OF_LINES, noOfLines);
}

优点:兼容性好,适用于旧项目。
缺点:代码比 Guava 略显啰嗦。

6. 使用 BufferedReader

如果你还在使用 JDK 7 之前的版本,或者不能引入第三方库,可以使用 BufferedReader

@Test
public void whenUsingBufferedReader_thenReturnTotalNumberOfLines() throws IOException {
    int noOfLines = 0;
    try (BufferedReader reader = new BufferedReader(new FileReader(INPUT_FILE_NAME))) {
        while (reader.readLine() != null) {
            noOfLines++;
        }
    }
    assertEquals(NO_OF_LINES, noOfLines);
}

优点:无需引入第三方库。
缺点:代码相对繁琐。

7. 使用 LineNumberReader

LineNumberReaderBufferedReader 的子类,自带行号功能,可以简化行数统计逻辑:

@Test
public void whenUsingLineNumberReader_thenReturnTotalNumberOfLines() throws IOException {
    try (LineNumberReader reader = new LineNumberReader(new FileReader(INPUT_FILE_NAME))) {
        reader.skip(Integer.MAX_VALUE);
        int noOfLines = reader.getLineNumber();
        assertEquals(NO_OF_LINES, noOfLines);
    }
}

⚠️ 注意:行号从 0 开始,所以 getLineNumber() 返回的是实际行数减一。

优点:代码简洁。
缺点:依赖 BufferedReader 的底层实现。

8. 使用 Scanner

如果你已经在使用 Scanner 来处理文件内容,也可以用它来统计行数:

@Test
public void whenUsingScanner_thenReturnTotalNumberOfLines() throws IOException {
    try (Scanner scanner = new Scanner(new FileReader(INPUT_FILE_NAME))) {
        int noOfLines = 0;
        while (scanner.hasNextLine()) {
            scanner.nextLine();
            noOfLines++;
        }
        assertEquals(NO_OF_LINES, noOfLines);
    }
}

优点:适合已有 Scanner 处理逻辑的项目。
缺点:性能不如其他方法。

9. 总结

在本教程中,我们介绍了多种使用 Java 统计文件行数的方法,包括:

方法 优点 缺点
Files.lines() / readAllLines() 简洁、现代 内存占用高
FileChannel 高性能 实现复杂
Guava.Files.readLines() 简洁、易用 需引入依赖
FileUtils.lineIterator() 兼容性好 代码啰嗦
BufferedReader 原生支持 代码繁琐
LineNumberReader 行号内置 行号从0开始
Scanner 适合已有逻辑 性能一般

选择建议

  • 小文件 → 用 Files.readAllLines().size()
  • 大文件 → 用 FileChannel
  • 快速开发 → 用 Guava
  • 旧项目 → 用 Apache Commons IO 或 BufferedReader

所有示例代码均可在 GitHub 上找到。


原始标题:Find the Number of Lines in a File Using Java