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