mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add FoundWord
This commit is contained in:
parent
fbc80c27ac
commit
850c766213
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# 5.5.3 (2020-12-07)
|
# 5.5.3 (2020-12-08)
|
||||||
|
|
||||||
### 新特性
|
### 新特性
|
||||||
* 【core 】 IdcardUtil增加行政区划83(issue#1277@Github)
|
* 【core 】 IdcardUtil增加行政区划83(issue#1277@Github)
|
||||||
@ -12,6 +12,7 @@
|
|||||||
* 【db 】 Db增加使用sql的page方法(issue#247@Gitee)
|
* 【db 】 Db增加使用sql的page方法(issue#247@Gitee)
|
||||||
* 【cache 】 CacheObj的isExpired()逻辑修改(issue#1295@Github)
|
* 【cache 】 CacheObj的isExpired()逻辑修改(issue#1295@Github)
|
||||||
* 【json 】 JSONStrFormater改为JSONStrFormatter
|
* 【json 】 JSONStrFormater改为JSONStrFormatter
|
||||||
|
* 【dfa 】 增加FoundWord(pr#1290@Github)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【cache 】 修复Cache中get重复misCount计数问题(issue#1281@Github)
|
* 【cache 】 修复Cache中get重复misCount计数问题(issue#1281@Github)
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package cn.hutool.core.lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 片段默认实现
|
||||||
|
*
|
||||||
|
* @param <T> 数字类型,用于表示位置index
|
||||||
|
* @author looly
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public class DefaultSegment<T extends Number> implements Segment<T> {
|
||||||
|
|
||||||
|
protected T startIndex;
|
||||||
|
protected T endIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
* @param startIndex 起始位置
|
||||||
|
* @param endIndex 结束位置
|
||||||
|
*/
|
||||||
|
public DefaultSegment(T startIndex, T endIndex) {
|
||||||
|
this.startIndex = startIndex;
|
||||||
|
this.endIndex = endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getStartIndex() {
|
||||||
|
return this.startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getEndIndex() {
|
||||||
|
return this.endIndex;
|
||||||
|
}
|
||||||
|
}
|
41
hutool-core/src/main/java/cn/hutool/core/lang/Segment.java
Normal file
41
hutool-core/src/main/java/cn/hutool/core/lang/Segment.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package cn.hutool.core.lang;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 片段表示,用于表示文本、集合等数据结构的一个区间。
|
||||||
|
* @param <T> 数字类型,用于表示位置index
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public interface Segment<T extends Number> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取起始位置
|
||||||
|
*
|
||||||
|
* @return 起始位置
|
||||||
|
*/
|
||||||
|
T getStartIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取结束位置
|
||||||
|
*
|
||||||
|
* @return 结束位置
|
||||||
|
*/
|
||||||
|
T getEndIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 片段长度,默认计算方法为abs({@link #getEndIndex()} - {@link #getEndIndex()})
|
||||||
|
*
|
||||||
|
* @return 片段长度
|
||||||
|
*/
|
||||||
|
default T length(){
|
||||||
|
final T start = Assert.notNull(getStartIndex(), "Start index must be not null!");
|
||||||
|
final T end = Assert.notNull(getEndIndex(), "End index must be not null!");
|
||||||
|
return Convert.convert((Type) start.getClass(), NumberUtil.sub(end, start).abs());
|
||||||
|
}
|
||||||
|
}
|
@ -523,7 +523,8 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
|
|||||||
* @param minimumCapacity 最小容量
|
* @param minimumCapacity 最小容量
|
||||||
*/
|
*/
|
||||||
private void ensureCapacity(int minimumCapacity) {
|
private void ensureCapacity(int minimumCapacity) {
|
||||||
if (minimumCapacity > value.length) {
|
// overflow-conscious code
|
||||||
|
if (minimumCapacity - value.length < 0) {
|
||||||
expandCapacity(minimumCapacity);
|
expandCapacity(minimumCapacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,8 +536,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
|
|||||||
* @param minimumCapacity 需要扩展的最小容量
|
* @param minimumCapacity 需要扩展的最小容量
|
||||||
*/
|
*/
|
||||||
private void expandCapacity(int minimumCapacity) {
|
private void expandCapacity(int minimumCapacity) {
|
||||||
int newCapacity = value.length * 2 + 2;
|
int newCapacity = (value.length << 1) + 2;
|
||||||
if (newCapacity < minimumCapacity) {
|
// overflow-conscious code
|
||||||
|
if (newCapacity - minimumCapacity < 0) {
|
||||||
newCapacity = minimumCapacity;
|
newCapacity = minimumCapacity;
|
||||||
}
|
}
|
||||||
if (newCapacity < 0) {
|
if (newCapacity < 0) {
|
||||||
|
@ -383,7 +383,7 @@ public class ObjectUtil {
|
|||||||
* 克隆对象<br>
|
* 克隆对象<br>
|
||||||
* 如果对象实现Cloneable接口,调用其clone方法<br>
|
* 如果对象实现Cloneable接口,调用其clone方法<br>
|
||||||
* 如果实现Serializable接口,执行深度克隆<br>
|
* 如果实现Serializable接口,执行深度克隆<br>
|
||||||
* 否则返回<code>null</code>
|
* 否则返回{@code null}
|
||||||
*
|
*
|
||||||
* @param <T> 对象类型
|
* @param <T> 对象类型
|
||||||
* @param obj 被克隆对象
|
* @param obj 被克隆对象
|
||||||
@ -606,11 +606,24 @@ public class ObjectUtil {
|
|||||||
return ArrayUtil.emptyCount(objs);
|
return ArrayUtil.emptyCount(objs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否存在{@code null}对象,通过{@link ObjectUtil#isNull(Object)} 判断元素
|
||||||
|
*
|
||||||
|
* @param objs 被检查对象
|
||||||
|
* @return 是否存在
|
||||||
|
* @since 5.5.3
|
||||||
|
* @see ArrayUtil#hasNull(Object[])
|
||||||
|
*/
|
||||||
|
public static boolean hasNull(Object... objs) {
|
||||||
|
return ArrayUtil.hasNull(objs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否存在{@code null}或空对象,通过{@link ObjectUtil#isEmpty(Object)} 判断元素
|
* 是否存在{@code null}或空对象,通过{@link ObjectUtil#isEmpty(Object)} 判断元素
|
||||||
*
|
*
|
||||||
* @param objs 被检查对象
|
* @param objs 被检查对象
|
||||||
* @return 是否存在
|
* @return 是否存在
|
||||||
|
* @see ArrayUtil#hasEmpty(Object...)
|
||||||
*/
|
*/
|
||||||
public static boolean hasEmpty(Object... objs) {
|
public static boolean hasEmpty(Object... objs) {
|
||||||
return ArrayUtil.hasEmpty(objs);
|
return ArrayUtil.hasEmpty(objs);
|
||||||
|
@ -105,7 +105,7 @@ public class StrUtil {
|
|||||||
public static final char C_COLON = CharUtil.COLON;
|
public static final char C_COLON = CharUtil.COLON;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符常量:艾特 <code>'@'</code>
|
* 字符常量:艾特 {@code '@'}
|
||||||
*/
|
*/
|
||||||
public static final char C_AT = CharUtil.AT;
|
public static final char C_AT = CharUtil.AT;
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ public class StrUtil {
|
|||||||
public static final String COLON = ":";
|
public static final String COLON = ":";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串常量:艾特 <code>"@"</code>
|
* 字符串常量:艾特 {@code "@"}
|
||||||
*/
|
*/
|
||||||
public static final String AT = "@";
|
public static final String AT = "@";
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ public class StrUtil {
|
|||||||
public static final String HTML_GT = ">";
|
public static final String HTML_GT = ">";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串常量:空 JSON <code>"{}"</code>
|
* 字符串常量:空 JSON {@code "{}"}
|
||||||
*/
|
*/
|
||||||
public static final String EMPTY_JSON = "{}";
|
public static final String EMPTY_JSON = "{}";
|
||||||
|
|
||||||
@ -542,7 +542,7 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 如果字符串是 <code>null</code>,则返回指定默认字符串,否则返回字符串本身。
|
* 如果字符串是 {@code null},则返回指定默认字符串,否则返回字符串本身。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* nullToDefault(null, "default") = "default"
|
* nullToDefault(null, "default") = "default"
|
||||||
@ -560,7 +560,7 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 如果字符串是<code>null</code>或者"",则返回指定默认字符串,否则返回字符串本身。
|
* 如果字符串是{@code null}或者"",则返回指定默认字符串,否则返回字符串本身。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* emptyToDefault(null, "default") = "default"
|
* emptyToDefault(null, "default") = "default"
|
||||||
@ -579,7 +579,7 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 如果字符串是<code>null</code>或者""或者空白,则返回指定默认字符串,否则返回字符串本身。
|
* 如果字符串是{@code null}或者""或者空白,则返回指定默认字符串,否则返回字符串本身。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* emptyToDefault(null, "default") = "default"
|
* emptyToDefault(null, "default") = "default"
|
||||||
@ -598,7 +598,7 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当给定字符串为空字符串时,转换为<code>null</code>
|
* 当给定字符串为空字符串时,转换为{@code null}
|
||||||
*
|
*
|
||||||
* @param str 被转换的字符串
|
* @param str 被转换的字符串
|
||||||
* @return 转换后的字符串
|
* @return 转换后的字符串
|
||||||
@ -774,10 +774,10 @@ public class StrUtil {
|
|||||||
// ------------------------------------------------------------------------ Trim
|
// ------------------------------------------------------------------------ Trim
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 除去字符串头尾部的空白,如果字符串是<code>null</code>,依然返回<code>null</code>。
|
* 除去字符串头尾部的空白,如果字符串是{@code null},依然返回{@code null}。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 注意,和<code>String.trim</code>不同,此方法使用<code>NumberUtil.isBlankChar</code> 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
|
* 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* trim(null) = null
|
* trim(null) = null
|
||||||
@ -788,7 +788,7 @@ public class StrUtil {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param str 要处理的字符串
|
* @param str 要处理的字符串
|
||||||
* @return 除去头尾空白的字符串,如果原字串为<code>null</code>,则返回<code>null</code>
|
* @return 除去头尾空白的字符串,如果原字串为{@code null},则返回{@code null}
|
||||||
*/
|
*/
|
||||||
public static String trim(CharSequence str) {
|
public static String trim(CharSequence str) {
|
||||||
return (null == str) ? null : trim(str, 0);
|
return (null == str) ? null : trim(str, 0);
|
||||||
@ -813,7 +813,7 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 除去字符串头尾部的空白,如果字符串是{@code null},返回<code>""</code>。
|
* 除去字符串头尾部的空白,如果字符串是{@code null},返回{@code ""}。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* StrUtil.trimToEmpty(null) = ""
|
* StrUtil.trimToEmpty(null) = ""
|
||||||
@ -852,10 +852,10 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 除去字符串头部的空白,如果字符串是<code>null</code>,则返回<code>null</code>。
|
* 除去字符串头部的空白,如果字符串是{@code null},则返回{@code null}。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 注意,和<code>String.trim</code>不同,此方法使用<code>CharUtil.isBlankChar</code> 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
|
* 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* trimStart(null) = null
|
* trimStart(null) = null
|
||||||
@ -867,17 +867,17 @@ public class StrUtil {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param str 要处理的字符串
|
* @param str 要处理的字符串
|
||||||
* @return 除去空白的字符串,如果原字串为<code>null</code>或结果字符串为<code>""</code>,则返回 <code>null</code>
|
* @return 除去空白的字符串,如果原字串为{@code null}或结果字符串为{@code ""},则返回 {@code null}
|
||||||
*/
|
*/
|
||||||
public static String trimStart(CharSequence str) {
|
public static String trimStart(CharSequence str) {
|
||||||
return trim(str, -1);
|
return trim(str, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 除去字符串尾部的空白,如果字符串是<code>null</code>,则返回<code>null</code>。
|
* 除去字符串尾部的空白,如果字符串是{@code null},则返回{@code null}。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 注意,和<code>String.trim</code>不同,此方法使用<code>CharUtil.isBlankChar</code> 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
|
* 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* trimEnd(null) = null
|
* trimEnd(null) = null
|
||||||
@ -889,47 +889,45 @@ public class StrUtil {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param str 要处理的字符串
|
* @param str 要处理的字符串
|
||||||
* @return 除去空白的字符串,如果原字串为<code>null</code>或结果字符串为<code>""</code>,则返回 <code>null</code>
|
* @return 除去空白的字符串,如果原字串为{@code null}或结果字符串为{@code ""},则返回 {@code null}
|
||||||
*/
|
*/
|
||||||
public static String trimEnd(CharSequence str) {
|
public static String trimEnd(CharSequence str) {
|
||||||
return trim(str, 1);
|
return trim(str, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 除去字符串头尾部的空白符,如果字符串是<code>null</code>,依然返回<code>null</code>。
|
* 除去字符串头尾部的空白符,如果字符串是{@code null},依然返回{@code null}。
|
||||||
*
|
*
|
||||||
* @param str 要处理的字符串
|
* @param str 要处理的字符串
|
||||||
* @param mode <code>-1</code>表示trimStart,<code>0</code>表示trim全部, <code>1</code>表示trimEnd
|
* @param mode {@code -1}表示trimStart,{@code 0}表示trim全部, {@code 1}表示trimEnd
|
||||||
* @return 除去指定字符后的的字符串,如果原字串为<code>null</code>,则返回<code>null</code>
|
* @return 除去指定字符后的的字符串,如果原字串为{@code null},则返回{@code null}
|
||||||
*/
|
*/
|
||||||
public static String trim(CharSequence str, int mode) {
|
public static String trim(CharSequence str, int mode) {
|
||||||
|
String result;
|
||||||
if (str == null) {
|
if (str == null) {
|
||||||
return null;
|
result = null;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
int length = str.length();
|
int length = str.length();
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = length;
|
int end = length;// 扫描字符串头部
|
||||||
|
|
||||||
// 扫描字符串头部
|
|
||||||
if (mode <= 0) {
|
if (mode <= 0) {
|
||||||
while ((start < end) && (CharUtil.isBlankChar(str.charAt(start)))) {
|
while ((start < end) && (CharUtil.isBlankChar(str.charAt(start)))) {
|
||||||
start++;
|
start++;
|
||||||
}
|
}
|
||||||
}
|
}// 扫描字符串尾部
|
||||||
|
|
||||||
// 扫描字符串尾部
|
|
||||||
if (mode >= 0) {
|
if (mode >= 0) {
|
||||||
while ((start < end) && (CharUtil.isBlankChar(str.charAt(end - 1)))) {
|
while ((start < end) && (CharUtil.isBlankChar(str.charAt(end - 1)))) {
|
||||||
end--;
|
end--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((start > 0) || (end < length)) {
|
if ((start > 0) || (end < length)) {
|
||||||
return str.toString().substring(start, end);
|
result = str.toString().substring(start, end);
|
||||||
|
} else {
|
||||||
|
result = str.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return str.toString();
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1251,7 +1249,7 @@ public class StrUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否包含特定字符,忽略大小写,如果给定两个参数都为<code>null</code>,返回true
|
* 是否包含特定字符,忽略大小写,如果给定两个参数都为{@code null},返回true
|
||||||
*
|
*
|
||||||
* @param str 被检测字符串
|
* @param str 被检测字符串
|
||||||
* @param testStr 被测试是否包含的字符串
|
* @param testStr 被测试是否包含的字符串
|
||||||
@ -2620,7 +2618,7 @@ public class StrUtil {
|
|||||||
*
|
*
|
||||||
* @param str1 要比较的字符串1
|
* @param str1 要比较的字符串1
|
||||||
* @param str2 要比较的字符串2
|
* @param str2 要比较的字符串2
|
||||||
* @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
|
* @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
|
||||||
*/
|
*/
|
||||||
public static boolean equals(CharSequence str1, CharSequence str2) {
|
public static boolean equals(CharSequence str1, CharSequence str2) {
|
||||||
return equals(str1, str2, false);
|
return equals(str1, str2, false);
|
||||||
@ -2639,7 +2637,7 @@ public class StrUtil {
|
|||||||
*
|
*
|
||||||
* @param str1 要比较的字符串1
|
* @param str1 要比较的字符串1
|
||||||
* @param str2 要比较的字符串2
|
* @param str2 要比较的字符串2
|
||||||
* @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
|
* @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
|
||||||
*/
|
*/
|
||||||
public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
|
public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
|
||||||
return equals(str1, str2, true);
|
return equals(str1, str2, true);
|
||||||
@ -2651,7 +2649,7 @@ public class StrUtil {
|
|||||||
* @param str1 要比较的字符串1
|
* @param str1 要比较的字符串1
|
||||||
* @param str2 要比较的字符串2
|
* @param str2 要比较的字符串2
|
||||||
* @param ignoreCase 是否忽略大小写
|
* @param ignoreCase 是否忽略大小写
|
||||||
* @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
|
* @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
*/
|
*/
|
||||||
public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
|
public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
|
||||||
@ -3431,7 +3429,7 @@ public class StrUtil {
|
|||||||
* StrUtil.padAfter("123", 2, '0');//"23"
|
* StrUtil.padAfter("123", 2, '0');//"23"
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param str 字符串,如果为<code>null</code>,直接返回null
|
* @param str 字符串,如果为{@code null},直接返回null
|
||||||
* @param minLength 最小长度
|
* @param minLength 最小长度
|
||||||
* @param padChar 补充的字符
|
* @param padChar 补充的字符
|
||||||
* @return 补充后的字符串
|
* @return 补充后的字符串
|
||||||
@ -3459,7 +3457,7 @@ public class StrUtil {
|
|||||||
* StrUtil.padAfter("123", 2, "ABC");//"23"
|
* StrUtil.padAfter("123", 2, "ABC");//"23"
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param str 字符串,如果为<code>null</code>,直接返回null
|
* @param str 字符串,如果为{@code null},直接返回null
|
||||||
* @param minLength 最小长度
|
* @param minLength 最小长度
|
||||||
* @param padStr 补充的字符
|
* @param padStr 补充的字符
|
||||||
* @return 补充后的字符串
|
* @return 补充后的字符串
|
||||||
|
@ -1,49 +1,61 @@
|
|||||||
package cn.hutool.dfa;
|
package cn.hutool.dfa;
|
||||||
|
|
||||||
/**
|
import cn.hutool.core.lang.DefaultSegment;
|
||||||
* @author 肖海斌
|
|
||||||
* <p>
|
|
||||||
* 匹配到的敏感词,包含敏感词,text中匹配敏感词的内容,以及匹配内容在text中的下标,
|
|
||||||
* 下标可以用来做敏感词的进一步处理,如果替换成**
|
|
||||||
*/
|
|
||||||
public class FoundWord {
|
|
||||||
/**
|
|
||||||
* 生效的敏感词
|
|
||||||
*/
|
|
||||||
private String word;
|
|
||||||
/**
|
|
||||||
* 敏感词匹配到的内容
|
|
||||||
*/
|
|
||||||
private String foundWord;
|
|
||||||
/**
|
|
||||||
* 匹配内容在待分析字符串中的开始位置
|
|
||||||
*/
|
|
||||||
private int startIndex;
|
|
||||||
/**
|
|
||||||
* 匹配内容在待分析字符串中的结束位置
|
|
||||||
*/
|
|
||||||
private int endIndex;
|
|
||||||
|
|
||||||
public FoundWord(String word, String foundWord, int start, int end) {
|
/**
|
||||||
|
* <p>
|
||||||
|
* 匹配到的单词,包含单词,text中匹配单词的内容,以及匹配内容在text中的下标,
|
||||||
|
* 下标可以用来做单词的进一步处理,如果替换成**
|
||||||
|
*
|
||||||
|
* @author 肖海斌
|
||||||
|
*/
|
||||||
|
public class FoundWord extends DefaultSegment<Integer> {
|
||||||
|
/**
|
||||||
|
* 生效的单词,即单词树中的词
|
||||||
|
*/
|
||||||
|
private final String word;
|
||||||
|
/**
|
||||||
|
* 单词匹配到的内容,即文中的单词
|
||||||
|
*/
|
||||||
|
private final String foundWord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param word 生效的单词,即单词树中的词
|
||||||
|
* @param foundWord 单词匹配到的内容,即文中的单词
|
||||||
|
* @param startIndex 起始位置(包含)
|
||||||
|
* @param endIndex 结束位置(包含)
|
||||||
|
*/
|
||||||
|
public FoundWord(String word, String foundWord, int startIndex, int endIndex) {
|
||||||
|
super(startIndex, endIndex);
|
||||||
this.word = word;
|
this.word = word;
|
||||||
this.foundWord = foundWord;
|
this.foundWord = foundWord;
|
||||||
this.startIndex = start;
|
|
||||||
this.endIndex = end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取生效的单词,即单词树中的词
|
||||||
|
*
|
||||||
|
* @return 生效的单词
|
||||||
|
*/
|
||||||
public String getWord() {
|
public String getWord() {
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单词匹配到的内容,即文中的单词
|
||||||
|
* @return 单词匹配到的内容
|
||||||
|
*/
|
||||||
public String getFoundWord() {
|
public String getFoundWord() {
|
||||||
return foundWord;
|
return foundWord;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStartIndex() {
|
/**
|
||||||
return startIndex;
|
* 默认的,只输出匹配到的关键字
|
||||||
}
|
* @return 匹配到的关键字
|
||||||
|
*/
|
||||||
public int getEndIndex() {
|
@Override
|
||||||
return endIndex;
|
public String toString() {
|
||||||
|
return this.foundWord;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package cn.hutool.dfa;
|
package cn.hutool.dfa;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
import cn.hutool.core.thread.ThreadUtil;
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@ -25,7 +25,7 @@ public final class SensitiveUtil {
|
|||||||
* @return 是否已经被初始化
|
* @return 是否已经被初始化
|
||||||
*/
|
*/
|
||||||
public static boolean isInited() {
|
public static boolean isInited() {
|
||||||
return !sensitiveTree.isEmpty();
|
return false == sensitiveTree.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,19 +117,44 @@ public final class SensitiveUtil {
|
|||||||
*
|
*
|
||||||
* @param text 文本
|
* @param text 文本
|
||||||
* @return 敏感词
|
* @return 敏感词
|
||||||
|
* @deprecated 请使用 {@link #getFoundFirstSensitive(String)}
|
||||||
*/
|
*/
|
||||||
public static FoundWord getFindedFirstSensitive(String text) {
|
@Deprecated
|
||||||
|
public static String getFindedFirstSensitive(String text) {
|
||||||
return sensitiveTree.match(text);
|
return sensitiveTree.match(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找敏感词,返回找到的第一个敏感词
|
||||||
|
*
|
||||||
|
* @param text 文本
|
||||||
|
* @return 敏感词
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public static FoundWord getFoundFirstSensitive(String text) {
|
||||||
|
return sensitiveTree.matchWord(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找敏感词,返回找到的第一个敏感词
|
||||||
|
*
|
||||||
|
* @param obj bean,会被转为JSON字符串
|
||||||
|
* @return 敏感词
|
||||||
|
* @deprecated 请使用 {@link #getFoundFirstSensitive(Object)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static String getFindedFirstSensitive(Object obj) {
|
||||||
|
return sensitiveTree.match(JSONUtil.toJsonStr(obj));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找敏感词,返回找到的第一个敏感词
|
* 查找敏感词,返回找到的第一个敏感词
|
||||||
*
|
*
|
||||||
* @param obj bean,会被转为JSON字符串
|
* @param obj bean,会被转为JSON字符串
|
||||||
* @return 敏感词
|
* @return 敏感词
|
||||||
*/
|
*/
|
||||||
public static FoundWord getFindedFirstSensitive(Object obj) {
|
public static FoundWord getFoundFirstSensitive(Object obj) {
|
||||||
return sensitiveTree.match(JSONUtil.toJsonStr(obj));
|
return sensitiveTree.matchWord(JSONUtil.toJsonStr(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,11 +162,40 @@ public final class SensitiveUtil {
|
|||||||
*
|
*
|
||||||
* @param text 文本
|
* @param text 文本
|
||||||
* @return 敏感词
|
* @return 敏感词
|
||||||
|
* @deprecated 请使用 {@link #getFoundAllSensitive(String)}
|
||||||
*/
|
*/
|
||||||
public static List<FoundWord> getFindedAllSensitive(String text) {
|
@Deprecated
|
||||||
|
public static List<String> getFindedAllSensitive(String text) {
|
||||||
return sensitiveTree.matchAll(text);
|
return sensitiveTree.matchAll(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找敏感词,返回找到的所有敏感词
|
||||||
|
*
|
||||||
|
* @param text 文本
|
||||||
|
* @return 敏感词
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public static List<FoundWord> getFoundAllSensitive(String text) {
|
||||||
|
return sensitiveTree.matchAllWords(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找敏感词,返回找到的所有敏感词<br>
|
||||||
|
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||||
|
* 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
|
||||||
|
*
|
||||||
|
* @param text 文本
|
||||||
|
* @param isDensityMatch 是否使用密集匹配原则
|
||||||
|
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||||
|
* @return 敏感词
|
||||||
|
* @deprecated 请使用 {@link #getFoundAllSensitive(String, boolean, boolean)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static List<String> getFindedAllSensitive(String text, boolean isDensityMatch, boolean isGreedMatch) {
|
||||||
|
return sensitiveTree.matchAll(text, -1, isDensityMatch, isGreedMatch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找敏感词,返回找到的所有敏感词<br>
|
* 查找敏感词,返回找到的所有敏感词<br>
|
||||||
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||||
@ -152,8 +206,8 @@ public final class SensitiveUtil {
|
|||||||
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||||
* @return 敏感词
|
* @return 敏感词
|
||||||
*/
|
*/
|
||||||
public static List<FoundWord> getFindedAllSensitive(String text, boolean isDensityMatch, boolean isGreedMatch) {
|
public static List<FoundWord> getFoundAllSensitive(String text, boolean isDensityMatch, boolean isGreedMatch) {
|
||||||
return sensitiveTree.matchAll(text, -1, isDensityMatch, isGreedMatch);
|
return sensitiveTree.matchAllWords(text, -1, isDensityMatch, isGreedMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,11 +215,24 @@ public final class SensitiveUtil {
|
|||||||
*
|
*
|
||||||
* @param bean 对象,会被转为JSON
|
* @param bean 对象,会被转为JSON
|
||||||
* @return 敏感词
|
* @return 敏感词
|
||||||
|
* @deprecated 请使用 {@link #getFoundAllSensitive(Object)}
|
||||||
*/
|
*/
|
||||||
public static List<FoundWord> getFindedAllSensitive(Object bean) {
|
@Deprecated
|
||||||
|
public static List<String> getFindedAllSensitive(Object bean) {
|
||||||
return sensitiveTree.matchAll(JSONUtil.toJsonStr(bean));
|
return sensitiveTree.matchAll(JSONUtil.toJsonStr(bean));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找敏感词,返回找到的所有敏感词
|
||||||
|
*
|
||||||
|
* @param bean 对象,会被转为JSON
|
||||||
|
* @return 敏感词
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public static List<FoundWord> getFoundAllSensitive(Object bean) {
|
||||||
|
return sensitiveTree.matchAllWords(JSONUtil.toJsonStr(bean));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找敏感词,返回找到的所有敏感词<br>
|
* 查找敏感词,返回找到的所有敏感词<br>
|
||||||
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||||
@ -175,9 +242,26 @@ public final class SensitiveUtil {
|
|||||||
* @param isDensityMatch 是否使用密集匹配原则
|
* @param isDensityMatch 是否使用密集匹配原则
|
||||||
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||||
* @return 敏感词
|
* @return 敏感词
|
||||||
|
* @deprecated 请使用 {@link #getFoundAllSensitive(Object, boolean, boolean)}
|
||||||
*/
|
*/
|
||||||
public static List<FoundWord> getFindedAllSensitive(Object bean, boolean isDensityMatch, boolean isGreedMatch) {
|
@Deprecated
|
||||||
return getFindedAllSensitive(JSONUtil.toJsonStr(bean), isDensityMatch, isGreedMatch);
|
public static List<String> getFindedAllSensitive(Object bean, boolean isDensityMatch, boolean isGreedMatch) {
|
||||||
|
return sensitiveTree.matchAll(JSONUtil.toJsonStr(bean), -1, isDensityMatch, isGreedMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找敏感词,返回找到的所有敏感词<br>
|
||||||
|
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||||
|
* 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
|
||||||
|
*
|
||||||
|
* @param bean 对象,会被转为JSON
|
||||||
|
* @param isDensityMatch 是否使用密集匹配原则
|
||||||
|
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||||
|
* @return 敏感词
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public static List<FoundWord> getFoundAllSensitive(Object bean, boolean isDensityMatch, boolean isGreedMatch) {
|
||||||
|
return getFoundAllSensitive(JSONUtil.toJsonStr(bean), isDensityMatch, isGreedMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,23 +275,27 @@ public final class SensitiveUtil {
|
|||||||
*/
|
*/
|
||||||
public static <T> T sensitiveFilter(T bean, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
|
public static <T> T sensitiveFilter(T bean, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
|
||||||
String jsonText = JSONUtil.toJsonStr(bean);
|
String jsonText = JSONUtil.toJsonStr(bean);
|
||||||
Class<T> c = (Class) bean.getClass();
|
@SuppressWarnings("unchecked")
|
||||||
|
final Class<T> c = (Class<T>) bean.getClass();
|
||||||
return JSONUtil.toBean(sensitiveFilter(jsonText, isGreedMatch, sensitiveProcessor), c);
|
return JSONUtil.toBean(sensitiveFilter(jsonText, isGreedMatch, sensitiveProcessor), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 处理过滤文本中的敏感词,默认替换成*
|
||||||
|
*
|
||||||
* @param text 文本
|
* @param text 文本
|
||||||
* @param isGreedMatch 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
|
* @param isGreedMatch 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
|
||||||
* @param sensitiveProcessor 敏感词处理器,默认按匹配内容的字符数替换成*
|
* @param sensitiveProcessor 敏感词处理器,默认按匹配内容的字符数替换成*
|
||||||
* @return 敏感词过滤处理后的文本
|
* @return 敏感词过滤处理后的文本
|
||||||
*/
|
*/
|
||||||
public static String sensitiveFilter(String text, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
|
public static String sensitiveFilter(String text, boolean isGreedMatch, SensitiveProcessor sensitiveProcessor) {
|
||||||
if (null == text || text.trim().equals("")) {
|
if (StrUtil.isEmpty(text)) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
//敏感词过滤场景下,不需要密集匹配
|
//敏感词过滤场景下,不需要密集匹配
|
||||||
List<FoundWord> foundWordList = getFindedAllSensitive(text, false, isGreedMatch);
|
List<FoundWord> foundWordList = getFoundAllSensitive(text, false, isGreedMatch);
|
||||||
if (CollectionUtil.isEmpty(foundWordList)) {
|
if (CollUtil.isEmpty(foundWordList)) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
sensitiveProcessor = sensitiveProcessor == null ? new SensitiveProcessor() {
|
sensitiveProcessor = sensitiveProcessor == null ? new SensitiveProcessor() {
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
package cn.hutool.dfa;
|
package cn.hutool.dfa;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
import cn.hutool.core.text.StrBuilder;
|
import cn.hutool.core.text.StrBuilder;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DFA(Deterministic Finite Automaton 确定有穷自动机)
|
* DFA(Deterministic Finite Automaton 确定有穷自动机)
|
||||||
@ -26,7 +32,7 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
private static final long serialVersionUID = -4646423269465809276L;
|
private static final long serialVersionUID = -4646423269465809276L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 敏感词字符末尾标识,用于标识单词末尾字符
|
* 单词字符末尾标识,用于标识单词末尾字符
|
||||||
*/
|
*/
|
||||||
private final Set<Character> endCharacterSet = new HashSet<>();
|
private final Set<Character> endCharacterSet = new HashSet<>();
|
||||||
/**
|
/**
|
||||||
@ -62,26 +68,30 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
* 增加一组单词
|
* 增加一组单词
|
||||||
*
|
*
|
||||||
* @param words 单词集合
|
* @param words 单词集合
|
||||||
|
* @return this
|
||||||
*/
|
*/
|
||||||
public void addWords(Collection<String> words) {
|
public WordTree addWords(Collection<String> words) {
|
||||||
if (false == (words instanceof Set)) {
|
if (false == (words instanceof Set)) {
|
||||||
words = new HashSet<>(words);
|
words = new HashSet<>(words);
|
||||||
}
|
}
|
||||||
for (String word : words) {
|
for (String word : words) {
|
||||||
addWord(word);
|
addWord(word);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加一组单词
|
* 增加一组单词
|
||||||
*
|
*
|
||||||
* @param words 单词数组
|
* @param words 单词数组
|
||||||
|
* @return this
|
||||||
*/
|
*/
|
||||||
public void addWords(String... words) {
|
public WordTree addWords(String... words) {
|
||||||
HashSet<String> wordsSet = CollectionUtil.newHashSet(words);
|
HashSet<String> wordsSet = CollectionUtil.newHashSet(words);
|
||||||
for (String word : wordsSet) {
|
for (String word : wordsSet) {
|
||||||
addWord(word);
|
addWord(word);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +99,7 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
*
|
*
|
||||||
* @param word 单词
|
* @param word 单词
|
||||||
*/
|
*/
|
||||||
public void addWord(String word) {
|
public WordTree addWord(String word) {
|
||||||
final Filter<Character> charFilter = this.charFilter;
|
final Filter<Character> charFilter = this.charFilter;
|
||||||
WordTree parent = null;
|
WordTree parent = null;
|
||||||
WordTree current = this;
|
WordTree current = this;
|
||||||
@ -112,8 +122,8 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
if (null != parent) {
|
if (null != parent) {
|
||||||
parent.setEnd(currentChar);
|
parent.setEnd(currentChar);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------- match
|
//------------------------------------------------------------------------------- match
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,7 +136,7 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
if (null == text) {
|
if (null == text) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return null != match(text);
|
return null != matchWord(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,15 +145,24 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
* @param text 被检查的文本
|
* @param text 被检查的文本
|
||||||
* @return 匹配到的关键字
|
* @return 匹配到的关键字
|
||||||
*/
|
*/
|
||||||
public FoundWord match(String text) {
|
public String match(String text) {
|
||||||
|
final FoundWord foundWord = matchWord(text);
|
||||||
|
return null != foundWord ? foundWord.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得第一个匹配的关键字
|
||||||
|
*
|
||||||
|
* @param text 被检查的文本
|
||||||
|
* @return 匹配到的关键字
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public FoundWord matchWord(String text) {
|
||||||
if (null == text) {
|
if (null == text) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<FoundWord> matchAll = matchAll(text, 1);
|
final List<FoundWord> matchAll = matchAllWords(text, 1);
|
||||||
if (CollectionUtil.isNotEmpty(matchAll)) {
|
return CollUtil.get(matchAll, 0);
|
||||||
return matchAll.get(0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------- match all
|
//------------------------------------------------------------------------------- match all
|
||||||
@ -154,10 +173,21 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
* @param text 被检查的文本
|
* @param text 被检查的文本
|
||||||
* @return 匹配的词列表
|
* @return 匹配的词列表
|
||||||
*/
|
*/
|
||||||
public List<FoundWord> matchAll(String text) {
|
public List<String> matchAll(String text) {
|
||||||
return matchAll(text, -1);
|
return matchAll(text, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有匹配的关键字
|
||||||
|
*
|
||||||
|
* @param text 被检查的文本
|
||||||
|
* @return 匹配的词列表
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public List<FoundWord> matchAllWords(String text) {
|
||||||
|
return matchAllWords(text, -1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 找出所有匹配的关键字
|
* 找出所有匹配的关键字
|
||||||
*
|
*
|
||||||
@ -165,10 +195,22 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
* @param limit 限制匹配个数
|
* @param limit 限制匹配个数
|
||||||
* @return 匹配的词列表
|
* @return 匹配的词列表
|
||||||
*/
|
*/
|
||||||
public List<FoundWord> matchAll(String text, int limit) {
|
public List<String> matchAll(String text, int limit) {
|
||||||
return matchAll(text, limit, false, false);
|
return matchAll(text, limit, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有匹配的关键字
|
||||||
|
*
|
||||||
|
* @param text 被检查的文本
|
||||||
|
* @param limit 限制匹配个数
|
||||||
|
* @return 匹配的词列表
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public List<FoundWord> matchAllWords(String text, int limit) {
|
||||||
|
return matchAllWords(text, limit, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 找出所有匹配的关键字<br>
|
* 找出所有匹配的关键字<br>
|
||||||
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||||
@ -180,7 +222,24 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||||
* @return 匹配的词列表
|
* @return 匹配的词列表
|
||||||
*/
|
*/
|
||||||
public List<FoundWord> matchAll(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
|
public List<String> matchAll(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
|
||||||
|
final List<FoundWord> matchAllWords = matchAllWords(text, limit, isDensityMatch, isGreedMatch);
|
||||||
|
return CollUtil.map(matchAllWords, FoundWord::toString, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有匹配的关键字<br>
|
||||||
|
* 密集匹配原则:假如关键词有 ab,b,文本是abab,将匹配 [ab,b,ab]<br>
|
||||||
|
* 贪婪匹配(最长匹配)原则:假如关键字a,ab,最长匹配将匹配[a, ab]
|
||||||
|
*
|
||||||
|
* @param text 被检查的文本
|
||||||
|
* @param limit 限制匹配个数
|
||||||
|
* @param isDensityMatch 是否使用密集匹配原则
|
||||||
|
* @param isGreedMatch 是否使用贪婪匹配(最长匹配)原则
|
||||||
|
* @return 匹配的词列表
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public List<FoundWord> matchAllWords(String text, int limit, boolean isDensityMatch, boolean isGreedMatch) {
|
||||||
if (null == text) {
|
if (null == text) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -239,8 +298,6 @@ public class WordTree extends HashMap<Character, WordTree> {
|
|||||||
}
|
}
|
||||||
return foundWords;
|
return foundWords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------- Private method start
|
//--------------------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package cn.hutool.dfa.test;
|
package cn.hutool.dfa;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.dfa.FoundWord;
|
|
||||||
import cn.hutool.dfa.WordTree;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DFA单元测试
|
* DFA单元测试
|
||||||
@ -29,8 +26,8 @@ public class DfaTest {
|
|||||||
// 情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词
|
// 情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词
|
||||||
// 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配
|
// 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配
|
||||||
// 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短)
|
// 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短)
|
||||||
List<FoundWord> matchAll = tree.matchAll(text, -1, false, false);
|
List<String> matchAll = tree.matchAll(text, -1, false, false);
|
||||||
Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "土^豆", "刚出锅"));
|
Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "土^豆", "刚出锅"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,8 +42,8 @@ public class DfaTest {
|
|||||||
// 情况二:匹配到最短关键词,不跳过已经匹配的关键词
|
// 情况二:匹配到最短关键词,不跳过已经匹配的关键词
|
||||||
// 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】
|
// 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】
|
||||||
// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
|
// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
|
||||||
List<FoundWord> matchAll = tree.matchAll(text, -1, true, false);
|
List<String> matchAll = tree.matchAll(text, -1, true, false);
|
||||||
Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "土^豆", "刚出锅", "出锅"));
|
Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "土^豆", "刚出锅", "出锅"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,8 +58,8 @@ public class DfaTest {
|
|||||||
// 情况三:匹配到最长关键词,跳过已经匹配的关键词
|
// 情况三:匹配到最长关键词,跳过已经匹配的关键词
|
||||||
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配
|
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配
|
||||||
// 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过
|
// 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过
|
||||||
List<FoundWord> matchAll = tree.matchAll(text, -1, false, true);
|
List<String> matchAll = tree.matchAll(text, -1, false, true);
|
||||||
Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "大土^豆", "刚出锅"));
|
Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "大土^豆", "刚出锅"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,8 +75,8 @@ public class DfaTest {
|
|||||||
// 情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词)
|
// 情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词)
|
||||||
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配
|
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配
|
||||||
// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
|
// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
|
||||||
List<FoundWord> matchAll = tree.matchAll(text, -1, true, true);
|
List<String> matchAll = tree.matchAll(text, -1, true, true);
|
||||||
Assert.assertEquals(matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("大", "大土^豆", "土^豆", "刚出锅", "出锅"));
|
Assert.assertEquals(matchAll, CollUtil.newArrayList("大", "大土^豆", "土^豆", "刚出锅", "出锅"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +88,8 @@ public class DfaTest {
|
|||||||
WordTree tree = new WordTree();
|
WordTree tree = new WordTree();
|
||||||
tree.addWord("tio");
|
tree.addWord("tio");
|
||||||
|
|
||||||
List<FoundWord> all = tree.matchAll("AAAAAAAt-ioBBBBBBB");
|
List<String> all = tree.matchAll("AAAAAAAt-ioBBBBBBB");
|
||||||
Assert.assertEquals(all.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()), CollectionUtil.newArrayList("t-io"));
|
Assert.assertEquals(all, CollUtil.newArrayList("t-io"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -100,12 +97,11 @@ public class DfaTest {
|
|||||||
WordTree tree = new WordTree();
|
WordTree tree = new WordTree();
|
||||||
tree.addWord("women");
|
tree.addWord("women");
|
||||||
String text = "a WOMEN todo.".toLowerCase();
|
String text = "a WOMEN todo.".toLowerCase();
|
||||||
List<FoundWord> matchAll = tree.matchAll(text, -1, false, false);
|
List<String> matchAll = tree.matchAll(text, -1, false, false);
|
||||||
Assert.assertEquals("[women]", matchAll.stream().map(fw -> fw.getFoundWord()).collect(Collectors.toList()).toString());
|
Assert.assertEquals("[women]", matchAll.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建查找树
|
* 构建查找树
|
||||||
*
|
*
|
@ -1,6 +1,5 @@
|
|||||||
package cn.hutool.dfa.test;
|
package cn.hutool.dfa;
|
||||||
|
|
||||||
import cn.hutool.dfa.SensitiveUtil;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
@ -694,12 +694,12 @@ public final class JSONUtil {
|
|||||||
* 在需要的时候包装对象<br>
|
* 在需要的时候包装对象<br>
|
||||||
* 包装包括:
|
* 包装包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><code>null</code> =》 <code>JSONNull.NULL</code></li>
|
* <li>{@code null} =》 {@code JSONNull.NULL}</li>
|
||||||
* <li>array or collection =》 JSONArray</li>
|
* <li>array or collection =》 JSONArray</li>
|
||||||
* <li>map =》 JSONObject</li>
|
* <li>map =》 JSONObject</li>
|
||||||
* <li>standard property (Double, String, et al) =》 原对象</li>
|
* <li>standard property (Double, String, et al) =》 原对象</li>
|
||||||
* <li>来自于java包 =》 字符串</li>
|
* <li>来自于java包 =》 字符串</li>
|
||||||
* <li>其它 =》 尝试包装为JSONObject,否则返回<code>null</code></li>
|
* <li>其它 =》 尝试包装为JSONObject,否则返回{@code null}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param object 被包装的对象
|
* @param object 被包装的对象
|
||||||
|
Loading…
x
Reference in New Issue
Block a user