This commit is contained in:
Looly 2022-11-13 22:06:29 +08:00
parent 7e28a55c75
commit d21c641efd
7 changed files with 122 additions and 58 deletions

View File

@ -1,22 +1,44 @@
package cn.hutool.core.io; 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.IOException;
import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Iterator;
/** /**
* 行读取器类似于BufferedInputStream支持注释和多行转义 * 行读取器类似于BufferedInputStream支持多行转义规则如下<br>
* TODO 待实现 * <ul>
* <li>支持'\n''\r\n'两种换行符不支持'\r'换行符</li>
* <li>如果想读取转义符必须定义为'\\'</li>
* <li>多行转义后的换行符和空格都会被忽略</li>
* </ul>
* <p>
* 例子
* <pre>
* a=1\
* 2
* </pre>
* 读出后就是{@code a=12}
* *
* @author looly * @author looly
* @since 6.0.0
*/ */
public class LineReader extends ReaderWrapper { public class LineReader extends ReaderWrapper implements Iterable<String> {
/** /**
* 注释标识符 * 构造
*
* @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} * @param reader {@link Reader}
*/ */
public LineReader(final Reader reader) { public LineReader(final Reader reader) {
super(reader); super(IoUtil.toBuffered(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;
} }
/** /**
@ -49,6 +56,60 @@ public class LineReader extends ReaderWrapper {
* @throws IOException IO异常 * @throws IOException IO异常
*/ */
public String readLine() throws IOException { 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<String> iterator() {
return new ComputeIter<String>() {
@Override
protected String computeNext() {
try {
return readLine();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
};
} }
} }

View File

@ -194,7 +194,7 @@ public class ResourceUtil {
public static EnumerationIter<URL> getResourceUrlIter(final String resource, final ClassLoader classLoader) { public static EnumerationIter<URL> getResourceUrlIter(final String resource, final ClassLoader classLoader) {
final Enumeration<URL> resources; final Enumeration<URL> resources;
try { try {
resources = ObjUtil.defaultIfNull(classLoader, ClassLoaderUtil.getClassLoader()).getResources(resource); resources = ObjUtil.defaultIfNull(classLoader, ClassLoaderUtil::getClassLoader).getResources(resource);
} catch (final IOException e) { } catch (final IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }

View File

@ -9,6 +9,7 @@ import java.util.function.UnaryOperator;
/** /**
* 可序列化的UnaryOperator * 可序列化的UnaryOperator
* *
* @param <T> 参数类型
* @author VampireAchao * @author VampireAchao
* @see UnaryOperator * @see UnaryOperator
*/ */

View File

@ -1,11 +1,30 @@
package cn.hutool.core.io; 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 org.junit.Test;
import java.io.IOException; import java.util.ArrayList;
public class LineReaderTest { public class LineReaderTest {
@Test @Test
public void readTest() throws IOException { public void readLfTest() {
final LineReader lineReader = new LineReader(ResourceUtil.getUtf8Reader("multi_line.properties"));
final ArrayList<String> 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<String> 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));
} }
} }

View File

@ -1,27 +1,5 @@
client.mode=single test1
configure={\ test2=a\
"singleServerConfig":{\ bc\
"idleConnectionTimeout":10000,\ d\\e
"pingTimeout":1000, \ test3=abc
"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"\
}

View File

@ -0,0 +1,5 @@
test1
test2=a\
bc\
d\\e
test3=abc

View File

@ -2,16 +2,16 @@ package cn.hutool.setting;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LineReader;
import cn.hutool.core.io.resource.Resource; import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert; 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.regex.ReUtil;
import cn.hutool.core.text.StrUtil; 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.core.util.SystemUtil;
import cn.hutool.log.Log; import cn.hutool.log.Log;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -101,9 +101,9 @@ public class SettingLoader {
*/ */
synchronized public boolean load(final InputStream settingStream) throws IOException { synchronized public boolean load(final InputStream settingStream) throws IOException {
this.groupedMap.clear(); this.groupedMap.clear();
BufferedReader reader = null; LineReader reader = null;
try { try {
reader = IoUtil.toReader(settingStream, this.charset); reader = new LineReader(settingStream, this.charset);
// 分组 // 分组
String group = null; String group = null;