Skip to content

Commit

Permalink
修复FileUtil#getTotalLines在JDK9+结果错误问题
Browse files Browse the repository at this point in the history
  • Loading branch information
looly committed May 20, 2024
1 parent 228785b commit 9d4e69d
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* 【db 】 修复JndiDSFactory空指针问题
* 【core 】 修复BiMap.put错误的返回值(pr#1218@Gitee)
* 【core 】 修复BooleanUtil.andOfWrap针对null错误问题(issue#3587@Github)
* 【core 】 修复FileUtil#getTotalLines在JDK9+结果错误问题(issue#3591@Github)

-------------------------------------------------------------------------------------------------------------
# 5.8.27(2024-03-29)
Expand Down
85 changes: 57 additions & 28 deletions hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
Expand All @@ -24,20 +25,7 @@
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.ZipUtil;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
Expand Down Expand Up @@ -580,24 +568,65 @@ public static long size(File file, boolean includeDirSize) {

/**
* 计算文件的总行数<br>
* 读取文件采用系统默认编码,一般乱码不会造成行数错误。
* 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java
*
* @param file 文件
* @return 该文件总行数
* @since 5.7.22
*/
public static int getTotalLines(File file) {
return getTotalLines(file, 1024);
}

/**
* 计算文件的总行数<br>
* 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java
*
* @param file 文件
* @param bufferSize 缓存大小,小于1则使用默认的1024
* @return 该文件总行数
* @since 5.8.28
*/
public static int getTotalLines(File file, int bufferSize) {
if (false == isFile(file)) {
throw new IORuntimeException("Input must be a File");
}
try (final LineNumberReader lineNumberReader = new LineNumberReader(new java.io.FileReader(file))) {
// 设置起始为1
lineNumberReader.setLineNumber(1);
// 跳过文件中内容
//noinspection ResultOfMethodCallIgnored
lineNumberReader.skip(Long.MAX_VALUE);
// 获取当前行号
return lineNumberReader.getLineNumber();
if (bufferSize < 1) {
bufferSize = 1024;
}
try (InputStream is = getInputStream(file)) {
byte[] c = new byte[bufferSize];
int readChars = is.read(c);
if (readChars == -1) {
// 空文件,返回0
return 0;
}

// 起始行为1
// 如果只有一行,无换行符,则读取结束后返回1
// 如果多行,最后一行无换行符,最后一行需要单独计数
// 如果多行,最后一行有换行符,则空行算作一行
int count = 1;
while (readChars == bufferSize) {
for (int i = 0; i < bufferSize; i++) {
if (c[i] == CharUtil.LF) {
++count;
}
}
readChars = is.read(c);
}

// count remaining characters
while (readChars != -1) {
for (int i = 0; i < readChars; i++) {
if (c[i] == CharUtil.LF) {
++count;
}
}
readChars = is.read(c);
}

return count;
} catch (IOException e) {
throw new IORuntimeException(e);
}
Expand Down Expand Up @@ -1415,8 +1444,8 @@ public static boolean equals(File file1, File file2) throws IORuntimeException {
if (false == file1.exists() || false == file2.exists()) {
// 两个文件都不存在判断其路径是否相同, 对于一个存在一个不存在的情况,一定不相同
return false == file1.exists()//
&& false == file2.exists()//
&& pathEquals(file1, file2);
&& false == file2.exists()//
&& pathEquals(file1, file2);
}
return equals(file1.toPath(), file2.toPath());
}
Expand Down Expand Up @@ -3510,7 +3539,7 @@ public static File checkSlip(File parentFile, File file) throws IllegalArgumentE
* @since 4.1.15
*/
public static String getMimeType(String filePath) {
if(StrUtil.isBlank(filePath)){
if (StrUtil.isBlank(filePath)) {
return null;
}

Expand Down Expand Up @@ -3637,8 +3666,8 @@ private static File buildFile(File outFile, String fileName) {
// 替换Windows路径分隔符为Linux路径分隔符,便于统一处理
fileName = fileName.replace('\\', '/');
if (false == isWindows()
// 检查文件名中是否包含"/",不考虑以"/"结尾的情况
&& fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) {
// 检查文件名中是否包含"/",不考虑以"/"结尾的情况
&& fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) {
// 在Linux下多层目录创建存在问题,/会被当成文件名的一部分,此处做处理
// 使用/拆分路径(zip中无\),级联创建父目录
final List<String> pathParts = StrUtil.split(fileName, '/', false, true);
Expand Down
14 changes: 10 additions & 4 deletions hutool-core/src/test/java/cn/hutool/core/io/FileUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -523,11 +523,17 @@ public void createTempFileTest(){
}

@Test
@Ignore
public void getTotalLinesTest() {
// 千万行秒级内返回
final int totalLines = FileUtil.getTotalLines(FileUtil.file(""));
Assert.assertEquals(10000000, totalLines);
// 此文件最后一行有换行符,则最后的空行算作一行
final int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines.csv"));
Assert.assertEquals(8, totalLines);
}

@Test
public void issue3591Test() {
// 此文件最后一行末尾无换行符
final int totalLines = FileUtil.getTotalLines(FileUtil.file("1_psi_index_0.txt"));
Assert.assertEquals(11, totalLines);
}

@Test
Expand Down
11 changes: 11 additions & 0 deletions hutool-core/src/test/resources/1_psi_index_0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
0
1
2
3
4
5
6
7
8
9
10

0 comments on commit 9d4e69d

Please sign in to comment.