diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ab547f9c..0ab1185ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
* 【core 】 Tree增加filter、filterNew、cloneTree、hasChild方法(issue#I4HFC6@Gitee)
* 【poi 】 增加ColumnSheetReader及ExcelReader.readColumn,支持读取某一列
* 【core 】 IdCardUtil.isValidCard不再自动trim(issue#I4I04O@Gitee)
+* 【core 】 IdCardUtil.isValidCard不再自动trim(issue#I4I04O@Gitee)
### 🐞Bug修复
* 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github)
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java
index 511fb3c6e..71392a3a7 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java
@@ -7,6 +7,8 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.Matcher;
import cn.hutool.core.lang.func.Func1;
+import cn.hutool.core.text.finder.Finder;
+import cn.hutool.core.text.finder.StrFinder;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
@@ -35,7 +37,7 @@ import java.util.function.Predicate;
*/
public class CharSequenceUtil {
- public static final int INDEX_NOT_FOUND = -1;
+ public static final int INDEX_NOT_FOUND = Finder.INDEX_NOT_FOUND;
/**
* 字符串常量:{@code "null"}
@@ -1101,9 +1103,13 @@ public class CharSequenceUtil {
if (start < 0 || start > len) {
start = 0;
}
- if (end > len || end < 0) {
+ if (end > len) {
end = len;
}
+ if (end < 0) {
+ end += len;
+ }
+
for (int i = start; i < end; i++) {
if (str.charAt(i) == searchChar) {
return i;
@@ -1168,40 +1174,22 @@ public class CharSequenceUtil {
/**
* 指定范围内查找字符串
*
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @param fromIndex 起始位置
+ * @param text 字符串,空则返回-1
+ * @param searchStr 需要查找位置的字符串,空则返回-1
+ * @param from 起始位置(包含)
* @param ignoreCase 是否忽略大小写
* @return 位置
* @since 3.2.1
*/
- public static int indexOf(final CharSequence str, CharSequence searchStr, int fromIndex, boolean ignoreCase) {
- if (str == null || searchStr == null) {
- return INDEX_NOT_FOUND;
- }
- if (fromIndex < 0) {
- fromIndex = 0;
- }
-
- final int endLimit = str.length() - searchStr.length() + 1;
- if (fromIndex > endLimit) {
- return INDEX_NOT_FOUND;
- }
- if (searchStr.length() == 0) {
- return fromIndex;
- }
-
- if (false == ignoreCase) {
- // 不忽略大小写调用JDK方法
- return str.toString().indexOf(searchStr.toString(), fromIndex);
- }
-
- for (int i = fromIndex; i < endLimit; i++) {
- if (isSubEquals(str, i, searchStr, 0, searchStr.length(), true)) {
- return i;
+ public static int indexOf(CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) {
+ if (isEmpty(text) || isEmpty(searchStr)) {
+ if (StrUtil.equals(text, searchStr)) {
+ return 0;
+ } else {
+ return INDEX_NOT_FOUND;
}
}
- return INDEX_NOT_FOUND;
+ return new StrFinder(searchStr, ignoreCase).setText(text).start(from);
}
/**
@@ -1212,7 +1200,7 @@ public class CharSequenceUtil {
* @return 位置
* @since 3.2.1
*/
- public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
+ public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
return lastIndexOfIgnoreCase(str, searchStr, str.length());
}
@@ -1226,7 +1214,7 @@ public class CharSequenceUtil {
* @return 位置
* @since 3.2.1
*/
- public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int fromIndex) {
+ public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) {
return lastIndexOf(str, searchStr, fromIndex, true);
}
@@ -1234,37 +1222,22 @@ public class CharSequenceUtil {
* 指定范围内查找字符串
* fromIndex 为搜索起始位置,从后往前计数
*
- * @param str 字符串
+ * @param text 字符串
* @param searchStr 需要查找位置的字符串
- * @param fromIndex 起始位置,从后往前计数
+ * @param from 起始位置,从后往前计数
* @param ignoreCase 是否忽略大小写
* @return 位置
* @since 3.2.1
*/
- public static int lastIndexOf(final CharSequence str, final CharSequence searchStr, int fromIndex, boolean ignoreCase) {
- if (str == null || searchStr == null) {
- return INDEX_NOT_FOUND;
- }
- if (fromIndex < 0) {
- fromIndex = 0;
- }
- fromIndex = Math.min(fromIndex, str.length());
-
- if (searchStr.length() == 0) {
- return fromIndex;
- }
-
- if (false == ignoreCase) {
- // 不忽略大小写调用JDK方法
- return str.toString().lastIndexOf(searchStr.toString(), fromIndex);
- }
-
- for (int i = fromIndex; i >= 0; i--) {
- if (isSubEquals(str, i, searchStr, 0, searchStr.length(), true)) {
- return i;
+ public static int lastIndexOf(CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) {
+ if (isEmpty(text) || isEmpty(searchStr)) {
+ if (StrUtil.equals(text, searchStr)) {
+ return 0;
+ } else {
+ return INDEX_NOT_FOUND;
}
}
- return INDEX_NOT_FOUND;
+ return new StrFinder(searchStr, ignoreCase, true).setText(text).start(from);
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java b/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java
index 90f041352..eb47e7630 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/StrSplitter.java
@@ -169,10 +169,9 @@ public class StrSplitter {
* @param ignoreEmpty 是否忽略空串
* @param ignoreCase 是否忽略大小写
* @return 切分后的集合
- * @since 3.2.1
*/
public static List split(CharSequence text, char separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
- return split(text, separator, limit, ignoreEmpty, trimFunc(isTrim));
+ return split(text, separator, limit, ignoreEmpty, ignoreCase, trimFunc(isTrim));
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/CharFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/CharFinder.java
index 0fed9fefc..358815c1a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/CharFinder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/CharFinder.java
@@ -4,7 +4,8 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
/**
- * 字符查找器
+ * 字符查找器
+ * 查找指定字符在字符串中的位置信息
*
* @author looly
* @since 5.7.14
@@ -38,8 +39,8 @@ public class CharFinder extends TextFinder {
@Override
public int start(int from) {
Assert.notNull(this.text, "Text to find must be not null!");
- final int length = text.length();
- for (int i = from; i < length; i++) {
+ final int limit = getValidEndIndex(false);
+ for (int i = from; i < limit; i++) {
if (NumberUtil.equals(c, text.charAt(i), caseInsensitive)) {
return i;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/CharMatcherFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/CharMatcherFinder.java
index 301f3a257..7f3537a7a 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/CharMatcherFinder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/CharMatcherFinder.java
@@ -4,7 +4,8 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Matcher;
/**
- * 字符匹配查找器
+ * 字符匹配查找器
+ * 查找满足指定{@link Matcher} 匹配的字符所在位置,此类长用于查找某一类字符,如数字等
*
* @since 5.7.14
* @author looly
@@ -25,8 +26,8 @@ public class CharMatcherFinder extends TextFinder {
@Override
public int start(int from) {
Assert.notNull(this.text, "Text to find must be not null!");
- final int length = text.length();
- for (int i = from; i < length; i++) {
+ final int limit = getValidEndIndex(false);
+ for (int i = from; i < limit; i++) {
if(matcher.match(text.charAt(i))){
return i;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/Finder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/Finder.java
index 82ab597ed..f6a1aa049 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/Finder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/Finder.java
@@ -8,10 +8,12 @@ package cn.hutool.core.text.finder;
*/
public interface Finder {
+ int INDEX_NOT_FOUND = -1;
+
/**
* 返回开始位置,即起始字符位置(包含),未找到返回-1
*
- * @param from 查找的开始位置(包含
+ * @param from 查找的开始位置(包含)
* @return 起始字符位置,未找到返回-1
*/
int start(int from);
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/LengthFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/LengthFinder.java
index ca19d4424..665d2438d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/LengthFinder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/LengthFinder.java
@@ -3,7 +3,8 @@ package cn.hutool.core.text.finder;
import cn.hutool.core.lang.Assert;
/**
- * 固定长度查找器
+ * 固定长度查找器
+ * 给定一个长度,查找的位置为from + length,一般用于分段截取
*
* @since 5.7.14
* @author looly
@@ -25,7 +26,8 @@ public class LengthFinder extends TextFinder {
public int start(int from) {
Assert.notNull(this.text, "Text to find must be not null!");
final int result = from + length;
- if(result < text.length()){
+ final int limit = getValidEndIndex(false);
+ if(result < limit){
return result;
}
return -1;
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java
index a393b9e23..af0d3db57 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/PatternFinder.java
@@ -4,7 +4,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * 正则查找器
+ * 正则查找器
+ * 通过传入正则表达式,查找指定字符串中匹配正则的开始和结束位置
*
* @author looly
* @since 5.7.14
@@ -43,14 +44,24 @@ public class PatternFinder extends TextFinder {
@Override
public int start(int from) {
if (matcher.find(from)) {
- return matcher.start();
+ // 只有匹配到的字符串结尾在limit范围内,才算找到
+ if(matcher.end() <= getValidEndIndex(false)){
+ return matcher.start();
+ }
}
- return -1;
+ return INDEX_NOT_FOUND;
}
@Override
public int end(int start) {
- return matcher.end();
+ final int end = matcher.end();
+ final int limit;
+ if(endIndex < 0){
+ limit = text.length();
+ }else{
+ limit = Math.min(endIndex, text.length());
+ }
+ return end < limit ? end : INDEX_NOT_FOUND;
}
@Override
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/StrFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/StrFinder.java
index 43ecb8b82..2b11b0a15 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/StrFinder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/StrFinder.java
@@ -1,10 +1,10 @@
package cn.hutool.core.text.finder;
import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.text.CharSequenceUtil;
/**
- * 字符查找器
+ * 字符串查找器
*
* @author looly
* @since 5.7.14
@@ -12,25 +12,59 @@ import cn.hutool.core.util.StrUtil;
public class StrFinder extends TextFinder {
private static final long serialVersionUID = 1L;
- private final CharSequence str;
+ private final CharSequence strToFind;
private final boolean caseInsensitive;
+ private final boolean negative;
/**
* 构造
*
- * @param str 被查找的字符串
+ * @param strToFind 被查找的字符串
* @param caseInsensitive 是否忽略大小写
*/
- public StrFinder(CharSequence str, boolean caseInsensitive) {
- Assert.notEmpty(str);
- this.str = str;
+ public StrFinder(CharSequence strToFind, boolean caseInsensitive) {
+ this(strToFind, caseInsensitive, false);
+ }
+
+ /**
+ * 构造
+ *
+ * @param strToFind 被查找的字符串
+ * @param caseInsensitive 是否忽略大小写
+ * @param negative 是否从后向前查找模式
+ */
+ public StrFinder(CharSequence strToFind, boolean caseInsensitive, boolean negative ) {
+ Assert.notEmpty(strToFind);
+ this.strToFind = strToFind;
this.caseInsensitive = caseInsensitive;
+ this.negative = negative ;
}
@Override
public int start(int from) {
Assert.notNull(this.text, "Text to find must be not null!");
- return StrUtil.indexOf(text, str, from, caseInsensitive);
+ final int subLen = strToFind.length();
+
+ if (from < 0) {
+ from = 0;
+ }
+ int endLimit = getValidEndIndex(negative);
+ if(negative){
+ for (int i = from; i > endLimit; i--) {
+ if (CharSequenceUtil.isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) {
+ return i;
+ }
+ }
+ } else {
+ endLimit = endLimit - subLen + 1;
+ for (int i = from; i < endLimit; i++) {
+ if (CharSequenceUtil.isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) {
+ return i;
+ }
+ }
+ }
+
+ return INDEX_NOT_FOUND;
}
@Override
@@ -38,6 +72,6 @@ public class StrFinder extends TextFinder {
if (start < 0) {
return -1;
}
- return start + str.length();
+ return start + strToFind.length();
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/finder/TextFinder.java b/hutool-core/src/main/java/cn/hutool/core/text/finder/TextFinder.java
index 7045aa8fd..5c0088ec9 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/finder/TextFinder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/finder/TextFinder.java
@@ -1,5 +1,7 @@
package cn.hutool.core.text.finder;
+import cn.hutool.core.lang.Assert;
+
import java.io.Serializable;
/**
@@ -12,6 +14,7 @@ public abstract class TextFinder implements Finder, Serializable {
private static final long serialVersionUID = 1L;
protected CharSequence text;
+ protected int endIndex = -1;
/**
* 设置被查找的文本
@@ -20,7 +23,37 @@ public abstract class TextFinder implements Finder, Serializable {
* @return this
*/
public TextFinder setText(CharSequence text) {
- this.text = text;
+ this.text = Assert.notNull(text, "Text must be not null!");
return this;
}
+
+ /**
+ * 设置查找的结束位置
+ * 如果从前向后查找,结束位置最大为text.length()
+ * 如果从后向前,结束位置为-1
+ *
+ * @param endIndex 结束位置(不包括)
+ * @return this
+ */
+ public TextFinder setEndIndex(int endIndex) {
+ this.endIndex = endIndex;
+ return this;
+ }
+
+ /**
+ * 获取有效结束位置
+ * 如果{@link #endIndex}小于0,在反向模式下是开头(-1),正向模式是结尾(text.length())
+ *
+ * @param negative 是否从后向前查找模式
+ * @return 有效结束位置
+ */
+ protected int getValidEndIndex(boolean negative) {
+ final int limit;
+ if (endIndex < 0) {
+ limit = negative ? -1 : text.length();
+ } else {
+ limit = Math.min(endIndex, text.length());
+ }
+ return limit;
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/text/split/SplitIterTest.java b/hutool-core/src/test/java/cn/hutool/core/text/split/SplitIterTest.java
index 8bc3027fc..18050df58 100644
--- a/hutool-core/src/test/java/cn/hutool/core/text/split/SplitIterTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/text/split/SplitIterTest.java
@@ -135,4 +135,18 @@ public class SplitIterTest {
final List strings = splitIter.toList(false);
Assert.assertEquals(1, strings.size());
}
+
+ // 切割字符串是空字符串时报错
+ @Test(expected = IllegalArgumentException.class)
+ public void splitByEmptyTest(){
+ String text = "aa,bb,cc";
+ SplitIter splitIter = new SplitIter(text,
+ new StrFinder("", false),
+ 3,
+ false
+ );
+
+ final List strings = splitIter.toList(false);
+ Assert.assertEquals(1, strings.size());
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java
index b0aca478c..9b9ebc7b4 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java
@@ -177,7 +177,7 @@ public class StrUtilTest {
Assert.assertEquals(5, StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3));
Assert.assertEquals(-1, StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9));
Assert.assertEquals(2, StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1));
- Assert.assertEquals(2, StrUtil.indexOfIgnoreCase("aabaabaa", "", 2));
+ Assert.assertEquals(-1, StrUtil.indexOfIgnoreCase("aabaabaa", "", 2));
Assert.assertEquals(-1, StrUtil.indexOfIgnoreCase("abc", "", 9));
}
@@ -199,8 +199,8 @@ public class StrUtilTest {
Assert.assertEquals(2, StrUtil.lastIndexOfIgnoreCase("aabaabaa", "B", 3));
Assert.assertEquals(5, StrUtil.lastIndexOfIgnoreCase("aabaabaa", "B", 9));
Assert.assertEquals(-1, StrUtil.lastIndexOfIgnoreCase("aabaabaa", "B", -1));
- Assert.assertEquals(2, StrUtil.lastIndexOfIgnoreCase("aabaabaa", "", 2));
- Assert.assertEquals(3, StrUtil.lastIndexOfIgnoreCase("abc", "", 9));
+ Assert.assertEquals(-1, StrUtil.lastIndexOfIgnoreCase("aabaabaa", "", 2));
+ Assert.assertEquals(-1, StrUtil.lastIndexOfIgnoreCase("abc", "", 9));
Assert.assertEquals(0, StrUtil.lastIndexOfIgnoreCase("AAAcsd", "aaa"));
}