From fcab6e63400debc5593b30c96301db7c7c87fc0e Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 18 Feb 2025 18:34:25 +0800 Subject: [PATCH] =?UTF-8?q?`FileWriter`=E5=A2=9E=E5=8A=A0=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=8F=AF=E9=80=89=E6=98=AF=E5=90=A6=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E6=8D=A2=E8=A1=8C=E7=AC=A6=EF=BC=88issue#3858@Github?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/hutool/core/io/file/FileUtil.java | 41 +++++++++++++++++-- .../hutool/core/io/file/FileWriter.java | 21 +++++++++- .../hutool/core/io/stream/LineCounter.java | 28 +++++++++++-- .../hutool/core/io/file/FileUtilTest.java | 15 +++++-- 4 files changed, 95 insertions(+), 10 deletions(-) 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 855edabd3..957efe21f 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 @@ -564,7 +564,8 @@ public class FileUtil { /** * 计算文件的总行数
- * 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java + * 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java
+ * 最后一行如果末尾带有换行符,则被当作为新行 * * @param file 文件 * @return 该文件总行数 @@ -576,7 +577,8 @@ public class FileUtil { /** * 计算文件的总行数
- * 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java + * 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java
+ * 最后一行如果末尾带有换行符,则被当作为新行 * * @param file 文件 * @param bufferSize 缓存大小,小于1则使用默认的1024 @@ -584,8 +586,23 @@ public class FileUtil { * @since 5.8.28 */ public static int getTotalLines(final File file, final int bufferSize) { + return getTotalLines(file, bufferSize, true); + } + + /** + * 计算文件的总行数
+ * 参考:https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java + * + * @param file 文件 + * @param bufferSize 缓存大小,小于1则使用默认的1024 + * @param lastLineSeparatorAsNewLine 是否将最后一行分隔符作为新行,Linux下要求最后一行必须带有换行符,不算一行,此处用户选择 + * @return 该文件总行数 + * @since 5.8.28 + */ + public static int getTotalLines(final File file, final int bufferSize, final boolean lastLineSeparatorAsNewLine) { Assert.isTrue(isFile(file), ()-> new IORuntimeException("Input must be a File")); try (final LineCounter lineCounter = new LineCounter(getInputStream(file), bufferSize)) { + lineCounter.setLastLineSeparatorAsNewLine(lastLineSeparatorAsNewLine); return lineCounter.getCount(); } catch (final IOException e) { throw new IORuntimeException(e); @@ -2493,10 +2510,28 @@ public class FileUtil { * @return 目标文件 * @throws IORuntimeException IO异常 */ - public static File writeLines(final Collection list, final File file, final Charset charset, final boolean isAppend) throws IORuntimeException { + public static File writeLines(final Collection list, final File file, final Charset charset, + final boolean isAppend) throws IORuntimeException { return FileWriter.of(file, charset).writeLines(list, isAppend); } + /** + * 将列表写入文件 + * + * @param 集合元素类型 + * @param list 列表 + * @param file 文件 + * @param charset 字符集 + * @param isAppend 是否追加 + * @param appendLineSeparator 是否在末尾追加换行符 + * @return 目标文件 + * @throws IORuntimeException IO异常 + */ + public static File writeLines(final Collection list, final File file, final Charset charset, + final boolean isAppend, final boolean appendLineSeparator) throws IORuntimeException { + return FileWriter.of(file, charset).writeLines(list, null, isAppend, appendLineSeparator); + } + /** * 将Map写入文件,每个键值对为一行,一行中键与值之间使用kvSeparator分隔 * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileWriter.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileWriter.java index dc3c7b6d0..c3af771f7 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileWriter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileWriter.java @@ -193,7 +193,7 @@ public class FileWriter extends FileWrapper { } /** - * 将列表写入文件 + * 将列表写入文件,最后一行末尾不追加换行符 * * @param 集合元素类型 * @param list 列表 @@ -204,6 +204,22 @@ public class FileWriter extends FileWrapper { * @since 3.1.0 */ public File writeLines(final Iterable list, final LineSeparator lineSeparator, final boolean isAppend) throws IORuntimeException { + return writeLines(list, lineSeparator, isAppend, false); + } + + /** + * 将列表写入文件 + * + * @param 集合元素类型 + * @param list 列表 + * @param lineSeparator 换行符枚举(Windows、Mac或Linux换行符) + * @param isAppend 是否追加 + * @param appendLineSeparator 是否在最后一行末尾追加换行符,Linux下要求最后一行必须带有换行符 + * @return 目标文件 + * @throws IORuntimeException IO异常 + * @since 3.1.0 + */ + public File writeLines(final Iterable list, final LineSeparator lineSeparator, final boolean isAppend, final boolean appendLineSeparator) throws IORuntimeException { try (final PrintWriter writer = getPrintWriter(isAppend)) { boolean isFirst = true; for (final T t : list) { @@ -218,6 +234,9 @@ public class FileWriter extends FileWrapper { printNewLine(writer, lineSeparator); } writer.print(t); + if(appendLineSeparator){ + printNewLine(writer, lineSeparator); + } writer.flush(); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/LineCounter.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/LineCounter.java index 42c67aad6..331555066 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/LineCounter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/LineCounter.java @@ -33,6 +33,10 @@ public class LineCounter implements Closeable { private final InputStream is; private final int bufferSize; + /** + * 是否将最后一行分隔符作为新行,Linux下要求最后一行必须带有换行符,不算一行,此处用户选择 + */ + private boolean lastLineSeparatorAsNewLine = true; private int count = -1; /** @@ -46,6 +50,17 @@ public class LineCounter implements Closeable { this.bufferSize = bufferSize < 1 ? 1024 : bufferSize; } + /** + * 设置是否将最后一行分隔符作为新行,Linux下要求最后一行必须带有换行符,不算一行,此处用户选择 + * + * @param lastLineSeparatorAsNewLine 是否将最后一行分隔符作为新行 + * @return this + */ + public LineCounter setLastLineSeparatorAsNewLine(final boolean lastLineSeparatorAsNewLine) { + this.lastLineSeparatorAsNewLine = lastLineSeparatorAsNewLine; + return this; + } + /** * 获取行数 * @@ -108,9 +123,16 @@ public class LineCounter implements Closeable { readChars = is.read(buf); } - // 最后一个字符为换行符,则单独计数行 - if(c == CharUtil.CR){ - ++count; + if(lastLineSeparatorAsNewLine){ + // 最后一个字符为\r,则单独计数行 + if(c == CharUtil.CR){ + ++count; + } + }else{ + // 最后一个字符为\n,则可选是否算作新行单独计数行 + if(c == CharUtil.LF){ + --count; + } } return count; 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 6d3c1c5b8..799b19be0 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 @@ -467,22 +467,31 @@ public class FileUtilTest { @Test public void getTotalLinesTest() { // 此文件最后一行有换行符,则最后的空行算作一行 - final int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines.csv")); + int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines.csv")); Assertions.assertEquals(8, totalLines); + + totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines.csv"), -1, false); + Assertions.assertEquals(7, totalLines); } @Test public void getTotalLinesCrTest() { // 此文件最后一行有换行符,则最后的空行算作一行 - final int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines_cr.csv")); + int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines_cr.csv")); assertEquals(8, totalLines); + + totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines_cr.csv"), -1, false); + Assertions.assertEquals(7, totalLines); } @Test public void getTotalLinesCrlfTest() { // 此文件最后一行有换行符,则最后的空行算作一行 - final int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines_crlf.csv")); + int totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines_crlf.csv")); assertEquals(8, totalLines); + + totalLines = FileUtil.getTotalLines(FileUtil.file("test_lines_crlf.csv"), -1, false); + Assertions.assertEquals(7, totalLines); } @Test