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
LineNumberReader
是 BufferedReader
的子类,自带行号功能,可以简化行数统计逻辑:
@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 上找到。