diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java
index 02c569d1a..624aeecd9 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileUtil.java
@@ -544,25 +544,66 @@ public class FileUtil extends PathUtil {
/**
* 计算文件的总行数
- * 读取文件采用系统默认编码,一般乱码不会造成行数错误。
+ * 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java
*
* @param file 文件
* @return 该文件总行数
* @since 5.7.22
*/
public static int getTotalLines(final File file) {
- if (!isFile(file)) {
+ return getTotalLines(file, 1024);
+ }
+
+ /**
+ * 计算文件的总行数
+ * 参考: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(final 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();
- } catch (final IOException e) {
+ 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);
}
}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/file/FileUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/file/FileUtilTest.java
index 837ca4515..25ad2a6eb 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/io/file/FileUtilTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/file/FileUtilTest.java
@@ -495,11 +495,17 @@ public class FileUtilTest {
}
@Test
- @Disabled
public void getTotalLinesTest() {
- // 千万行秒级内返回
- final int totalLines = FileUtil.getTotalLines(FileUtil.file(""));
- Assertions.assertEquals(10000000, totalLines);
+ // 此文件最后一行有换行符,则最后的空行算作一行
+ final int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines.csv"));
+ Assertions.assertEquals(8, totalLines);
+ }
+
+ @Test
+ public void issue3591Test() {
+ // 此文件最后一行末尾无换行符
+ final int totalLines = FileUtil.getTotalLines(FileUtil.file("1_psi_index_0.txt"));
+ Assertions.assertEquals(11, totalLines);
}
@Test
diff --git a/hutool-core/src/test/resources/1_psi_index_0.txt b/hutool-core/src/test/resources/1_psi_index_0.txt
new file mode 100644
index 000000000..2465b23f4
--- /dev/null
+++ b/hutool-core/src/test/resources/1_psi_index_0.txt
@@ -0,0 +1,11 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
\ No newline at end of file