diff --git a/hutool-core/src/main/java/cn/hutool/core/io/LineReader.java b/hutool-core/src/main/java/cn/hutool/core/io/LineReader.java
index 0e7c559ca..eba0c8455 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/LineReader.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/LineReader.java
@@ -1,22 +1,44 @@
package cn.hutool.core.io;
-import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.collection.iter.ComputeIter;
+import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.CharUtil;
import java.io.IOException;
+import java.io.InputStream;
import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Iterator;
/**
- * 行读取器,类似于BufferedInputStream,支持注释和多行转义
- * TODO 待实现
+ * 行读取器,类似于BufferedInputStream,支持多行转义,规则如下:
+ *
+ * - 支持'\n'和'\r\n'两种换行符,不支持'\r'换行符
+ * - 如果想读取转义符,必须定义为'\\'
+ * - 多行转义后的换行符和空格都会被忽略
+ *
+ *
+ * 例子:
+ *
+ * a=1\
+ * 2
+ *
+ * 读出后就是{@code a=12}
*
* @author looly
+ * @since 6.0.0
*/
-public class LineReader extends ReaderWrapper {
+public class LineReader extends ReaderWrapper implements Iterable {
/**
- * 注释标识符
+ * 构造
+ *
+ * @param in {@link InputStream}
+ * @param charset 编码
*/
- private char[] commentFlags;
+ public LineReader(final InputStream in, final Charset charset) {
+ this(IoUtil.toReader(in, charset));
+ }
/**
* 构造
@@ -24,22 +46,7 @@ public class LineReader extends ReaderWrapper {
* @param reader {@link Reader}
*/
public LineReader(final Reader reader) {
- super(reader);
- }
-
- /**
- * 设置注释行标识符
- *
- * @param commentFlags 注释行标识符
- * @return this
- */
- public LineReader setCommentFlags(final char... commentFlags) {
- if (ArrayUtil.isEmpty(commentFlags)) {
- // 无注释行
- this.commentFlags = null;
- }
- this.commentFlags = ArrayUtil.copy(commentFlags, new char[commentFlags.length]);
- return this;
+ super(IoUtil.toBuffered(reader));
}
/**
@@ -49,6 +56,60 @@ public class LineReader extends ReaderWrapper {
* @throws IOException IO异常
*/
public String readLine() throws IOException {
- return null;
+ StringBuilder str = null;
+ // 换行符前是否为转义符
+ boolean precedingBackslash = false;
+ int c;
+ while ((c = read()) > 0) {
+ if (null == str) {
+ // 只有有字符的情况下才初始化行,否则为行结束
+ str = StrUtil.builder(1024);
+ }
+ if (CharUtil.BACKSLASH == c) {
+ // 转义符转义,行尾需要使用'\'时,使用转义符转义,即`\\`
+ if (false == precedingBackslash) {
+ // 转义符,添加标识,但是不加入字符
+ precedingBackslash = true;
+ continue;
+ } else {
+ precedingBackslash = false;
+ }
+ } else {
+ if (precedingBackslash) {
+ // 转义模式下,跳过转义符后的所有空白符
+ if (CharUtil.isBlankChar(c)) {
+ continue;
+ }
+ // 遇到普通字符,关闭转义
+ precedingBackslash = false;
+ } else if (CharUtil.LF == c) {
+ // 非转义状态下,表示行的结束
+ // 如果换行符是`\r\n`,删除末尾的`\r`
+ final int lastIndex = str.length() - 1;
+ if (lastIndex >= 0 && CharUtil.CR == str.charAt(lastIndex)) {
+ str.deleteCharAt(lastIndex);
+ }
+ break;
+ }
+ }
+
+ str.append((char) c);
+ }
+
+ return StrUtil.toStringOrNull(str);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new ComputeIter() {
+ @Override
+ protected String computeNext() {
+ try {
+ return readLine();
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java
index bed806d64..1ce456083 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java
@@ -194,7 +194,7 @@ public class ResourceUtil {
public static EnumerationIter getResourceUrlIter(final String resource, final ClassLoader classLoader) {
final Enumeration resources;
try {
- resources = ObjUtil.defaultIfNull(classLoader, ClassLoaderUtil.getClassLoader()).getResources(resource);
+ resources = ObjUtil.defaultIfNull(classLoader, ClassLoaderUtil::getClassLoader).getResources(resource);
} catch (final IOException e) {
throw new IORuntimeException(e);
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/SerUnaryOperator.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/SerUnaryOperator.java
index bf6295228..8ddc6d489 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/func/SerUnaryOperator.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/SerUnaryOperator.java
@@ -9,6 +9,7 @@ import java.util.function.UnaryOperator;
/**
* 可序列化的UnaryOperator
*
+ * @param 参数类型
* @author VampireAchao
* @see UnaryOperator
*/
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/LineReaderTest.java b/hutool-core/src/test/java/cn/hutool/core/io/LineReaderTest.java
index 2300bcdaa..01bdfdc39 100755
--- a/hutool-core/src/test/java/cn/hutool/core/io/LineReaderTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/LineReaderTest.java
@@ -1,11 +1,30 @@
package cn.hutool.core.io;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.io.resource.ResourceUtil;
+import org.junit.Assert;
import org.junit.Test;
-import java.io.IOException;
+import java.util.ArrayList;
public class LineReaderTest {
@Test
- public void readTest() throws IOException {
+ public void readLfTest() {
+ final LineReader lineReader = new LineReader(ResourceUtil.getUtf8Reader("multi_line.properties"));
+ final ArrayList list = ListUtil.of(lineReader);
+ Assert.assertEquals(3, list.size());
+ Assert.assertEquals("test1", list.get(0));
+ Assert.assertEquals("test2=abcd\\e", list.get(1));
+ Assert.assertEquals("test3=abc", list.get(2));
+ }
+
+ @Test
+ public void readCrLfTest() {
+ final LineReader lineReader = new LineReader(ResourceUtil.getUtf8Reader("multi_line_crlf.properties"));
+ final ArrayList list = ListUtil.of(lineReader);
+ Assert.assertEquals(3, list.size());
+ Assert.assertEquals("test1", list.get(0));
+ Assert.assertEquals("test2=abcd\\e", list.get(1));
+ Assert.assertEquals("test3=abc", list.get(2));
}
}
diff --git a/hutool-core/src/test/resources/multi_line.properties b/hutool-core/src/test/resources/multi_line.properties
index 17719feaa..432d0997a 100755
--- a/hutool-core/src/test/resources/multi_line.properties
+++ b/hutool-core/src/test/resources/multi_line.properties
@@ -1,27 +1,5 @@
-client.mode=single
-configure={\
- "singleServerConfig":{\
- "idleConnectionTimeout":10000,\
- "pingTimeout":1000, \
- "connectTimeout":10000, \
- "timeout":3000,\
- "retryAttempts":3,\
- "retryInterval":1500,\
- "reconnectionTimeout":3000,\
- "failedAttempts":3,\
- "password":null,\
- "subscriptionsPerConnection":5,\
- "clientName":null,\
- "address": "redis://127.0.0.1:6379",\
- "subscriptionConnectionMinimumIdleSize":1,\
- "subscriptionConnectionPoolSize":50,\
- "connectionMinimumIdleSize":12,\
- "connectionPoolSize":12\
- },\
- "threads":2,\
- "nettyThreads":2,\
- "codec":{\
- "class":"org.redisson.client.codec.StringCodec"\
- },\
- "transportMode":"NIO"\
- }
+test1
+test2=a\
+ bc\
+ d\\e
+test3=abc
diff --git a/hutool-core/src/test/resources/multi_line_crlf.properties b/hutool-core/src/test/resources/multi_line_crlf.properties
new file mode 100755
index 000000000..12b8b1c9a
--- /dev/null
+++ b/hutool-core/src/test/resources/multi_line_crlf.properties
@@ -0,0 +1,5 @@
+test1
+test2=a\
+ bc\
+ d\\e
+test3=abc
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java b/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java
index 4735fe91f..516284d49 100755
--- a/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java
+++ b/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java
@@ -2,16 +2,16 @@ package cn.hutool.setting;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.io.LineReader;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.CharUtil;
-import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.CharUtil;
+import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.SystemUtil;
import cn.hutool.log.Log;
-import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -101,9 +101,9 @@ public class SettingLoader {
*/
synchronized public boolean load(final InputStream settingStream) throws IOException {
this.groupedMap.clear();
- BufferedReader reader = null;
+ LineReader reader = null;
try {
- reader = IoUtil.toReader(settingStream, this.charset);
+ reader = new LineReader(settingStream, this.charset);
// 分组
String group = null;