prune
策略0
表示无大小限制
+ * 返回缓存容量,{@code 0}表示无大小限制
*/
protected int capacity;
/**
- * 缓存失效时长, 0
表示无限制,单位毫秒
+ * 缓存失效时长, {@code 0} 表示无限制,单位毫秒
*/
protected long timeout;
@@ -168,15 +168,13 @@ public abstract class AbstractCachenull
或者含有非null
属性的对象
+ * 判断Bean是否为非空对象,非空对象表示本身不为{@code null}或者含有非{@code null}属性的对象
*
* @param bean Bean对象
* @param ignoreFiledNames 忽略检查的字段名
- * @return 是否为空,true
- 空 / false
- 非空
+ * @return 是否为空,{@code true} - 空 / {@code false} - 非空
* @since 5.0.7
*/
public static boolean isNotEmpty(Object bean, String... ignoreFiledNames) {
@@ -757,12 +757,12 @@ public class BeanUtil {
}
/**
- * 判断Bean是否为空对象,空对象表示本身为null
或者所有属性都为null
true
- 空 / false
- 非空
+ * @return 是否为空,{@code true} - 空 / {@code false} - 非空
* @since 4.1.10
*/
public static boolean isEmpty(Object bean, String... ignoreFiledNames) {
@@ -781,12 +781,12 @@ public class BeanUtil {
}
/**
- * 判断Bean是否包含值为null
的属性null
也返回true
+ * 判断Bean是否包含值为{@code null}的属性null
的属性,true
- 包含 / false
- 不包含
+ * @return 是否包含值为null
的属性,{@code true} - 包含 / {@code false} - 不包含
* @since 4.1.10
*/
public static boolean hasNullField(Object bean, String... ignoreFiledNames) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
index b56c239bd..7f629bc0f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
@@ -1870,7 +1870,7 @@ public class CollUtil {
/**
* Iterator转换为Enumeration
*
- * Adapt the specified
- * Adapt the specified
* 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*
- * @return {@link URLEncoder}
+ * @return URLEncoder
*/
public static URLEncoder createQuery() {
final URLEncoder encoder = new URLEncoder();
@@ -133,7 +133,7 @@ public class URLEncoder implements Serializable {
}
/**
- * 创建{@link URLEncoder}
* 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*
- * @return {@link URLEncoder}
+ * @return URLEncoder
*/
public static URLEncoder createAll() {
final URLEncoder encoder = new URLEncoder();
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java
index b6162dc98..76a12178f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java
@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.multi.ListValueMap;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
@@ -78,10 +77,7 @@ public class MultipartFormData {
putFile(header.formFieldName, newFile);
} else {
// 标准表单项
- ByteArrayOutputStream fbos = new ByteArrayOutputStream(1024);
- input.copy(fbos);
- String value = (charset != null) ? new String(fbos.toByteArray(), charset) : new String(fbos.toByteArray());
- putParameter(header.formFieldName, value);
+ putParameter(header.formFieldName, input.readString(charset));
}
input.skipBytes(1);
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartRequestInputStream.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartRequestInputStream.java
index 8dc5df6b0..12acf6b6e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartRequestInputStream.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartRequestInputStream.java
@@ -1,5 +1,7 @@
package cn.hutool.core.net.multipart;
+import cn.hutool.core.io.FastByteArrayOutputStream;
+
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -144,7 +146,20 @@ public class MultipartRequestInputStream extends BufferedInputStream {
// ---------------------------------------------------------------- copy
/**
- * 全部字节流复制到out
+ * 读取字节流,直到下一个boundary
+ *
+ * @param charset 编码,null表示系统默认编码
+ * @return 读取的字符串
+ * @throws IOException 读取异常
+ */
+ public String readString(Charset charset) throws IOException {
+ final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
+ copy(out);
+ return out.toString(charset);
+ }
+
+ /**
+ * 字节流复制到out,直到下一个boundary
*
* @param out 输出流
* @return 复制的字节数
@@ -171,7 +186,7 @@ public class MultipartRequestInputStream extends BufferedInputStream {
* @return 复制的字节数
* @throws IOException 读取异常
*/
- public int copy(OutputStream out, int limit) throws IOException {
+ public int copy(OutputStream out, long limit) throws IOException {
int count = 0;
while (true) {
byte b = readByte();
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFile.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFile.java
index 4cc456934..3734ca11b 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFile.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFile.java
@@ -7,7 +7,6 @@ import cn.hutool.core.util.StrUtil;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -24,7 +23,7 @@ public class UploadFile {
private final UploadFileHeader header;
private final UploadSetting setting;
- private int size = -1;
+ private long size = -1;
// 文件流(小文件位于内存中)
private byte[] data;
@@ -150,7 +149,7 @@ public class UploadFile {
/**
* @return 上传文件的大小,> 0 表示未上传
*/
- public int size() {
+ public long size() {
return size;
}
@@ -200,13 +199,13 @@ public class UploadFile {
// 处理硬盘文件
tempFile = FileUtil.createTempFile(TMP_FILE_PREFIX, TMP_FILE_SUFFIX, FileUtil.touch(setting.tmpUploadPath), false);
- BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile));
+ final BufferedOutputStream out = FileUtil.getOutputStream(this.tempFile);
if (data != null) {
size = data.length;
out.write(data);
data = null; // not needed anymore
}
- int maxFileSize = setting.maxFileSize;
+ final long maxFileSize = setting.maxFileSize;
try {
if (maxFileSize == -1) {
size += input.copy(out);
@@ -236,14 +235,14 @@ public class UploadFile {
* @return 是否为允许的扩展名
*/
private boolean isAllowedExtension() {
- String[] exts = setting.fileExts;
+ final String[] exts = setting.fileExts;
boolean isAllow = setting.isAllowFileExts;
if (exts == null || exts.length == 0) {
// 如果给定扩展名列表为空,当允许扩展名时全部允许,否则全部禁止
return isAllow;
}
- String fileNameExt = FileUtil.extName(this.getFileName());
+ final String fileNameExt = FileUtil.extName(this.getFileName());
for (String fileExtension : setting.fileExts) {
if (fileNameExt.equalsIgnoreCase(fileExtension)) {
return isAllow;
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFileHeader.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFileHeader.java
index 5c1770d9b..8f769dd97 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFileHeader.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadFileHeader.java
@@ -31,7 +31,8 @@ public class UploadFileHeader {
// ---------------------------------------------------------------- public interface
/**
- * Returns 字符串是否为空白,空白的定义如下: 例: 注意:该方法与 {@link #isEmpty(CharSequence)} 的区别是:
+ * 该方法会校验空白字符,且性能相对于 {@link #isEmpty(CharSequence)} 略慢。 建议: 字符串是否为非空白,非空白的定义如下: 例: 注意:该方法与 {@link #isNotEmpty(CharSequence)} 的区别是:
+ * 该方法会校验空白字符,且性能相对于 {@link #isNotEmpty(CharSequence)} 略慢。 建议:仅对于客户端(或第三方接口)传入的参数使用该方法。 指定字符串数组中,是否包含空字符串。 如果指定的字符串数组的长度为 0,或者其中的任意一个元素是空字符串,则返回 true。 例: 注意:该方法与 {@link #isAllBlank(CharSequence...)} 的区别在于: 指定字符串数组中的元素,是否全部为空字符串。 如果指定的字符串数组的长度为 0,或者所有元素都是空字符串,则返回 true。 例: 注意:该方法与 {@link #hasBlank(CharSequence...)} 的区别在于: 字符串是否为空,空的定义如下: 例: 注意:该方法与 {@link #isBlank(CharSequence)} 的区别是:该方法不校验空白字符。 建议: 字符串是否为非空白,非空白的定义如下: 例: 注意:该方法与 {@link #isNotBlank(CharSequence)} 的区别是:该方法不校验空白字符。 建议:该方法建议用于工具类或任何可以预期的方法参数的校验中。 是否包含空字符串。 如果指定的字符串数组的长度为 0,或者其中的任意一个元素是空字符串,则返回 true。 例: 注意:该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于: 指定字符串数组中的元素,是否全部为空字符串。 如果指定的字符串数组的长度为 0,或者所有元素都是空字符串,则返回 true。 例: 注意:该方法与 {@link #hasEmpty(CharSequence...)} 的区别在于: 指定字符串数组中的元素,是否都不为空字符串。 如果指定的字符串数组的长度不为 0,或者所有元素都不是空字符串,则返回 true。 例: 注意:该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于:
+ * 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
+ *
+ *
+ * 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
+ *
+ *
+ * 注意,和{@link String#trim()}不同,此方法使用{@link CharUtil#isBlankChar(char)} 来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
+ *
+ *
+ * 如果 str=null 或 searchStr=null 或 ordinal≥0 则返回-1
+ * 例子(*代表任意字符):
+ *
+ *
+ * 栗子:
+ *
+ *
+ * 栗子:
+ *
+ *
+ * 栗子:
+ *
+ *
+ * 栗子:
+ *
+ *
+ * 当{@link #setFirstPageNo(int)}设置为1时:
+ *
- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回
- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回
- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回
- * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回
* 此方法只检查方法名是否一致,并不检查参数的一致性。
@@ -544,7 +544,7 @@ public class ReflectUtil {
}
/**
- * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回
* 此方法只检查方法名是否一致(忽略大小写),并不检查参数的一致性。
@@ -561,7 +561,7 @@ public class ReflectUtil {
}
/**
- * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回
* 此方法只检查方法名是否一致,并不检查参数的一致性。
@@ -842,7 +842,7 @@ public class ReflectUtil {
*
*
* @param 字符串是否为空白,空白的定义如下: 例: 注意:该方法与 {@link #isEmpty(CharSequence)} 的区别是:
- * 该方法会校验空白字符,且性能相对于 {@link #isEmpty(CharSequence)} 略慢。 建议: 如果对象是字符串是否为空白,空白的定义如下: 字符串是否为非空白,非空白的定义如下: 例: 注意:该方法与 {@link #isNotEmpty(CharSequence)} 的区别是:
- * 该方法会校验空白字符,且性能相对于 {@link #isNotEmpty(CharSequence)} 略慢。 建议:仅对于客户端(或第三方接口)传入的参数使用该方法。 指定字符串数组中,是否包含空字符串。 如果指定的字符串数组的长度为 0,或者其中的任意一个元素是空字符串,则返回 true。 例: 注意:该方法与 {@link #isAllBlank(CharSequence...)} 的区别在于: 指定字符串数组中的元素,是否全部为空字符串。 如果指定的字符串数组的长度为 0,或者所有元素都是空字符串,则返回 true。 例: 注意:该方法与 {@link #hasBlank(CharSequence...)} 的区别在于: 字符串是否为空,空的定义如下: 例: 注意:该方法与 {@link #isBlank(CharSequence)} 的区别是:该方法不校验空白字符。 建议: 如果对象是字符串是否为空串,空的定义如下: 字符串是否为非空白,非空白的定义如下: 例: 注意:该方法与 {@link #isNotBlank(CharSequence)} 的区别是:该方法不校验空白字符。 建议:该方法建议用于工具类或任何可以预期的方法参数的校验中。 是否包含空字符串。 如果指定的字符串数组的长度为 0,或者其中的任意一个元素是空字符串,则返回 true。 例: 注意:该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于: 指定字符串数组中的元素,是否全部为空字符串。 如果指定的字符串数组的长度为 0,或者所有元素都是空字符串,则返回 true。 例: 注意:该方法与 {@link #hasEmpty(CharSequence...)} 的区别在于: 指定字符串数组中的元素,是否都不为空字符串。 如果指定的字符串数组的长度不为 0,或者所有元素都不是空字符串,则返回 true。 例: 注意:该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于:
- * 注意,和
- * 注意,和
- * 注意,和
- * 栗子:
- *
- *
- * 栗子:
- *
- *
- * 栗子:
- *
- *
- * 栗子:
- *
- *
- * 如果 str=null 或 searchStr=null 或 ordinal≥0 则返回-1
- * 例子(*代表任意字符):
- *
- * Iterator
to the Enumeration
interface.
+ * Adapt the specified {@link Iterator} to the {@link Enumeration} interface.
*
* @param Enumeration
to the Iterator
interface
+ * Adapt the specified {@code Enumeration} to the {@code Iterator} interface
*
* @param
- * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 如果给定的值为{@code null},或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -85,7 +85,7 @@ public class Convert {
/**
* 转换为字符
- * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 如果给定的值为{@code null},或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -108,7 +108,7 @@ public class Convert {
/**
* 转换为byte
- * 如果给定的值为null
,或者转换失败,返回默认值
+ * 如果给定的值为{@code null},或者转换失败,返回默认值
* 转换失败不会报错
*
* @param value 被转换的值
@@ -121,7 +121,7 @@ public class Convert {
/**
* 转换为byte
- * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 如果给定的值为{@code null},或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -155,7 +155,7 @@ public class Convert {
/**
* 转换为Short
- * 如果给定的值为null
,或者转换失败,返回默认值
+ * 如果给定的值为{@code null},或者转换失败,返回默认值
* 转换失败不会报错
*
* @param value 被转换的值
@@ -168,7 +168,7 @@ public class Convert {
/**
* 转换为Short
- * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 如果给定的值为{@code null},或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -204,7 +204,7 @@ public class Convert {
/**
* 转换为Number
- * 如果给定的值为空,或者转换失败,返回默认值null
+ * 如果给定的值为空,或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -240,7 +240,7 @@ public class Convert {
/**
* 转换为int
- * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 如果给定的值为{@code null},或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -275,7 +275,7 @@ public class Convert {
/**
* 转换为long
- * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 如果给定的值为{@code null},或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -310,7 +310,7 @@ public class Convert {
/**
* 转换为double
- * 如果给定的值为空,或者转换失败,返回默认值null
+ * 如果给定的值为空,或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -345,7 +345,7 @@ public class Convert {
/**
* 转换为Float
- * 如果给定的值为空,或者转换失败,返回默认值null
+ * 如果给定的值为空,或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -380,7 +380,7 @@ public class Convert {
/**
* 转换为boolean
- * 如果给定的值为空,或者转换失败,返回默认值null
+ * 如果给定的值为空,或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -415,7 +415,7 @@ public class Convert {
/**
* 转换为BigInteger
- * 如果给定的值为空,或者转换失败,返回默认值null
+ * 如果给定的值为空,或者转换失败,返回默认值{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -480,7 +480,7 @@ public class Convert {
/**
* 转换为LocalDateTime
- * 如果给定的值为空,或者转换失败,返回null
+ * 如果给定的值为空,或者转换失败,返回{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -506,7 +506,7 @@ public class Convert {
/**
* 转换为Date
- * 如果给定的值为空,或者转换失败,返回null
+ * 如果给定的值为空,或者转换失败,返回{@code null}
* 转换失败不会报错
*
* @param value 被转换的值
@@ -534,7 +534,7 @@ public class Convert {
/**
* 转换为Enum对象
- * 如果给定的值为空,或者转换失败,返回默认值null
+ * 如果给定的值为空,或者转换失败,返回默认值{@code null}
*
* @param
- * 在非重置情况下,如果起始日期的天小于结束日期的天,月数要少算1(不足1个月)
+ * 在非重置情况下,如果起始日期的天大于结束日期的天,月数要少算1(不足1个月)
*
* @param isReset 是否重置时间为起始时间(重置天时分秒)
* @return 相差月数
@@ -122,7 +122,7 @@ public class DateBetween implements Serializable{
/**
* 计算两个日期相差年数
- * 在非重置情况下,如果起始日期的月小于结束日期的月,年数要少算1(不足1年)
+ * 在非重置情况下,如果起始日期的月大于结束日期的月,年数要少算1(不足1年)
*
* @param isReset 是否重置时间为起始时间(重置月天时分秒)
* @return 相差年数
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java
index 365b42b0d..c08f46b7d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java
@@ -1339,8 +1339,8 @@ public class DateUtil extends CalendarUtil {
*
* 有时候我们计算相差天数的时候需要忽略时分秒。
* 比如:2016-02-01 23:59:59和2016-02-02 00:00:00相差一秒
- * 如果isReset为
*
* @param beginDate 起始日期
@@ -1375,7 +1375,7 @@ public class DateUtil extends CalendarUtil {
/**
* 计算两个日期相差月数false
相差天数为0。
- * 如果isReset为true
相差天数将被计算为1
+ * 如果isReset为{@code false}相差天数为0。
+ * 如果isReset为{@code true}相差天数将被计算为1
*
- * 在非重置情况下,如果起始日期的天小于结束日期的天,月数要少算1(不足1个月)
+ * 在非重置情况下,如果起始日期的天大于结束日期的天,月数要少算1(不足1个月)
*
* @param beginDate 起始日期
* @param endDate 结束日期
@@ -1389,7 +1389,7 @@ public class DateUtil extends CalendarUtil {
/**
* 计算两个日期相差年数
- * 在非重置情况下,如果起始日期的月小于结束日期的月,年数要少算1(不足1年)
+ * 在非重置情况下,如果起始日期的月大于结束日期的月,年数要少算1(不足1年)
*
* @param beginDate 起始日期
* @param endDate 结束日期
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java
index 3ecb8a5af..9d838f861 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java
@@ -13,6 +13,7 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
+import java.time.temporal.UnsupportedTemporalTypeException;
/**
* {@link TemporalAccessor} 工具类封装
@@ -54,7 +55,19 @@ public class TemporalAccessorUtil extends TemporalUtil{
formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
}
- return formatter.format(time);
+
+ try {
+ return formatter.format(time);
+ } catch (UnsupportedTemporalTypeException e){
+ if(time instanceof LocalDate && e.getMessage().contains("HourOfDay")){
+ // 用户传入LocalDate,但是要求格式化带有时间部分,转换为LocalDateTime重试
+ return formatter.format(((LocalDate) time).atStartOfDay());
+ }else if(time instanceof LocalTime && e.getMessage().contains("YearOfEra")){
+ // 用户传入LocalTime,但是要求格式化带有日期部分,转换为LocalDateTime重试
+ return formatter.format(((LocalTime) time).atDate(LocalDate.now()));
+ }
+ throw e;
+ }
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FastByteArrayOutputStream.java b/hutool-core/src/main/java/cn/hutool/core/io/FastByteArrayOutputStream.java
index a27cace9d..088db9ee0 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/FastByteArrayOutputStream.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/FastByteArrayOutputStream.java
@@ -1,6 +1,7 @@
package cn.hutool.core.io;
import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ObjectUtil;
import java.io.IOException;
import java.io.OutputStream;
@@ -90,7 +91,7 @@ public class FastByteArrayOutputStream extends OutputStream {
@Override
public String toString() {
- return new String(toByteArray());
+ return toString(CharsetUtil.defaultCharset());
}
/**
@@ -104,11 +105,12 @@ public class FastByteArrayOutputStream extends OutputStream {
/**
* 转为字符串
- * @param charset 编码
+ * @param charset 编码,null表示默认编码
* @return 字符串
*/
public String toString(Charset charset) {
- return new String(toByteArray(), charset);
+ return new String(toByteArray(),
+ ObjectUtil.defaultIfNull(charset, CharsetUtil.defaultCharset()));
}
}
\ No newline at end of file
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
index 039845a71..2eccd286e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
@@ -1,7 +1,6 @@
package cn.hutool.core.io;
import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.file.FileCopier;
import cn.hutool.core.io.file.FileMode;
import cn.hutool.core.io.file.FileNameUtil;
@@ -173,13 +172,7 @@ public class FileUtil extends PathUtil {
* @return 文件列表
*/
public static List
- * 本方法不会关闭流
- *
- * @param in 输入流
- * @param out 输出流
- * @param bufferSize 缓存大小
- * @param streamProgress 进度条
- * @return 传输的byte数
- * @throws IORuntimeException IO异常
- */
- public static long copyByNIO(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
- return copy(Channels.newChannel(in), Channels.newChannel(out), bufferSize, streamProgress);
- }
-
/**
* 拷贝文件流,使用NIO
*
@@ -227,79 +189,13 @@ public class IoUtil {
try {
inChannel = in.getChannel();
outChannel = out.getChannel();
- return inChannel.transferTo(0, inChannel.size(), outChannel);
- } catch (IOException e) {
- throw new IORuntimeException(e);
+ return copy(inChannel, outChannel);
} finally {
close(outChannel);
close(inChannel);
}
}
- /**
- * 拷贝流,使用NIO,不会关闭流
- *
- * @param in {@link ReadableByteChannel}
- * @param out {@link WritableByteChannel}
- * @return 拷贝的字节数
- * @throws IORuntimeException IO异常
- * @since 4.5.0
- */
- public static long copy(ReadableByteChannel in, WritableByteChannel out) throws IORuntimeException {
- return copy(in, out, DEFAULT_BUFFER_SIZE);
- }
-
- /**
- * 拷贝流,使用NIO,不会关闭流
- *
- * @param in {@link ReadableByteChannel}
- * @param out {@link WritableByteChannel}
- * @param bufferSize 缓冲大小,如果小于等于0,使用默认
- * @return 拷贝的字节数
- * @throws IORuntimeException IO异常
- * @since 4.5.0
- */
- public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize) throws IORuntimeException {
- return copy(in, out, bufferSize, null);
- }
-
- /**
- * 拷贝流,使用NIO,不会关闭流
- *
- * @param in {@link ReadableByteChannel}
- * @param out {@link WritableByteChannel}
- * @param bufferSize 缓冲大小,如果小于等于0,使用默认
- * @param streamProgress {@link StreamProgress}进度处理器
- * @return 拷贝的字节数
- * @throws IORuntimeException IO异常
- */
- public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
- Assert.notNull(in, "InputStream is null !");
- Assert.notNull(out, "OutputStream is null !");
-
- ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize <= 0 ? DEFAULT_BUFFER_SIZE : bufferSize);
- long size = 0;
- if (null != streamProgress) {
- streamProgress.start();
- }
- try {
- while (in.read(byteBuffer) != EOF) {
- byteBuffer.flip();// 写转读
- size += out.write(byteBuffer);
- byteBuffer.clear();
- if (null != streamProgress) {
- streamProgress.progress(size);
- }
- }
- } catch (IOException e) {
- throw new IORuntimeException(e);
- }
- if (null != streamProgress) {
- streamProgress.finish();
- }
-
- return size;
- }
// -------------------------------------------------------------------------------------- Copy end
// -------------------------------------------------------------------------------------- getReader and getWriter start
@@ -455,22 +351,7 @@ public class IoUtil {
* @throws IORuntimeException IO异常
*/
public static String read(InputStream in, Charset charset) throws IORuntimeException {
- FastByteArrayOutputStream out = read(in);
- return null == charset ? out.toString() : out.toString(charset);
- }
-
- /**
- * 从流中读取内容,读取完毕后并不关闭流
- *
- * @param channel 可读通道,读取完毕后并不关闭通道
- * @param charset 字符集
- * @return 内容
- * @throws IORuntimeException IO异常
- * @since 4.5.0
- */
- public static String read(ReadableByteChannel channel, Charset charset) throws IORuntimeException {
- FastByteArrayOutputStream out = read(channel);
- return null == charset ? out.toString() : out.toString(charset);
+ return StrUtil.str(readBytes(in), charset);
}
/**
@@ -486,19 +367,6 @@ public class IoUtil {
return out;
}
- /**
- * 从流中读取内容,读到输出流中
- *
- * @param channel 可读通道,读取完毕后并不关闭通道
- * @return 输出流
- * @throws IORuntimeException IO异常
- */
- public static FastByteArrayOutputStream read(ReadableByteChannel channel) throws IORuntimeException {
- final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
- copy(channel, Channels.newChannel(out));
- return out;
- }
-
/**
* 从Reader中读取String,读取完毕后关闭Reader
*
@@ -513,7 +381,7 @@ public class IoUtil {
/**
* 从{@link Reader}中读取String
*
- * @param reader {@link Reader}
+ * @param reader {@link Reader}
* @param isClose 是否关闭{@link Reader}
* @return String
* @throws IORuntimeException IO异常
@@ -527,55 +395,14 @@ public class IoUtil {
}
} catch (IOException e) {
throw new IORuntimeException(e);
- } finally{
- if(isClose){
+ } finally {
+ if (isClose) {
IoUtil.close(reader);
}
}
return builder.toString();
}
- /**
- * 从FileChannel中读取UTF-8编码内容
- *
- * @param fileChannel 文件管道
- * @return 内容
- * @throws IORuntimeException IO异常
- */
- public static String readUtf8(FileChannel fileChannel) throws IORuntimeException {
- return read(fileChannel, CharsetUtil.CHARSET_UTF_8);
- }
-
- /**
- * 从FileChannel中读取内容,读取完毕后并不关闭Channel
- *
- * @param fileChannel 文件管道
- * @param charsetName 字符集
- * @return 内容
- * @throws IORuntimeException IO异常
- */
- public static String read(FileChannel fileChannel, String charsetName) throws IORuntimeException {
- return read(fileChannel, CharsetUtil.charset(charsetName));
- }
-
- /**
- * 从FileChannel中读取内容
- *
- * @param fileChannel 文件管道
- * @param charset 字符集
- * @return 内容
- * @throws IORuntimeException IO异常
- */
- public static String read(FileChannel fileChannel, Charset charset) throws IORuntimeException {
- MappedByteBuffer buffer;
- try {
- buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).load();
- } catch (IOException e) {
- throw new IORuntimeException(e);
- }
- return StrUtil.str(buffer, charset);
- }
-
/**
* 从流中读取bytes,读取完毕后关闭流
*
@@ -597,12 +424,19 @@ public class IoUtil {
* @since 5.0.4
*/
public static byte[] readBytes(InputStream in, boolean isCloseStream) throws IORuntimeException {
- final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
- copy(in, out);
- if (isCloseStream) {
- close(in);
+ final InputStream availableStream = toAvailableStream(in);
+ try{
+ final int available = availableStream.available();
+ if(available > 0){
+ byte[] result = new byte[available];
+ //noinspection ResultOfMethodCallIgnored
+ availableStream.read(result);
+ return result;
+ }
+ } catch (IOException e){
+ throw new IORuntimeException(e);
}
- return out.toByteArray();
+ return new byte[0];
}
/**
@@ -966,6 +800,42 @@ public class IoUtil {
return (in instanceof PushbackInputStream) ? (PushbackInputStream) in : new PushbackInputStream(in, pushBackSize);
}
+ /**
+ * 将指定{@link InputStream} 转换为{@link InputStream#available()}方法可用的流。
+ * 在Socket通信流中,服务端未返回数据情况下{@link InputStream#available()}方法始终为{@code 0}
+ * 因此,在读取前需要调用{@link InputStream#read()}读取一个字节(未返回会阻塞),一旦读取到了,{@link InputStream#available()}方法就正常了。
+ * 此方法返回对象的规则为:
+ *
+ *
+ *
+ *
+ * @param in 被转换的流
+ * @return 转换后的流,可能为{@link PushbackInputStream}
+ * @since 5.5.3
+ */
+ public static InputStream toAvailableStream(InputStream in) {
+ if(in instanceof FileInputStream){
+ // FileInputStream本身支持available方法。
+ return in;
+ }
+
+ final PushbackInputStream pushbackInputStream = toPushbackStream(in, 1);
+ try {
+ final int available = pushbackInputStream.available();
+ if (available <= 0) {
+ //此操作会阻塞,直到有数据被读到
+ int b = pushbackInputStream.read();
+ pushbackInputStream.unread(b);
+ }
+ } catch (IOException e) {
+ throw new IORuntimeException(e);
+ }
+
+ return pushbackInputStream;
+ }
+
/**
* 将byte[]写到流中
*
@@ -1113,22 +983,6 @@ public class IoUtil {
}
}
- /**
- * 关闭
- * 关闭失败不会抛出异常
- *
- * @param closeable 被关闭的对象
- */
- public static void close(AutoCloseable closeable) {
- if (null != closeable) {
- try {
- closeable.close();
- } catch (Exception e) {
- // 静默关闭
- }
- }
- }
-
/**
* 尝试关闭指定对象
* 判断对象如果实现了{@link AutoCloseable},则调用之
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java
new file mode 100644
index 000000000..bed52ac53
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/NioUtil.java
@@ -0,0 +1,227 @@
+package cn.hutool.core.io;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+
+/**
+ * NIO相关工具封装,主要针对Channel读写、拷贝等封装
+ *
+ * @author looly
+ * @since 5.5.3
+ */
+public class NioUtil {
+
+ /**
+ * 默认缓存大小 8192
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 2 << 12;
+ /**
+ * 默认中等缓存大小 16384
+ */
+ public static final int DEFAULT_MIDDLE_BUFFER_SIZE = 2 << 13;
+ /**
+ * 默认大缓存大小 32768
+ */
+ public static final int DEFAULT_LARGE_BUFFER_SIZE = 2 << 14;
+
+ /**
+ * 数据流末尾
+ */
+ public static final int EOF = -1;
+
+ /**
+ * 拷贝流 thanks to: https://github.com/venusdrogon/feilong-io/blob/master/src/main/java/com/feilong/io/IOWriteUtil.java
+ * 本方法不会关闭流
+ *
+ * @param in 输入流
+ * @param out 输出流
+ * @param bufferSize 缓存大小
+ * @param streamProgress 进度条
+ * @return 传输的byte数
+ * @throws IORuntimeException IO异常
+ */
+ public static long copyByNIO(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
+ return copy(Channels.newChannel(in), Channels.newChannel(out), bufferSize, streamProgress);
+ }
+
+ /**
+ * 拷贝文件Channel,使用NIO,拷贝后不会关闭channel
+ *
+ * @param inChannel {@link FileChannel}
+ * @param outChannel {@link FileChannel}
+ * @return 拷贝的字节数
+ * @throws IORuntimeException IO异常
+ * @since 5.5.3
+ */
+ public static long copy(FileChannel inChannel, FileChannel outChannel) throws IORuntimeException {
+ Assert.notNull(inChannel, "In channel is null!");
+ Assert.notNull(outChannel, "Out channel is null!");
+
+ try {
+ return inChannel.transferTo(0, inChannel.size(), outChannel);
+ } catch (IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+
+ /**
+ * 拷贝流,使用NIO,不会关闭channel
+ *
+ * @param in {@link ReadableByteChannel}
+ * @param out {@link WritableByteChannel}
+ * @return 拷贝的字节数
+ * @throws IORuntimeException IO异常
+ * @since 4.5.0
+ */
+ public static long copy(ReadableByteChannel in, WritableByteChannel out) throws IORuntimeException {
+ return copy(in, out, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * 拷贝流,使用NIO,不会关闭channel
+ *
+ * @param in {@link ReadableByteChannel}
+ * @param out {@link WritableByteChannel}
+ * @param bufferSize 缓冲大小,如果小于等于0,使用默认
+ * @return 拷贝的字节数
+ * @throws IORuntimeException IO异常
+ * @since 4.5.0
+ */
+ public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize) throws IORuntimeException {
+ return copy(in, out, bufferSize, null);
+ }
+
+ /**
+ * 拷贝流,使用NIO,不会关闭channel
+ *
+ * @param in {@link ReadableByteChannel}
+ * @param out {@link WritableByteChannel}
+ * @param bufferSize 缓冲大小,如果小于等于0,使用默认
+ * @param streamProgress {@link StreamProgress}进度处理器
+ * @return 拷贝的字节数
+ * @throws IORuntimeException IO异常
+ */
+ public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
+ Assert.notNull(in, "InputStream is null !");
+ Assert.notNull(out, "OutputStream is null !");
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize <= 0 ? DEFAULT_BUFFER_SIZE : bufferSize);
+ long size = 0;
+ if (null != streamProgress) {
+ streamProgress.start();
+ }
+ try {
+ while (in.read(byteBuffer) != EOF) {
+ byteBuffer.flip();// 写转读
+ size += out.write(byteBuffer);
+ byteBuffer.clear();
+ if (null != streamProgress) {
+ streamProgress.progress(size);
+ }
+ }
+ } catch (IOException e) {
+ throw new IORuntimeException(e);
+ }
+ if (null != streamProgress) {
+ streamProgress.finish();
+ }
+
+ return size;
+ }
+
+ /**
+ * 从流中读取内容,读取完毕后并不关闭流
+ *
+ * @param channel 可读通道,读取完毕后并不关闭通道
+ * @param charset 字符集
+ * @return 内容
+ * @throws IORuntimeException IO异常
+ * @since 4.5.0
+ */
+ public static String read(ReadableByteChannel channel, Charset charset) throws IORuntimeException {
+ FastByteArrayOutputStream out = read(channel);
+ return null == charset ? out.toString() : out.toString(charset);
+ }
+
+ /**
+ * 从流中读取内容,读到输出流中
+ *
+ * @param channel 可读通道,读取完毕后并不关闭通道
+ * @return 输出流
+ * @throws IORuntimeException IO异常
+ */
+ public static FastByteArrayOutputStream read(ReadableByteChannel channel) throws IORuntimeException {
+ final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
+ copy(channel, Channels.newChannel(out));
+ return out;
+ }
+
+ /**
+ * 从FileChannel中读取UTF-8编码内容
+ *
+ * @param fileChannel 文件管道
+ * @return 内容
+ * @throws IORuntimeException IO异常
+ */
+ public static String readUtf8(FileChannel fileChannel) throws IORuntimeException {
+ return read(fileChannel, CharsetUtil.CHARSET_UTF_8);
+ }
+
+ /**
+ * 从FileChannel中读取内容,读取完毕后并不关闭Channel
+ *
+ * @param fileChannel 文件管道
+ * @param charsetName 字符集
+ * @return 内容
+ * @throws IORuntimeException IO异常
+ */
+ public static String read(FileChannel fileChannel, String charsetName) throws IORuntimeException {
+ return read(fileChannel, CharsetUtil.charset(charsetName));
+ }
+
+ /**
+ * 从FileChannel中读取内容
+ *
+ * @param fileChannel 文件管道
+ * @param charset 字符集
+ * @return 内容
+ * @throws IORuntimeException IO异常
+ */
+ public static String read(FileChannel fileChannel, Charset charset) throws IORuntimeException {
+ MappedByteBuffer buffer;
+ try {
+ buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).load();
+ } catch (IOException e) {
+ throw new IORuntimeException(e);
+ }
+ return StrUtil.str(buffer, charset);
+ }
+
+ /**
+ * 关闭
+ * 关闭失败不会抛出异常
+ *
+ * @param closeable 被关闭的对象
+ */
+ public static void close(AutoCloseable closeable) {
+ if (null != closeable) {
+ try {
+ closeable.close();
+ } catch (Exception e) {
+ // 静默关闭
+ }
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
index 5b7baaa07..e9877d575 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
@@ -94,8 +94,8 @@ public class PathUtil {
/**
* 遍历指定path下的文件并做处理
*
- * @param start 起始路径,必须为目录
- * @param visitor {@link FileVisitor} 接口,用于自定义在访问文件时,访问目录前后等节点做的操作
+ * @param start 起始路径,必须为目录
+ * @param visitor {@link FileVisitor} 接口,用于自定义在访问文件时,访问目录前后等节点做的操作
* @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor)
* @since 5.5.2
*/
@@ -169,7 +169,7 @@ public class PathUtil {
* 通过JDK7+的 {@link Files#copy(Path, Path, CopyOption...)} 方法拷贝文件
*
* @param src 源文件路径,如果为目录只在目标中创建新目录
- * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
+ * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
* @param options {@link StandardCopyOption}
* @return Path
* @throws IORuntimeException IO异常
@@ -191,14 +191,14 @@ public class PathUtil {
* 拷贝文件或目录
*
* @param src 源文件路径,如果为目录只在目标中创建新目录
- * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
+ * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
* @param options {@link StandardCopyOption}
* @return Path
* @throws IORuntimeException IO异常
* @since 5.5.1
*/
public static Path copy(Path src, Path target, CopyOption... options) throws IORuntimeException {
- if(isFile(src, false)){
+ if (isFile(src, false)) {
return copyFile(src, target, options);
}
return copyContent(src, target.resolve(src.getFileName()), options);
@@ -208,7 +208,7 @@ public class PathUtil {
* 拷贝目录下的所有文件或目录到目标目录中
*
* @param src 源文件路径,如果为目录只在目标中创建新目录
- * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
+ * @param target 目标文件或目录,如果为目录使用与源文件相同的文件名
* @param options {@link StandardCopyOption}
* @return Path
* @throws IORuntimeException IO异常
@@ -227,7 +227,7 @@ public class PathUtil {
* 判断是否为目录,如果file为null,则返回false
* 此方法不会追踪到软链对应的真实地址,即软链被当作文件
*
- * @param path {@link Path}
+ * @param path {@link Path}
* @return 如果为目录true
* @since 5.5.1
*/
@@ -487,4 +487,17 @@ public class PathUtil {
public static boolean isSymlink(Path path) {
return Files.isSymbolicLink(path);
}
+
+ /**
+ * 判断文件或目录是否存在
+ *
+ * @param path 文件
+ * @param isFollowLinks 是否跟踪软链(快捷方式)
+ * @return 是否存在
+ * @since 5.5.3
+ */
+ public static boolean exists(Path path, boolean isFollowLinks) {
+ final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
+ return Files.exists(path, options);
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java b/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java
new file mode 100644
index 000000000..9337ec9cd
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java
@@ -0,0 +1,34 @@
+package cn.hutool.core.lang;
+
+/**
+ * 片段默认实现
+ *
+ * @param
* 实现方式为一处然后重新put,当旧的key不存在直接返回
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/PassAuth.java b/hutool-core/src/main/java/cn/hutool/core/net/PassAuth.java
new file mode 100644
index 000000000..d87177355
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/net/PassAuth.java
@@ -0,0 +1,41 @@
+package cn.hutool.core.net;
+
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+
+/**
+ * 账号密码形式的{@link Authenticator} 实现。
+ *
+ * @author looly
+ * @since 5.5.3
+ */
+public class PassAuth extends Authenticator {
+
+ /**
+ * 创建账号密码形式的{@link Authenticator} 实现。
+ *
+ * @param user 用户名
+ * @param pass 密码
+ * @return PassAuth
+ */
+ public static PassAuth of(String user, char[] pass) {
+ return new PassAuth(user, pass);
+ }
+
+ private final PasswordAuthentication auth;
+
+ /**
+ * 构造
+ *
+ * @param user 用户名
+ * @param pass 密码
+ */
+ public PassAuth(String user, char[] pass) {
+ auth = new PasswordAuthentication(user, pass);
+ }
+
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return auth;
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java b/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java
index 2bfe00ee3..1da457b75 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java
@@ -26,7 +26,7 @@ public class URLEncoder implements Serializable {
// --------------------------------------------------------------------------------------------- Static method start
/**
- * 默认{@link URLEncoder}
+ * 默认URLEncoder
* 默认的编码器针对URI路径编码,定义如下:
*
*
@@ -38,7 +38,7 @@ public class URLEncoder implements Serializable {
public static final URLEncoder DEFAULT = createDefault();
/**
- * 用于查询语句的{@link URLEncoder}
+ * 用于查询语句的URLEncoder
* 编码器针对URI路径编码,定义如下:
*
*
@@ -53,7 +53,7 @@ public class URLEncoder implements Serializable {
public static final URLEncoder QUERY = createQuery();
/**
- * 全编码的{@link URLEncoder}
+ * 全编码的URLEncoder
*
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' 不编码
@@ -63,7 +63,7 @@ public class URLEncoder implements Serializable {
public static final URLEncoder ALL = createAll();
/**
- * 创建默认{@link URLEncoder}
+ * 创建默认URLEncoder
* 默认的编码器针对URI路径编码,定义如下:
*
*
@@ -72,7 +72,7 @@ public class URLEncoder implements Serializable {
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
*
*
- * @return {@link URLEncoder}
+ * @return URLEncoder
*/
public static URLEncoder createDefault() {
final URLEncoder encoder = new URLEncoder();
@@ -102,7 +102,7 @@ public class URLEncoder implements Serializable {
}
/**
- * 创建用于查询语句的{@link URLEncoder}
+ * 创建用于查询语句的URLEncoder
* 编码器针对URI路径编码,定义如下:
*
*
@@ -114,7 +114,7 @@ public class URLEncoder implements Serializable {
*
+ * 创建URLEncoder
* 编码器针对URI路径编码,定义如下:
*
*
@@ -144,7 +144,7 @@ public class URLEncoder implements Serializable {
*
true
if uploaded data are correctly marked as a file. This is true if header contains string 'filename'.
+ * Returns {@code true} if uploaded data are correctly marked as a file.
+ * This is true if header contains string 'filename'.
*
* @return 是否为文件
*/
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadSetting.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadSetting.java
index 872bb7010..5af183ad6 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadSetting.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/UploadSetting.java
@@ -9,7 +9,7 @@ package cn.hutool.core.net.multipart;
public class UploadSetting {
/** 最大文件大小,默认无限制 */
- protected int maxFileSize = -1;
+ protected long maxFileSize = -1;
/** 文件保存到内存的边界 */
protected int memoryThreshold = 8192;
/** 临时文件目录 */
@@ -26,7 +26,7 @@ public class UploadSetting {
/**
* @return 获得最大文件大小,-1表示无限制
*/
- public int getMaxFileSize() {
+ public long getMaxFileSize() {
return maxFileSize;
}
@@ -35,7 +35,7 @@ public class UploadSetting {
*
* @param maxFileSize 最大文件大小
*/
- public void setMaxFileSize(int maxFileSize) {
+ public void setMaxFileSize(long maxFileSize) {
this.maxFileSize = maxFileSize;
}
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
new file mode 100644
index 000000000..96ec94cfe
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java
@@ -0,0 +1,4231 @@
+package cn.hutool.core.text;
+
+import cn.hutool.core.comparator.VersionComparator;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.Filter;
+import cn.hutool.core.lang.func.Func1;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.CharUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * {@link CharSequence} 相关工具类封装
+ *
+ * @author looly
+ * @since 5.5.3
+ */
+public class CharSequenceUtil {
+
+ public static final int INDEX_NOT_FOUND = -1;
+
+ /**
+ * 字符串常量:{@code "null"}
+ * 注意:{@code "null" != null}
+ */
+ public static final String NULL = "null";
+
+ /**
+ * 字符串常量:空字符串 {@code ""}
+ */
+ public static final String EMPTY = "";
+
+ /**
+ * 字符串常量:空格符 {@code " "}
+ */
+ public static final String SPACE = " ";
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param str 被检测的字符串
+ * @return 若为空白,则返回 true
+ * @see #isEmpty(CharSequence)
+ */
+ public static boolean isBlank(CharSequence str) {
+ int length;
+
+ if ((str == null) || ((length = str.length()) == 0)) {
+ return true;
+ }
+
+ for (int i = 0; i < length; i++) {
+ // 只要有一个非空字符即为非空字符串
+ if (false == CharUtil.isBlankChar(str.charAt(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param strs 字符串列表
+ * @return 是否包含空字符串
+ */
+ public static boolean hasBlank(CharSequence... strs) {
+ if (ArrayUtil.isEmpty(strs)) {
+ return true;
+ }
+
+ for (CharSequence str : strs) {
+ if (isBlank(str)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param strs 字符串列表
+ * @return 所有字符串是否为空白
+ */
+ public static boolean isAllBlank(CharSequence... strs) {
+ if (ArrayUtil.isEmpty(strs)) {
+ return true;
+ }
+
+ for (CharSequence str : strs) {
+ if (isNotBlank(str)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param str 被检测的字符串
+ * @return 是否为空
+ * @see #isBlank(CharSequence)
+ */
+ public static boolean isEmpty(CharSequence str) {
+ return str == null || str.length() == 0;
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * nullToDefault(null, "default") = "default"
+ * nullToDefault("", "default") = ""
+ * nullToDefault(" ", "default") = " "
+ * nullToDefault("bat", "default") = "bat"
+ *
+ *
+ * @param str 要转换的字符串
+ * @param defaultStr 默认字符串
+ * @return 字符串本身或指定的默认字符串
+ */
+ public static String nullToDefault(CharSequence str, String defaultStr) {
+ return (str == null) ? defaultStr : str.toString();
+ }
+
+ /**
+ * 如果字符串是{@code null}或者"",则返回指定默认字符串,否则返回字符串本身。
+ *
+ *
+ * emptyToDefault(null, "default") = "default"
+ * emptyToDefault("", "default") = "default"
+ * emptyToDefault(" ", "default") = " "
+ * emptyToDefault("bat", "default") = "bat"
+ *
+ *
+ * @param str 要转换的字符串
+ * @param defaultStr 默认字符串
+ * @return 字符串本身或指定的默认字符串
+ * @since 4.1.0
+ */
+ public static String emptyToDefault(CharSequence str, String defaultStr) {
+ return isEmpty(str) ? defaultStr : str.toString();
+ }
+
+ /**
+ * 如果字符串是{@code null}或者""或者空白,则返回指定默认字符串,否则返回字符串本身。
+ *
+ *
+ * emptyToDefault(null, "default") = "default"
+ * emptyToDefault("", "default") = "default"
+ * emptyToDefault(" ", "default") = "default"
+ * emptyToDefault("bat", "default") = "bat"
+ *
+ *
+ * @param str 要转换的字符串
+ * @param defaultStr 默认字符串
+ * @return 字符串本身或指定的默认字符串
+ * @since 4.1.0
+ */
+ public static String blankToDefault(CharSequence str, String defaultStr) {
+ return isBlank(str) ? defaultStr : str.toString();
+ }
+
+ /**
+ * 当给定字符串为空字符串时,转换为{@code null}
+ *
+ * @param str 被转换的字符串
+ * @return 转换后的字符串
+ */
+ public static String emptyToNull(CharSequence str) {
+ return isEmpty(str) ? null : str.toString();
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param strs 字符串列表
+ * @return 是否包含空字符串
+ */
+ public static boolean hasEmpty(CharSequence... strs) {
+ if (ArrayUtil.isEmpty(strs)) {
+ return true;
+ }
+
+ for (CharSequence str : strs) {
+ if (isEmpty(str)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param strs 字符串列表
+ * @return 所有字符串是否为空白
+ */
+ public static boolean isAllEmpty(CharSequence... strs) {
+ if (ArrayUtil.isEmpty(strs)) {
+ return true;
+ }
+
+ for (CharSequence str : strs) {
+ if (isNotEmpty(str)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param args 字符串数组
+ * @return 所有字符串是否都不为为空白
+ * @since 5.3.6
+ */
+ public static boolean isAllNotEmpty(CharSequence... args) {
+ return false == hasEmpty(args);
+ }
+
+ /**
+ * 是否存都不为{@code null}或空对象或空白符的对象,通过{@link #hasBlank(CharSequence...)} 判断元素
+ *
+ * @param args 被检查的对象,一个或者多个
+ * @return 是否都不为空
+ * @since 5.3.6
+ */
+ public static boolean isAllNotBlank(CharSequence... args) {
+ return false == hasBlank(args);
+ }
+
+ /**
+ * 检查字符串是否为null、“null”、“undefined”
+ *
+ * @param str 被检查的字符串
+ * @return 是否为null、“null”、“undefined”
+ * @since 4.0.10
+ */
+ public static boolean isNullOrUndefined(CharSequence str) {
+ if (null == str) {
+ return true;
+ }
+ return isNullOrUndefinedStr(str);
+ }
+
+ /**
+ * 检查字符串是否为null、“”、“null”、“undefined”
+ *
+ * @param str 被检查的字符串
+ * @return 是否为null、“”、“null”、“undefined”
+ * @since 4.0.10
+ */
+ public static boolean isEmptyOrUndefined(CharSequence str) {
+ if (isEmpty(str)) {
+ return true;
+ }
+ return isNullOrUndefinedStr(str);
+ }
+
+ /**
+ * 检查字符串是否为null、空白串、“null”、“undefined”
+ *
+ * @param str 被检查的字符串
+ * @return 是否为null、空白串、“null”、“undefined”
+ * @since 4.0.10
+ */
+ public static boolean isBlankOrUndefined(CharSequence str) {
+ if (isBlank(str)) {
+ return true;
+ }
+ return isNullOrUndefinedStr(str);
+ }
+
+ /**
+ * 是否为“null”、“undefined”,不做空指针检查
+ *
+ * @param str 字符串
+ * @return 是否为“null”、“undefined”
+ */
+ private static boolean isNullOrUndefinedStr(CharSequence str) {
+ String strString = str.toString().trim();
+ return NULL.equals(strString) || "undefined".equals(strString);
+ }
+
+ // ------------------------------------------------------------------------ Trim
+
+ /**
+ * 除去字符串头尾部的空白,如果字符串是{@code null},依然返回{@code null}。
+ *
+ *
+ * trim(null) = null
+ * trim("") = ""
+ * trim(" ") = ""
+ * trim("abc") = "abc"
+ * trim(" abc ") = "abc"
+ *
+ *
+ * @param str 要处理的字符串
+ * @return 除去头尾空白的字符串,如果原字串为{@code null},则返回{@code null}
+ */
+ public static String trim(CharSequence str) {
+ return (null == str) ? null : trim(str, 0);
+ }
+
+ /**
+ * 除去字符串头尾部的空白,如果字符串是{@code null},返回{@code ""}。
+ *
+ *
+ * StrUtil.trimToEmpty(null) = ""
+ * StrUtil.trimToEmpty("") = ""
+ * StrUtil.trimToEmpty(" ") = ""
+ * StrUtil.trimToEmpty("abc") = "abc"
+ * StrUtil.trimToEmpty(" abc ") = "abc"
+ *
+ *
+ * @param str 字符串
+ * @return 去除两边空白符后的字符串, 如果为null返回""
+ * @since 3.1.1
+ */
+ public static String trimToEmpty(CharSequence str) {
+ return str == null ? EMPTY : trim(str);
+ }
+
+ /**
+ * 除去字符串头尾部的空白,如果字符串是{@code null}或者"",返回{@code null}。
+ *
+ *
+ * StrUtil.trimToNull(null) = null
+ * StrUtil.trimToNull("") = null
+ * StrUtil.trimToNull(" ") = null
+ * StrUtil.trimToNull("abc") = "abc"
+ * StrUtil.trimToEmpty(" abc ") = "abc"
+ *
+ *
+ * @param str 字符串
+ * @return 去除两边空白符后的字符串, 如果为空返回null
+ * @since 3.2.1
+ */
+ public static String trimToNull(CharSequence str) {
+ final String trimStr = trim(str);
+ return EMPTY.equals(trimStr) ? null : trimStr;
+ }
+
+ /**
+ * 除去字符串头部的空白,如果字符串是{@code null},则返回{@code null}。
+ *
+ *
+ * trimStart(null) = null
+ * trimStart("") = ""
+ * trimStart("abc") = "abc"
+ * trimStart(" abc") = "abc"
+ * trimStart("abc ") = "abc "
+ * trimStart(" abc ") = "abc "
+ *
+ *
+ * @param str 要处理的字符串
+ * @return 除去空白的字符串,如果原字串为{@code null}或结果字符串为{@code ""},则返回 {@code null}
+ */
+ public static String trimStart(CharSequence str) {
+ return trim(str, -1);
+ }
+
+ /**
+ * 除去字符串尾部的空白,如果字符串是{@code null},则返回{@code null}。
+ *
+ *
+ * trimEnd(null) = null
+ * trimEnd("") = ""
+ * trimEnd("abc") = "abc"
+ * trimEnd(" abc") = " abc"
+ * trimEnd("abc ") = "abc"
+ * trimEnd(" abc ") = " abc"
+ *
+ *
+ * @param str 要处理的字符串
+ * @return 除去空白的字符串,如果原字串为{@code null}或结果字符串为{@code ""},则返回 {@code null}
+ */
+ public static String trimEnd(CharSequence str) {
+ return trim(str, 1);
+ }
+
+ /**
+ * 除去字符串头尾部的空白符,如果字符串是{@code null},依然返回{@code null}。
+ *
+ * @param str 要处理的字符串
+ * @param mode {@code -1}表示trimStart,{@code 0}表示trim全部, {@code 1}表示trimEnd
+ * @return 除去指定字符后的的字符串,如果原字串为{@code null},则返回{@code null}
+ */
+ public static String trim(CharSequence str, int mode) {
+ String result;
+ if (str == null) {
+ result = null;
+ } else {
+ int length = str.length();
+ int start = 0;
+ int end = length;// 扫描字符串头部
+ if (mode <= 0) {
+ while ((start < end) && (CharUtil.isBlankChar(str.charAt(start)))) {
+ start++;
+ }
+ }// 扫描字符串尾部
+ if (mode >= 0) {
+ while ((start < end) && (CharUtil.isBlankChar(str.charAt(end - 1)))) {
+ end--;
+ }
+ }
+ if ((start > 0) || (end < length)) {
+ result = str.toString().substring(start, end);
+ } else {
+ result = str.toString();
+ }
+ }
+
+ return result;
+ }
+
+ // ------------------------------------------------------------------------ startWith
+
+ /**
+ * 字符串是否以给定字符开始
+ *
+ * @param str 字符串
+ * @param c 字符
+ * @return 是否开始
+ */
+ public static boolean startWith(CharSequence str, char c) {
+ if (isEmpty(str)) {
+ return false;
+ }
+ return c == str.charAt(0);
+ }
+
+ /**
+ * 是否以指定字符串开头
+ * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
+ *
+ * @param str 被监测字符串
+ * @param prefix 开头字符串
+ * @param ignoreCase 是否忽略大小写
+ * @return 是否以指定字符串开头
+ * @since 5.4.3
+ */
+ public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
+ return startWith(str, prefix, ignoreCase, false);
+ }
+
+ /**
+ * 是否以指定字符串开头
+ * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
+ *
+ * @param str 被监测字符串
+ * @param prefix 开头字符串
+ * @param ignoreCase 是否忽略大小写
+ * @param ignoreEquals 是否忽略字符串相等的情况
+ * @return 是否以指定字符串开头
+ * @since 5.4.3
+ */
+ public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase, boolean ignoreEquals) {
+ if (null == str || null == prefix) {
+ if (false == ignoreEquals) {
+ return false;
+ }
+ return null == str && null == prefix;
+ }
+
+ boolean isStartWith;
+ if (ignoreCase) {
+ isStartWith = str.toString().toLowerCase().startsWith(prefix.toString().toLowerCase());
+ } else {
+ isStartWith = str.toString().startsWith(prefix.toString());
+ }
+
+ if (isStartWith) {
+ return (false == ignoreEquals) || (false == equals(str, prefix, ignoreCase));
+ }
+ return false;
+ }
+
+ /**
+ * 是否以指定字符串开头
+ *
+ * @param str 被监测字符串
+ * @param prefix 开头字符串
+ * @return 是否以指定字符串开头
+ */
+ public static boolean startWith(CharSequence str, CharSequence prefix) {
+ return startWith(str, prefix, false);
+ }
+
+ /**
+ * 是否以指定字符串开头,忽略相等字符串的情况
+ *
+ * @param str 被监测字符串
+ * @param prefix 开头字符串
+ * @return 是否以指定字符串开头并且两个字符串不相等
+ */
+ public static boolean startWithIgnoreEquals(CharSequence str, CharSequence prefix) {
+ return startWith(str, prefix, false, true);
+ }
+
+ /**
+ * 是否以指定字符串开头,忽略大小写
+ *
+ * @param str 被监测字符串
+ * @param prefix 开头字符串
+ * @return 是否以指定字符串开头
+ */
+ public static boolean startWithIgnoreCase(CharSequence str, CharSequence prefix) {
+ return startWith(str, prefix, true);
+ }
+
+ /**
+ * 给定字符串是否以任何一个字符串开始
+ * 给定字符串和数组为空都返回false
+ *
+ * @param str 给定字符串
+ * @param prefixes 需要检测的开始字符串
+ * @return 给定字符串是否以任何一个字符串开始
+ * @since 3.0.6
+ */
+ public static boolean startWithAny(CharSequence str, CharSequence... prefixes) {
+ if (isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
+ return false;
+ }
+
+ for (CharSequence suffix : prefixes) {
+ if (startWith(str, suffix, false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // ------------------------------------------------------------------------ endWith
+
+ /**
+ * 字符串是否以给定字符结尾
+ *
+ * @param str 字符串
+ * @param c 字符
+ * @return 是否结尾
+ */
+ public static boolean endWith(CharSequence str, char c) {
+ if (isEmpty(str)) {
+ return false;
+ }
+ return c == str.charAt(str.length() - 1);
+ }
+
+ /**
+ * 是否以指定字符串结尾
+ * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
+ *
+ * @param str 被监测字符串
+ * @param suffix 结尾字符串
+ * @param isIgnoreCase 是否忽略大小写
+ * @return 是否以指定字符串结尾
+ */
+ public static boolean endWith(CharSequence str, CharSequence suffix, boolean isIgnoreCase) {
+ if (null == str || null == suffix) {
+ return null == str && null == suffix;
+ }
+
+ if (isIgnoreCase) {
+ return str.toString().toLowerCase().endsWith(suffix.toString().toLowerCase());
+ } else {
+ return str.toString().endsWith(suffix.toString());
+ }
+ }
+
+ /**
+ * 是否以指定字符串结尾
+ *
+ * @param str 被监测字符串
+ * @param suffix 结尾字符串
+ * @return 是否以指定字符串结尾
+ */
+ public static boolean endWith(CharSequence str, CharSequence suffix) {
+ return endWith(str, suffix, false);
+ }
+
+ /**
+ * 是否以指定字符串结尾,忽略大小写
+ *
+ * @param str 被监测字符串
+ * @param suffix 结尾字符串
+ * @return 是否以指定字符串结尾
+ */
+ public static boolean endWithIgnoreCase(CharSequence str, CharSequence suffix) {
+ return endWith(str, suffix, true);
+ }
+
+ /**
+ * 给定字符串是否以任何一个字符串结尾
+ * 给定字符串和数组为空都返回false
+ *
+ * @param str 给定字符串
+ * @param suffixes 需要检测的结尾字符串
+ * @return 给定字符串是否以任何一个字符串结尾
+ * @since 3.0.6
+ */
+ public static boolean endWithAny(CharSequence str, CharSequence... suffixes) {
+ if (isEmpty(str) || ArrayUtil.isEmpty(suffixes)) {
+ return false;
+ }
+
+ for (CharSequence suffix : suffixes) {
+ if (endWith(str, suffix, false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // ------------------------------------------------------------------------ contains
+
+ /**
+ * 指定字符是否在字符串中出现过
+ *
+ * @param str 字符串
+ * @param searchChar 被查找的字符
+ * @return 是否包含
+ * @since 3.1.2
+ */
+ public static boolean contains(CharSequence str, char searchChar) {
+ return indexOf(str, searchChar) > -1;
+ }
+
+ /**
+ * 指定字符串是否在字符串中出现过
+ *
+ * @param str 字符串
+ * @param searchStr 被查找的字符串
+ * @return 是否包含
+ * @since 5.1.1
+ */
+ public static boolean contains(CharSequence str, CharSequence searchStr) {
+ if (null == str || null == searchStr) {
+ return false;
+ }
+ return str.toString().contains(searchStr);
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符串列表中的任意一个字符串
+ *
+ * @param str 指定字符串
+ * @param testStrs 需要检查的字符串数组
+ * @return 是否包含任意一个字符串
+ * @since 3.2.0
+ */
+ public static boolean containsAny(CharSequence str, CharSequence... testStrs) {
+ return null != getContainsStr(str, testStrs);
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符列表中的任意一个字符
+ *
+ * @param str 指定字符串
+ * @param testChars 需要检查的字符数组
+ * @return 是否包含任意一个字符
+ * @since 4.1.11
+ */
+ public static boolean containsAny(CharSequence str, char... testChars) {
+ if (false == isEmpty(str)) {
+ int len = str.length();
+ for (int i = 0; i < len; i++) {
+ if (ArrayUtil.contains(testChars, str.charAt(i))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 检查指定字符串中是否只包含给定的字符
+ *
+ * @param str 字符串
+ * @param testChars 检查的字符
+ * @return 字符串含有非检查的字符,返回false
+ * @since 4.4.1
+ */
+ public static boolean containsOnly(CharSequence str, char... testChars) {
+ if (false == isEmpty(str)) {
+ int len = str.length();
+ for (int i = 0; i < len; i++) {
+ if (false == ArrayUtil.contains(testChars, str.charAt(i))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 给定字符串是否包含空白符(空白符包括空格、制表符、全角空格和不间断空格)
+ * 如果给定字符串为null或者"",则返回false
+ *
+ * @param str 字符串
+ * @return 是否包含空白符
+ * @since 4.0.8
+ */
+ public static boolean containsBlank(CharSequence str) {
+ if (null == str) {
+ return false;
+ }
+ final int length = str.length();
+ if (0 == length) {
+ return false;
+ }
+
+ for (int i = 0; i < length; i += 1) {
+ if (CharUtil.isBlankChar(str.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串
+ *
+ * @param str 指定字符串
+ * @param testStrs 需要检查的字符串数组
+ * @return 被包含的第一个字符串
+ * @since 3.2.0
+ */
+ public static String getContainsStr(CharSequence str, CharSequence... testStrs) {
+ if (isEmpty(str) || ArrayUtil.isEmpty(testStrs)) {
+ return null;
+ }
+ for (CharSequence checkStr : testStrs) {
+ if (str.toString().contains(checkStr)) {
+ return checkStr.toString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 是否包含特定字符,忽略大小写,如果给定两个参数都为{@code null},返回true
+ *
+ * @param str 被检测字符串
+ * @param testStr 被测试是否包含的字符串
+ * @return 是否包含
+ */
+ public static boolean containsIgnoreCase(CharSequence str, CharSequence testStr) {
+ if (null == str) {
+ // 如果被监测字符串和
+ return null == testStr;
+ }
+ return str.toString().toLowerCase().contains(testStr.toString().toLowerCase());
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符串列表中的任意一个字符串
+ * 忽略大小写
+ *
+ * @param str 指定字符串
+ * @param testStrs 需要检查的字符串数组
+ * @return 是否包含任意一个字符串
+ * @since 3.2.0
+ */
+ public static boolean containsAnyIgnoreCase(CharSequence str, CharSequence... testStrs) {
+ return null != getContainsStrIgnoreCase(str, testStrs);
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串
+ * 忽略大小写
+ *
+ * @param str 指定字符串
+ * @param testStrs 需要检查的字符串数组
+ * @return 被包含的第一个字符串
+ * @since 3.2.0
+ */
+ public static String getContainsStrIgnoreCase(CharSequence str, CharSequence... testStrs) {
+ if (isEmpty(str) || ArrayUtil.isEmpty(testStrs)) {
+ return null;
+ }
+ for (CharSequence testStr : testStrs) {
+ if (containsIgnoreCase(str, testStr)) {
+ return testStr.toString();
+ }
+ }
+ return null;
+ }
+
+ // ------------------------------------------------------------------------ indexOf
+
+ /**
+ * 指定范围内查找指定字符
+ *
+ * @param str 字符串
+ * @param searchChar 被查找的字符
+ * @return 位置
+ */
+ public static int indexOf(final CharSequence str, char searchChar) {
+ return indexOf(str, searchChar, 0);
+ }
+
+ /**
+ * 指定范围内查找指定字符
+ *
+ * @param str 字符串
+ * @param searchChar 被查找的字符
+ * @param start 起始位置,如果小于0,从0开始查找
+ * @return 位置
+ */
+ public static int indexOf(CharSequence str, char searchChar, int start) {
+ if (str instanceof String) {
+ return ((String) str).indexOf(searchChar, start);
+ } else {
+ return indexOf(str, searchChar, start, -1);
+ }
+ }
+
+ /**
+ * 指定范围内查找指定字符
+ *
+ * @param str 字符串
+ * @param searchChar 被查找的字符
+ * @param start 起始位置,如果小于0,从0开始查找
+ * @param end 终止位置,如果超过str.length()则默认查找到字符串末尾
+ * @return 位置
+ */
+ public static int indexOf(final CharSequence str, char searchChar, int start, int end) {
+ if (isEmpty(str)) {
+ return INDEX_NOT_FOUND;
+ }
+ final int len = str.length();
+ if (start < 0 || start > len) {
+ start = 0;
+ }
+ if (end > len || end < 0) {
+ end = len;
+ }
+ for (int i = start; i < end; i++) {
+ if (str.charAt(i) == searchChar) {
+ return i;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ /**
+ * 指定范围内查找字符串,忽略大小写
+ *
+ *
+ * StrUtil.indexOfIgnoreCase(null, *, *) = -1
+ * StrUtil.indexOfIgnoreCase(*, null, *) = -1
+ * StrUtil.indexOfIgnoreCase("", "", 0) = 0
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
+ * StrUtil.indexOfIgnoreCase("abc", "", 9) = -1
+ *
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @return 位置
+ * @since 3.2.1
+ */
+ public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
+ return indexOfIgnoreCase(str, searchStr, 0);
+ }
+
+ /**
+ * 指定范围内查找字符串
+ *
+ *
+ * StrUtil.indexOfIgnoreCase(null, *, *) = -1
+ * StrUtil.indexOfIgnoreCase(*, null, *) = -1
+ * StrUtil.indexOfIgnoreCase("", "", 0) = 0
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+ * StrUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
+ * StrUtil.indexOfIgnoreCase("abc", "", 9) = -1
+ *
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置
+ * @return 位置
+ * @since 3.2.1
+ */
+ public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int fromIndex) {
+ return indexOf(str, searchStr, fromIndex, true);
+ }
+
+ /**
+ * 指定范围内查找字符串
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置
+ * @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;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ /**
+ * 指定范围内查找字符串,忽略大小写
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @return 位置
+ * @since 3.2.1
+ */
+ public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
+ return lastIndexOfIgnoreCase(str, searchStr, str.length());
+ }
+
+ /**
+ * 指定范围内查找字符串,忽略大小写
+ * fromIndex 为搜索起始位置,从后往前计数
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置,从后往前计数
+ * @return 位置
+ * @since 3.2.1
+ */
+ public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int fromIndex) {
+ return lastIndexOf(str, searchStr, fromIndex, true);
+ }
+
+ /**
+ * 指定范围内查找字符串
+ * fromIndex 为搜索起始位置,从后往前计数
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置,从后往前计数
+ * @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;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ /**
+ * 返回字符串 searchStr 在字符串 str 中第 ordinal 次出现的位置。
+ *
+ *
+ * 此方法来自:Apache-Commons-Lang
+ *
+ * StrUtil.ordinalIndexOf(null, *, *) = -1
+ * StrUtil.ordinalIndexOf(*, null, *) = -1
+ * StrUtil.ordinalIndexOf("", "", *) = 0
+ * StrUtil.ordinalIndexOf("aabaabaa", "a", 1) = 0
+ * StrUtil.ordinalIndexOf("aabaabaa", "a", 2) = 1
+ * StrUtil.ordinalIndexOf("aabaabaa", "b", 1) = 2
+ * StrUtil.ordinalIndexOf("aabaabaa", "b", 2) = 5
+ * StrUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1
+ * StrUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4
+ * StrUtil.ordinalIndexOf("aabaabaa", "", 1) = 0
+ * StrUtil.ordinalIndexOf("aabaabaa", "", 2) = 0
+ *
+ *
+ * @param str 被检查的字符串,可以为null
+ * @param searchStr 被查找的字符串,可以为null
+ * @param ordinal 第几次出现的位置
+ * @return 查找到的位置
+ * @since 3.2.3
+ */
+ public static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) {
+ if (str == null || searchStr == null || ordinal <= 0) {
+ return INDEX_NOT_FOUND;
+ }
+ if (searchStr.length() == 0) {
+ return 0;
+ }
+ int found = 0;
+ int index = INDEX_NOT_FOUND;
+ do {
+ index = indexOf(str, searchStr, index + 1, false);
+ if (index < 0) {
+ return index;
+ }
+ found++;
+ } while (found < ordinal);
+ return index;
+ }
+
+ // ------------------------------------------------------------------------ remove
+
+ /**
+ * 移除字符串中所有给定字符串
+ * 例:removeAll("aa-bb-cc-dd", "-") =》 aabbccdd
+ *
+ * @param str 字符串
+ * @param strToRemove 被移除的字符串
+ * @return 移除后的字符串
+ */
+ public static String removeAll(CharSequence str, CharSequence strToRemove) {
+ // strToRemove如果为空, 也不用继续后面的逻辑
+ if (isEmpty(str) || isEmpty(strToRemove)) {
+ return str(str);
+ }
+ return str.toString().replace(strToRemove, EMPTY);
+ }
+
+ /**
+ * 移除字符串中所有给定字符串,当某个字符串出现多次,则全部移除
+ * 例:removeAny("aa-bb-cc-dd", "a", "b") =》 --cc-dd
+ *
+ * @param str 字符串
+ * @param strsToRemove 被移除的字符串
+ * @return 移除后的字符串
+ * @since 5.3.8
+ */
+ public static String removeAny(CharSequence str, CharSequence... strsToRemove) {
+ String result = str(str);
+ if (isNotEmpty(str)) {
+ for (CharSequence strToRemove : strsToRemove) {
+ result = removeAll(result, strToRemove);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 去除字符串中指定的多个字符,如有多个则全部去除
+ *
+ * @param str 字符串
+ * @param chars 字符列表
+ * @return 去除后的字符
+ * @since 4.2.2
+ */
+ public static String removeAll(CharSequence str, char... chars) {
+ if (null == str || ArrayUtil.isEmpty(chars)) {
+ return str(str);
+ }
+ final int len = str.length();
+ if (0 == len) {
+ return str(str);
+ }
+ final StringBuilder builder = new StringBuilder(len);
+ char c;
+ for (int i = 0; i < len; i++) {
+ c = str.charAt(i);
+ if (false == ArrayUtil.contains(chars, c)) {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * 去除所有换行符,包括:
+ *
+ *
+ * 1. \r
+ * 1. \n
+ *
+ *
+ * @param str 字符串
+ * @return 处理后的字符串
+ * @since 4.2.2
+ */
+ public static String removeAllLineBreaks(CharSequence str) {
+ return removeAll(str, CharUtil.CR, CharUtil.LF);
+ }
+
+ /**
+ * 去掉首部指定长度的字符串并将剩余字符串首字母小写
+ * 例如:str=setName, preLength=3 =》 return name
+ *
+ * @param str 被处理的字符串
+ * @param preLength 去掉的长度
+ * @return 处理后的字符串,不符合规范返回null
+ */
+ public static String removePreAndLowerFirst(CharSequence str, int preLength) {
+ if (str == null) {
+ return null;
+ }
+ if (str.length() > preLength) {
+ char first = Character.toLowerCase(str.charAt(preLength));
+ if (str.length() > preLength + 1) {
+ return first + str.toString().substring(preLength + 1);
+ }
+ return String.valueOf(first);
+ } else {
+ return str.toString();
+ }
+ }
+
+ /**
+ * 去掉首部指定长度的字符串并将剩余字符串首字母小写
+ * 例如:str=setName, prefix=set =》 return name
+ *
+ * @param str 被处理的字符串
+ * @param prefix 前缀
+ * @return 处理后的字符串,不符合规范返回null
+ */
+ public static String removePreAndLowerFirst(CharSequence str, CharSequence prefix) {
+ return lowerFirst(removePrefix(str, prefix));
+ }
+
+ /**
+ * 去掉指定前缀
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串
+ */
+ public static String removePrefix(CharSequence str, CharSequence prefix) {
+ if (isEmpty(str) || isEmpty(prefix)) {
+ return str(str);
+ }
+
+ final String str2 = str.toString();
+ if (str2.startsWith(prefix.toString())) {
+ return subSuf(str2, prefix.length());// 截取后半段
+ }
+ return str2;
+ }
+
+ /**
+ * 忽略大小写去掉指定前缀
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @return 切掉后的字符串,若前缀不是 prefix, 返回原字符串
+ */
+ public static String removePrefixIgnoreCase(CharSequence str, CharSequence prefix) {
+ if (isEmpty(str) || isEmpty(prefix)) {
+ return str(str);
+ }
+
+ final String str2 = str.toString();
+ if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) {
+ return subSuf(str2, prefix.length());// 截取后半段
+ }
+ return str2;
+ }
+
+ /**
+ * 去掉指定后缀
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
+ */
+ public static String removeSuffix(CharSequence str, CharSequence suffix) {
+ if (isEmpty(str) || isEmpty(suffix)) {
+ return str(str);
+ }
+
+ final String str2 = str.toString();
+ if (str2.endsWith(suffix.toString())) {
+ return subPre(str2, str2.length() - suffix.length());// 截取前半段
+ }
+ return str2;
+ }
+
+ /**
+ * 去掉指定后缀,并小写首字母
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
+ */
+ public static String removeSufAndLowerFirst(CharSequence str, CharSequence suffix) {
+ return lowerFirst(removeSuffix(str, suffix));
+ }
+
+ /**
+ * 忽略大小写去掉指定后缀
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
+ */
+ public static String removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) {
+ if (isEmpty(str) || isEmpty(suffix)) {
+ return str(str);
+ }
+
+ final String str2 = str.toString();
+ if (str2.toLowerCase().endsWith(suffix.toString().toLowerCase())) {
+ return subPre(str2, str2.length() - suffix.length());
+ }
+ return str2;
+ }
+
+ /**
+ * 清理空白字符
+ *
+ * @param str 被清理的字符串
+ * @return 清理后的字符串
+ */
+ public static String cleanBlank(CharSequence str) {
+ return filter(str, c -> false == CharUtil.isBlankChar(c));
+ }
+
+ // ------------------------------------------------------------------------ strip
+
+ /**
+ * 去除两边的指定字符串
+ *
+ * @param str 被处理的字符串
+ * @param prefixOrSuffix 前缀或后缀
+ * @return 处理后的字符串
+ * @since 3.1.2
+ */
+ public static String strip(CharSequence str, CharSequence prefixOrSuffix) {
+ if (equals(str, prefixOrSuffix)) {
+ // 对于去除相同字符的情况单独处理
+ return EMPTY;
+ }
+ return strip(str, prefixOrSuffix, prefixOrSuffix);
+ }
+
+ /**
+ * 去除两边的指定字符串
+ *
+ * @param str 被处理的字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 处理后的字符串
+ * @since 3.1.2
+ */
+ public static String strip(CharSequence str, CharSequence prefix, CharSequence suffix) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+
+ int from = 0;
+ int to = str.length();
+
+ String str2 = str.toString();
+ if (startWith(str2, prefix)) {
+ from = prefix.length();
+ }
+ if (endWith(str2, suffix)) {
+ to -= suffix.length();
+ }
+
+ return str2.substring(Math.min(from, to), Math.max(from, to));
+ }
+
+ /**
+ * 去除两边的指定字符串,忽略大小写
+ *
+ * @param str 被处理的字符串
+ * @param prefixOrSuffix 前缀或后缀
+ * @return 处理后的字符串
+ * @since 3.1.2
+ */
+ public static String stripIgnoreCase(CharSequence str, CharSequence prefixOrSuffix) {
+ return stripIgnoreCase(str, prefixOrSuffix, prefixOrSuffix);
+ }
+
+ /**
+ * 去除两边的指定字符串,忽略大小写
+ *
+ * @param str 被处理的字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 处理后的字符串
+ * @since 3.1.2
+ */
+ public static String stripIgnoreCase(CharSequence str, CharSequence prefix, CharSequence suffix) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+ int from = 0;
+ int to = str.length();
+
+ String str2 = str.toString();
+ if (startWithIgnoreCase(str2, prefix)) {
+ from = prefix.length();
+ }
+ if (endWithIgnoreCase(str2, suffix)) {
+ to -= suffix.length();
+ }
+ return str2.substring(from, to);
+ }
+
+ // ------------------------------------------------------------------------ add
+
+ /**
+ * 如果给定字符串不是以prefix开头的,在开头补充 prefix
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @return 补充后的字符串
+ */
+ public static String addPrefixIfNot(CharSequence str, CharSequence prefix) {
+ if (isEmpty(str) || isEmpty(prefix)) {
+ return str(str);
+ }
+
+ final String str2 = str.toString();
+ final String prefix2 = prefix.toString();
+ if (false == str2.startsWith(prefix2)) {
+ return prefix2.concat(str2);
+ }
+ return str2;
+ }
+
+ /**
+ * 如果给定字符串不是以suffix结尾的,在尾部补充 suffix
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 补充后的字符串
+ */
+ public static String addSuffixIfNot(CharSequence str, CharSequence suffix) {
+ if (isEmpty(str) || isEmpty(suffix)) {
+ return str(str);
+ }
+
+ final String str2 = str.toString();
+ final String suffix2 = suffix.toString();
+ if (false == str2.endsWith(suffix2)) {
+ return str2.concat(suffix2);
+ }
+ return str2;
+ }
+
+ // ------------------------------------------------------------------------ split
+
+ /**
+ * 切分字符串
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符字符
+ * @return 切分后的数组
+ */
+ public static String[] splitToArray(CharSequence str, char separator) {
+ return splitToArray(str, separator, 0);
+ }
+
+ /**
+ * 切分字符串为long数组
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符
+ * @return 切分后long数组
+ * @since 4.0.6
+ */
+ public static long[] splitToLong(CharSequence str, char separator) {
+ return Convert.convert(long[].class, splitTrim(str, separator));
+ }
+
+ /**
+ * 切分字符串为long数组
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符字符串
+ * @return 切分后long数组
+ * @since 4.0.6
+ */
+ public static long[] splitToLong(CharSequence str, CharSequence separator) {
+ return Convert.convert(long[].class, splitTrim(str, separator));
+ }
+
+ /**
+ * 切分字符串为int数组
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符
+ * @return 切分后long数组
+ * @since 4.0.6
+ */
+ public static int[] splitToInt(CharSequence str, char separator) {
+ return Convert.convert(int[].class, splitTrim(str, separator));
+ }
+
+ /**
+ * 切分字符串为int数组
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符字符串
+ * @return 切分后long数组
+ * @since 4.0.6
+ */
+ public static int[] splitToInt(CharSequence str, CharSequence separator) {
+ return Convert.convert(int[].class, splitTrim(str, separator));
+ }
+
+ /**
+ * 切分字符串
+ * a#b#c =》 [a,b,c]
+ * a##b#c =》 [a,"",b,c]
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符字符
+ * @return 切分后的集合
+ */
+ public static List
+ * index从0开始计算,最后一个字符为-1
+ * 如果from和to位置一样,返回 ""
+ * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length
+ * 如果经过修正的index中from大于to,则互换from和to example:
+ * abcdefgh 2 3 =》 c
+ * abcdefgh 2 -3 =》 cde
+ *
+ * @param str String
+ * @param fromIndexInclude 开始的index(包括)
+ * @param toIndexExclude 结束的index(不包括)
+ * @return 字串
+ */
+ public static String sub(CharSequence str, int fromIndexInclude, int toIndexExclude) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+ int len = str.length();
+
+ if (fromIndexInclude < 0) {
+ fromIndexInclude = len + fromIndexInclude;
+ if (fromIndexInclude < 0) {
+ fromIndexInclude = 0;
+ }
+ } else if (fromIndexInclude > len) {
+ fromIndexInclude = len;
+ }
+
+ if (toIndexExclude < 0) {
+ toIndexExclude = len + toIndexExclude;
+ if (toIndexExclude < 0) {
+ toIndexExclude = len;
+ }
+ } else if (toIndexExclude > len) {
+ toIndexExclude = len;
+ }
+
+ if (toIndexExclude < fromIndexInclude) {
+ int tmp = fromIndexInclude;
+ fromIndexInclude = toIndexExclude;
+ toIndexExclude = tmp;
+ }
+
+ if (fromIndexInclude == toIndexExclude) {
+ return EMPTY;
+ }
+
+ return str.toString().substring(fromIndexInclude, toIndexExclude);
+ }
+
+ /**
+ * 通过CodePoint截取字符串,可以截断Emoji
+ *
+ * @param str String
+ * @param fromIndex 开始的index(包括)
+ * @param toIndex 结束的index(不包括)
+ * @return 字串
+ */
+ public static String subByCodePoint(CharSequence str, int fromIndex, int toIndex) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+
+ if (fromIndex < 0 || fromIndex > toIndex) {
+ throw new IllegalArgumentException();
+ }
+
+ if (fromIndex == toIndex) {
+ return EMPTY;
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ final int subLen = toIndex - fromIndex;
+ str.toString().codePoints().skip(fromIndex).limit(subLen).forEach(v -> sb.append(Character.toChars(v)));
+ return sb.toString();
+ }
+
+ /**
+ * 截取部分字符串,这里一个汉字的长度认为是2
+ *
+ * @param str 字符串
+ * @param len 切割的位置
+ * @param suffix 切割后加上后缀
+ * @return 切割后的字符串
+ * @since 3.1.1
+ */
+ public static String subPreGbk(CharSequence str, int len, CharSequence suffix) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+
+ byte[] b;
+ int counterOfDoubleByte = 0;
+ b = str.toString().getBytes(CharsetUtil.CHARSET_GBK);
+ if (b.length <= len) {
+ return str.toString();
+ }
+ for (int i = 0; i < len; i++) {
+ if (b[i] < 0) {
+ counterOfDoubleByte++;
+ }
+ }
+
+ if (counterOfDoubleByte % 2 != 0) {
+ len += 1;
+ }
+ return new String(b, 0, len, CharsetUtil.CHARSET_GBK) + suffix;
+ }
+
+ /**
+ * 切割指定位置之前部分的字符串
+ *
+ * @param string 字符串
+ * @param toIndexExclude 切割到的位置(不包括)
+ * @return 切割后的剩余的前半部分字符串
+ */
+ public static String subPre(CharSequence string, int toIndexExclude) {
+ return sub(string, 0, toIndexExclude);
+ }
+
+ /**
+ * 切割指定位置之后部分的字符串
+ *
+ * @param string 字符串
+ * @param fromIndex 切割开始的位置(包括)
+ * @return 切割后后剩余的后半部分字符串
+ */
+ public static String subSuf(CharSequence string, int fromIndex) {
+ if (isEmpty(string)) {
+ return null;
+ }
+ return sub(string, fromIndex, string.length());
+ }
+
+ /**
+ * 切割指定长度的后部分的字符串
+ *
+ *
+ * StrUtil.subSufByLength("abcde", 3) = "cde"
+ * StrUtil.subSufByLength("abcde", 0) = ""
+ * StrUtil.subSufByLength("abcde", -5) = ""
+ * StrUtil.subSufByLength("abcde", -1) = ""
+ * StrUtil.subSufByLength("abcde", 5) = "abcde"
+ * StrUtil.subSufByLength("abcde", 10) = "abcde"
+ * StrUtil.subSufByLength(null, 3) = null
+ *
+ *
+ * @param string 字符串
+ * @param length 切割长度
+ * @return 切割后后剩余的后半部分字符串
+ * @since 4.0.1
+ */
+ public static String subSufByLength(CharSequence string, int length) {
+ if (isEmpty(string)) {
+ return null;
+ }
+ if (length <= 0) {
+ return EMPTY;
+ }
+ return sub(string, -length, string.length());
+ }
+
+ /**
+ * 截取字符串,从指定位置开始,截取指定长度的字符串
+ * author weibaohui
+ *
+ * @param input 原始字符串
+ * @param fromIndex 开始的index,包括
+ * @param length 要截取的长度
+ * @return 截取后的字符串
+ */
+ public static String subWithLength(String input, int fromIndex, int length) {
+ return sub(input, fromIndex, fromIndex + length);
+ }
+
+ /**
+ * 截取分隔字符串之前的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串(null或"")或者分隔字符串为null,返回原字符串
+ * 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串,举例如下:
+ *
+ *
+ * StrUtil.subBefore(null, *, false) = null
+ * StrUtil.subBefore("", *, false) = ""
+ * StrUtil.subBefore("abc", "a", false) = ""
+ * StrUtil.subBefore("abcba", "b", false) = "a"
+ * StrUtil.subBefore("abc", "c", false) = "ab"
+ * StrUtil.subBefore("abc", "d", false) = "abc"
+ * StrUtil.subBefore("abc", "", false) = ""
+ * StrUtil.subBefore("abc", null, false) = "abc"
+ *
+ *
+ * @param string 被查找的字符串
+ * @param separator 分隔字符串(不包括)
+ * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
+ * @return 切割后的字符串
+ * @since 3.1.1
+ */
+ public static String subBefore(CharSequence string, CharSequence separator, boolean isLastSeparator) {
+ if (isEmpty(string) || separator == null) {
+ return null == string ? null : string.toString();
+ }
+
+ final String str = string.toString();
+ final String sep = separator.toString();
+ if (sep.isEmpty()) {
+ return EMPTY;
+ }
+ final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
+ if (INDEX_NOT_FOUND == pos) {
+ return str;
+ }
+ if (0 == pos) {
+ return EMPTY;
+ }
+ return str.substring(0, pos);
+ }
+
+ /**
+ * 截取分隔字符串之前的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串(null或"")或者分隔字符串为null,返回原字符串
+ * 如果分隔字符串未找到,返回原字符串,举例如下:
+ *
+ *
+ * StrUtil.subBefore(null, *, false) = null
+ * StrUtil.subBefore("", *, false) = ""
+ * StrUtil.subBefore("abc", 'a', false) = ""
+ * StrUtil.subBefore("abcba", 'b', false) = "a"
+ * StrUtil.subBefore("abc", 'c', false) = "ab"
+ * StrUtil.subBefore("abc", 'd', false) = "abc"
+ *
+ *
+ * @param string 被查找的字符串
+ * @param separator 分隔字符串(不包括)
+ * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
+ * @return 切割后的字符串
+ * @since 4.1.15
+ */
+ public static String subBefore(CharSequence string, char separator, boolean isLastSeparator) {
+ if (isEmpty(string)) {
+ return null == string ? null : EMPTY;
+ }
+
+ final String str = string.toString();
+ final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
+ if (INDEX_NOT_FOUND == pos) {
+ return str;
+ }
+ if (0 == pos) {
+ return EMPTY;
+ }
+ return str.substring(0, pos);
+ }
+
+ /**
+ * 截取分隔字符串之后的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串(null或""),返回原字符串
+ * 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串,举例如下:
+ *
+ *
+ * StrUtil.subAfter(null, *, false) = null
+ * StrUtil.subAfter("", *, false) = ""
+ * StrUtil.subAfter(*, null, false) = ""
+ * StrUtil.subAfter("abc", "a", false) = "bc"
+ * StrUtil.subAfter("abcba", "b", false) = "cba"
+ * StrUtil.subAfter("abc", "c", false) = ""
+ * StrUtil.subAfter("abc", "d", false) = ""
+ * StrUtil.subAfter("abc", "", false) = "abc"
+ *
+ *
+ * @param string 被查找的字符串
+ * @param separator 分隔字符串(不包括)
+ * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
+ * @return 切割后的字符串
+ * @since 3.1.1
+ */
+ public static String subAfter(CharSequence string, CharSequence separator, boolean isLastSeparator) {
+ if (isEmpty(string)) {
+ return null == string ? null : EMPTY;
+ }
+ if (separator == null) {
+ return EMPTY;
+ }
+ final String str = string.toString();
+ final String sep = separator.toString();
+ final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
+ if (INDEX_NOT_FOUND == pos || (string.length() - 1) == pos) {
+ return EMPTY;
+ }
+ return str.substring(pos + separator.length());
+ }
+
+ /**
+ * 截取分隔字符串之后的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串(null或""),返回原字符串
+ * 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串,举例如下:
+ *
+ *
+ * StrUtil.subAfter(null, *, false) = null
+ * StrUtil.subAfter("", *, false) = ""
+ * StrUtil.subAfter("abc", 'a', false) = "bc"
+ * StrUtil.subAfter("abcba", 'b', false) = "cba"
+ * StrUtil.subAfter("abc", 'c', false) = ""
+ * StrUtil.subAfter("abc", 'd', false) = ""
+ *
+ *
+ * @param string 被查找的字符串
+ * @param separator 分隔字符串(不包括)
+ * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
+ * @return 切割后的字符串
+ * @since 4.1.15
+ */
+ public static String subAfter(CharSequence string, char separator, boolean isLastSeparator) {
+ if (isEmpty(string)) {
+ return null == string ? null : EMPTY;
+ }
+ final String str = string.toString();
+ final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
+ if (INDEX_NOT_FOUND == pos) {
+ return EMPTY;
+ }
+ return str.substring(pos + 1);
+ }
+
+ /**
+ * 截取指定字符串中间部分,不包括标识字符串
+ *
+ * StrUtil.subBetween("wx[b]yz", "[", "]") = "b"
+ * StrUtil.subBetween(null, *, *) = null
+ * StrUtil.subBetween(*, null, *) = null
+ * StrUtil.subBetween(*, *, null) = null
+ * StrUtil.subBetween("", "", "") = ""
+ * StrUtil.subBetween("", "", "]") = null
+ * StrUtil.subBetween("", "[", "]") = null
+ * StrUtil.subBetween("yabcz", "", "") = ""
+ * StrUtil.subBetween("yabcz", "y", "z") = "abc"
+ * StrUtil.subBetween("yabczyabcz", "y", "z") = "abc"
+ *
+ *
+ * @param str 被切割的字符串
+ * @param before 截取开始的字符串标识
+ * @param after 截取到的字符串标识
+ * @return 截取后的字符串
+ * @since 3.1.1
+ */
+ public static String subBetween(CharSequence str, CharSequence before, CharSequence after) {
+ if (str == null || before == null || after == null) {
+ return null;
+ }
+
+ final String str2 = str.toString();
+ final String before2 = before.toString();
+ final String after2 = after.toString();
+
+ final int start = str2.indexOf(before2);
+ if (start != INDEX_NOT_FOUND) {
+ final int end = str2.indexOf(after2, start + before2.length());
+ if (end != INDEX_NOT_FOUND) {
+ return str2.substring(start + before2.length(), end);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 截取指定字符串中间部分,不包括标识字符串
+ *
+ * StrUtil.subBetween(null, *) = null
+ * StrUtil.subBetween("", "") = ""
+ * StrUtil.subBetween("", "tag") = null
+ * StrUtil.subBetween("tagabctag", null) = null
+ * StrUtil.subBetween("tagabctag", "") = ""
+ * StrUtil.subBetween("tagabctag", "tag") = "abc"
+ *
+ *
+ * @param str 被切割的字符串
+ * @param beforeAndAfter 截取开始和结束的字符串标识
+ * @return 截取后的字符串
+ * @since 3.1.1
+ */
+ public static String subBetween(CharSequence str, CharSequence beforeAndAfter) {
+ return subBetween(str, beforeAndAfter, beforeAndAfter);
+ }
+
+ /**
+ * 截取指定字符串多段中间部分,不包括标识字符串
+ *
+ * StrUtil.subBetweenAll("wx[b]y[z]", "[", "]") = ["b","z"]
+ * StrUtil.subBetweenAll(null, *, *) = []
+ * StrUtil.subBetweenAll(*, null, *) = []
+ * StrUtil.subBetweenAll(*, *, null) = []
+ * StrUtil.subBetweenAll("", "", "") = []
+ * StrUtil.subBetweenAll("", "", "]") = []
+ * StrUtil.subBetweenAll("", "[", "]") = []
+ * StrUtil.subBetweenAll("yabcz", "", "") = []
+ * StrUtil.subBetweenAll("yabcz", "y", "z") = ["abc"]
+ * StrUtil.subBetweenAll("yabczyabcz", "y", "z") = ["abc","abc"]
+ * StrUtil.subBetweenAll("[yabc[zy]abcz]", "[", "]"); = ["zy"] 重叠时只截取内部,
+ *
+ *
+ * @param str 被切割的字符串
+ * @param prefix 截取开始的字符串标识
+ * @param suffix 截取到的字符串标识
+ * @return 截取后的字符串
+ * @author dahuoyzs
+ * @since 5.2.5
+ */
+ public static String[] subBetweenAll(CharSequence str, CharSequence prefix, CharSequence suffix) {
+ if (hasEmpty(str, prefix, suffix) ||
+ // 不包含起始字符串,则肯定没有子串
+ false == contains(str, prefix)) {
+ return new String[0];
+ }
+
+ final List
+ *
+ * StrUtil.subBetweenAll(null, *) = []
+ * StrUtil.subBetweenAll(*, null) = []
+ * StrUtil.subBetweenAll(*, *) = []
+ * StrUtil.subBetweenAll("", "") = []
+ * StrUtil.subBetweenAll("", "#") = []
+ * StrUtil.subBetweenAll("gotanks", "") = []
+ * StrUtil.subBetweenAll("#gotanks#", "#") = ["gotanks"]
+ * StrUtil.subBetweenAll("#hello# #world#!", "#") = ["hello", "world"]
+ * StrUtil.subBetweenAll("#hello# world#!", "#"); = ["hello"]
+ *
+ *
+ * @param str 被切割的字符串
+ * @param prefixAndSuffix 截取开始和结束的字符串标识
+ * @return 截取后的字符串
+ * @author gotanks
+ * @since 5.5.0
+ */
+ public static String[] subBetweenAll(CharSequence str, CharSequence prefixAndSuffix) {
+ return subBetweenAll(str, prefixAndSuffix, prefixAndSuffix);
+ }
+
+ // ------------------------------------------------------------------------ repeat
+
+ /**
+ * 重复某个字符
+ *
+ * @param c 被重复的字符
+ * @param count 重复的数目,如果小于等于0则返回""
+ * @return 重复字符字符串
+ */
+ public static String repeat(char c, int count) {
+ if (count <= 0) {
+ return EMPTY;
+ }
+
+ char[] result = new char[count];
+ for (int i = 0; i < count; i++) {
+ result[i] = c;
+ }
+ return new String(result);
+ }
+
+ /**
+ * 重复某个字符串
+ *
+ * @param str 被重复的字符
+ * @param count 重复的数目
+ * @return 重复字符字符串
+ */
+ public static String repeat(CharSequence str, int count) {
+ if (null == str) {
+ return null;
+ }
+ if (count <= 0 || str.length() == 0) {
+ return EMPTY;
+ }
+ if (count == 1) {
+ return str.toString();
+ }
+
+ // 检查
+ final int len = str.length();
+ final long longSize = (long) len * (long) count;
+ final int size = (int) longSize;
+ if (size != longSize) {
+ throw new ArrayIndexOutOfBoundsException("Required String length is too large: " + longSize);
+ }
+
+ final char[] array = new char[size];
+ str.toString().getChars(0, len, array, 0);
+ int n;
+ for (n = len; n < size - n; n <<= 1) {// n <<= 1相当于n *2
+ System.arraycopy(array, 0, array, n, n);
+ }
+ System.arraycopy(array, 0, array, n, size - n);
+ return new String(array);
+ }
+
+ /**
+ * 重复某个字符串到指定长度
+ *
+ * @param str 被重复的字符
+ * @param padLen 指定长度
+ * @return 重复字符字符串
+ * @since 4.3.2
+ */
+ public static String repeatByLength(CharSequence str, int padLen) {
+ if (null == str) {
+ return null;
+ }
+ if (padLen <= 0) {
+ return StrUtil.EMPTY;
+ }
+ final int strLen = str.length();
+ if (strLen == padLen) {
+ return str.toString();
+ } else if (strLen > padLen) {
+ return subPre(str, padLen);
+ }
+
+ // 重复,直到达到指定长度
+ final char[] padding = new char[padLen];
+ for (int i = 0; i < padLen; i++) {
+ padding[i] = str.charAt(i % strLen);
+ }
+ return new String(padding);
+ }
+
+ /**
+ * 重复某个字符串并通过分界符连接
+ *
+ *
+ * StrUtil.repeatAndJoin("?", 5, ",") = "?,?,?,?,?"
+ * StrUtil.repeatAndJoin("?", 0, ",") = ""
+ * StrUtil.repeatAndJoin("?", 5, null) = "?????"
+ *
+ *
+ * @param str 被重复的字符串
+ * @param count 数量
+ * @param conjunction 分界符
+ * @return 连接后的字符串
+ * @since 4.0.1
+ */
+ public static String repeatAndJoin(CharSequence str, int count, CharSequence conjunction) {
+ if (count <= 0) {
+ return EMPTY;
+ }
+ final StrBuilder builder = StrBuilder.create();
+ boolean isFirst = true;
+ while (count-- > 0) {
+ if (isFirst) {
+ isFirst = false;
+ } else if (isNotEmpty(conjunction)) {
+ builder.append(conjunction);
+ }
+ builder.append(str);
+ }
+ return builder.toString();
+ }
+
+ // ------------------------------------------------------------------------ equals
+
+ /**
+ * 比较两个字符串(大小写敏感)。
+ *
+ *
+ * equals(null, null) = true
+ * equals(null, "abc") = false
+ * equals("abc", null) = false
+ * equals("abc", "abc") = true
+ * equals("abc", "ABC") = false
+ *
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
+ */
+ public static boolean equals(CharSequence str1, CharSequence str2) {
+ return equals(str1, str2, false);
+ }
+
+ /**
+ * 比较两个字符串(大小写不敏感)。
+ *
+ *
+ * equalsIgnoreCase(null, null) = true
+ * equalsIgnoreCase(null, "abc") = false
+ * equalsIgnoreCase("abc", null) = false
+ * equalsIgnoreCase("abc", "abc") = true
+ * equalsIgnoreCase("abc", "ABC") = true
+ *
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
+ */
+ public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
+ return equals(str1, str2, true);
+ }
+
+ /**
+ * 比较两个字符串是否相等。
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param ignoreCase 是否忽略大小写
+ * @return 如果两个字符串相同,或者都是{@code null},则返回{@code true}
+ * @since 3.2.0
+ */
+ public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
+ if (null == str1) {
+ // 只有两个都为null才判断相等
+ return str2 == null;
+ }
+ if (null == str2) {
+ // 字符串2空,字符串1非空,直接false
+ return false;
+ }
+
+ if (ignoreCase) {
+ return str1.toString().equalsIgnoreCase(str2.toString());
+ } else {
+ return str1.toString().contentEquals(str2);
+ }
+ }
+
+ /**
+ * 给定字符串是否与提供的中任一字符串相同(忽略大小写),相同则返回{@code true},没有相同的返回{@code false}
+ * 如果参与比对的字符串列表为空,返回{@code false}
+ *
+ * @param str1 给定需要检查的字符串
+ * @param strs 需要参与比对的字符串列表
+ * @return 是否相同
+ * @since 4.3.2
+ */
+ public static boolean equalsAnyIgnoreCase(CharSequence str1, CharSequence... strs) {
+ return equalsAny(str1, true, strs);
+ }
+
+ /**
+ * 给定字符串是否与提供的中任一字符串相同,相同则返回{@code true},没有相同的返回{@code false}
+ * 如果参与比对的字符串列表为空,返回{@code false}
+ *
+ * @param str1 给定需要检查的字符串
+ * @param strs 需要参与比对的字符串列表
+ * @return 是否相同
+ * @since 4.3.2
+ */
+ public static boolean equalsAny(CharSequence str1, CharSequence... strs) {
+ return equalsAny(str1, false, strs);
+ }
+
+ /**
+ * 给定字符串是否与提供的中任一字符串相同,相同则返回{@code true},没有相同的返回{@code false}
+ * 如果参与比对的字符串列表为空,返回{@code false}
+ *
+ * @param str1 给定需要检查的字符串
+ * @param ignoreCase 是否忽略大小写
+ * @param strs 需要参与比对的字符串列表
+ * @return 是否相同
+ * @since 4.3.2
+ */
+ public static boolean equalsAny(CharSequence str1, boolean ignoreCase, CharSequence... strs) {
+ if (ArrayUtil.isEmpty(strs)) {
+ return false;
+ }
+
+ for (CharSequence str : strs) {
+ if (equals(str1, str, ignoreCase)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 字符串指定位置的字符是否与给定字符相同
+ * 如果字符串为null,返回false
+ * 如果给定的位置大于字符串长度,返回false
+ * 如果给定的位置小于0,返回false
+ *
+ * @param str 字符串
+ * @param position 位置
+ * @param c 需要对比的字符
+ * @return 字符串指定位置的字符是否与给定字符相同
+ * @since 3.3.1
+ */
+ public static boolean equalsCharAt(CharSequence str, int position, char c) {
+ if (null == str || position < 0) {
+ return false;
+ }
+ return str.length() > position && c == str.charAt(position);
+ }
+
+ /**
+ * 截取两个字符串的不同部分(长度一致),判断截取的子串是否相同
+ * 任意一个字符串为null返回false
+ *
+ * @param str1 第一个字符串
+ * @param start1 第一个字符串开始的位置
+ * @param str2 第二个字符串
+ * @param start2 第二个字符串开始的位置
+ * @param length 截取长度
+ * @param ignoreCase 是否忽略大小写
+ * @return 子串是否相同
+ * @since 3.2.1
+ */
+ public static boolean isSubEquals(CharSequence str1, int start1, CharSequence str2, int start2, int length, boolean ignoreCase) {
+ if (null == str1 || null == str2) {
+ return false;
+ }
+
+ return str1.toString().regionMatches(ignoreCase, start1, str2.toString(), start2, length);
+ }
+
+ // ------------------------------------------------------------------------ format
+
+ /**
+ * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") =》 this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") =》 this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b
+ *
+ * @param template 文本模板,被替换的部分用 {} 表示,如果模板为null,返回"null"
+ * @param params 参数值
+ * @return 格式化后的文本,如果模板为null,返回"null"
+ */
+ public static String format(CharSequence template, Object... params) {
+ if (null == template) {
+ return NULL;
+ }
+ if (ArrayUtil.isEmpty(params) || isBlank(template)) {
+ return template.toString();
+ }
+ return StrFormatter.format(template.toString(), params);
+ }
+
+ /**
+ * 有序的格式化文本,使用{number}做为占位符
+ * 通常使用:format("this is {0} for {1}", "a", "b") =》 this is a for b
+ *
+ * @param pattern 文本格式
+ * @param arguments 参数
+ * @return 格式化后的文本
+ */
+ public static String indexedFormat(CharSequence pattern, Object... arguments) {
+ return MessageFormat.format(pattern.toString(), arguments);
+ }
+ // ------------------------------------------------------------------------ bytes
+
+ /**
+ * 编码字符串,编码为UTF-8
+ *
+ * @param str 字符串
+ * @return 编码后的字节码
+ */
+ public static byte[] utf8Bytes(CharSequence str) {
+ return bytes(str, CharsetUtil.CHARSET_UTF_8);
+ }
+
+ /**
+ * 编码字符串
+ * 使用系统默认编码
+ *
+ * @param str 字符串
+ * @return 编码后的字节码
+ */
+ public static byte[] bytes(CharSequence str) {
+ return bytes(str, Charset.defaultCharset());
+ }
+
+ /**
+ * 编码字符串
+ *
+ * @param str 字符串
+ * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+ * @return 编码后的字节码
+ */
+ public static byte[] bytes(CharSequence str, String charset) {
+ return bytes(str, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+ }
+
+ /**
+ * 编码字符串
+ *
+ * @param str 字符串
+ * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+ * @return 编码后的字节码
+ */
+ public static byte[] bytes(CharSequence str, Charset charset) {
+ if (str == null) {
+ return null;
+ }
+
+ if (null == charset) {
+ return str.toString().getBytes();
+ }
+ return str.toString().getBytes(charset);
+ }
+
+ /**
+ * 字符串转换为byteBuffer
+ *
+ * @param str 字符串
+ * @param charset 编码
+ * @return byteBuffer
+ */
+ public static ByteBuffer byteBuffer(CharSequence str, String charset) {
+ return ByteBuffer.wrap(bytes(str, charset));
+ }
+
+ // ------------------------------------------------------------------------ wrap
+
+ /**
+ * 包装指定字符串
+ * 当前缀和后缀一致时使用此方法
+ *
+ * @param str 被包装的字符串
+ * @param prefixAndSuffix 前缀和后缀
+ * @return 包装后的字符串
+ * @since 3.1.0
+ */
+ public static String wrap(CharSequence str, CharSequence prefixAndSuffix) {
+ return wrap(str, prefixAndSuffix, prefixAndSuffix);
+ }
+
+ /**
+ * 包装指定字符串
+ *
+ * @param str 被包装的字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 包装后的字符串
+ */
+ public static String wrap(CharSequence str, CharSequence prefix, CharSequence suffix) {
+ return nullToEmpty(prefix).concat(nullToEmpty(str)).concat(nullToEmpty(suffix));
+ }
+
+ /**
+ * 使用单个字符包装多个字符串
+ *
+ * @param prefixAndSuffix 前缀和后缀
+ * @param strs 多个字符串
+ * @return 包装的字符串数组
+ * @since 5.4.1
+ */
+ public static String[] wrapAllWithPair(CharSequence prefixAndSuffix, CharSequence... strs) {
+ return wrapAll(prefixAndSuffix, prefixAndSuffix, strs);
+ }
+
+ /**
+ * 包装多个字符串
+ *
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @param strs 多个字符串
+ * @return 包装的字符串数组
+ * @since 4.0.7
+ */
+ public static String[] wrapAll(CharSequence prefix, CharSequence suffix, CharSequence... strs) {
+ final String[] results = new String[strs.length];
+ for (int i = 0; i < strs.length; i++) {
+ results[i] = wrap(strs[i], prefix, suffix);
+ }
+ return results;
+ }
+
+ /**
+ * 包装指定字符串,如果前缀或后缀已经包含对应的字符串,则不再包装
+ *
+ * @param str 被包装的字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 包装后的字符串
+ */
+ public static String wrapIfMissing(CharSequence str, CharSequence prefix, CharSequence suffix) {
+ int len = 0;
+ if (isNotEmpty(str)) {
+ len += str.length();
+ }
+ if (isNotEmpty(prefix)) {
+ len += str.length();
+ }
+ if (isNotEmpty(suffix)) {
+ len += str.length();
+ }
+ StringBuilder sb = new StringBuilder(len);
+ if (isNotEmpty(prefix) && false == startWith(str, prefix)) {
+ sb.append(prefix);
+ }
+ if (isNotEmpty(str)) {
+ sb.append(str);
+ }
+ if (isNotEmpty(suffix) && false == endWith(str, suffix)) {
+ sb.append(suffix);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 使用成对的字符包装多个字符串,如果已经包装,则不再包装
+ *
+ * @param prefixAndSuffix 前缀和后缀
+ * @param strs 多个字符串
+ * @return 包装的字符串数组
+ * @since 5.4.1
+ */
+ public static String[] wrapAllWithPairIfMissing(CharSequence prefixAndSuffix, CharSequence... strs) {
+ return wrapAllIfMissing(prefixAndSuffix, prefixAndSuffix, strs);
+ }
+
+ /**
+ * 包装多个字符串,如果已经包装,则不再包装
+ *
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @param strs 多个字符串
+ * @return 包装的字符串数组
+ * @since 4.0.7
+ */
+ public static String[] wrapAllIfMissing(CharSequence prefix, CharSequence suffix, CharSequence... strs) {
+ final String[] results = new String[strs.length];
+ for (int i = 0; i < strs.length; i++) {
+ results[i] = wrapIfMissing(strs[i], prefix, suffix);
+ }
+ return results;
+ }
+
+ /**
+ * 去掉字符包装,如果未被包装则返回原字符串
+ *
+ * @param str 字符串
+ * @param prefix 前置字符串
+ * @param suffix 后置字符串
+ * @return 去掉包装字符的字符串
+ * @since 4.0.1
+ */
+ public static String unWrap(CharSequence str, String prefix, String suffix) {
+ if (isWrap(str, prefix, suffix)) {
+ return sub(str, prefix.length(), str.length() - suffix.length());
+ }
+ return str.toString();
+ }
+
+ /**
+ * 去掉字符包装,如果未被包装则返回原字符串
+ *
+ * @param str 字符串
+ * @param prefix 前置字符
+ * @param suffix 后置字符
+ * @return 去掉包装字符的字符串
+ * @since 4.0.1
+ */
+ public static String unWrap(CharSequence str, char prefix, char suffix) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+ if (str.charAt(0) == prefix && str.charAt(str.length() - 1) == suffix) {
+ return sub(str, 1, str.length() - 1);
+ }
+ return str.toString();
+ }
+
+ /**
+ * 去掉字符包装,如果未被包装则返回原字符串
+ *
+ * @param str 字符串
+ * @param prefixAndSuffix 前置和后置字符
+ * @return 去掉包装字符的字符串
+ * @since 4.0.1
+ */
+ public static String unWrap(CharSequence str, char prefixAndSuffix) {
+ return unWrap(str, prefixAndSuffix, prefixAndSuffix);
+ }
+
+ /**
+ * 指定字符串是否被包装
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 是否被包装
+ */
+ public static boolean isWrap(CharSequence str, String prefix, String suffix) {
+ if (ArrayUtil.hasNull(str, prefix, suffix)) {
+ return false;
+ }
+ final String str2 = str.toString();
+ return str2.startsWith(prefix) && str2.endsWith(suffix);
+ }
+
+ /**
+ * 指定字符串是否被同一字符包装(前后都有这些字符串)
+ *
+ * @param str 字符串
+ * @param wrapper 包装字符串
+ * @return 是否被包装
+ */
+ public static boolean isWrap(CharSequence str, String wrapper) {
+ return isWrap(str, wrapper, wrapper);
+ }
+
+ /**
+ * 指定字符串是否被同一字符包装(前后都有这些字符串)
+ *
+ * @param str 字符串
+ * @param wrapper 包装字符
+ * @return 是否被包装
+ */
+ public static boolean isWrap(CharSequence str, char wrapper) {
+ return isWrap(str, wrapper, wrapper);
+ }
+
+ /**
+ * 指定字符串是否被包装
+ *
+ * @param str 字符串
+ * @param prefixChar 前缀
+ * @param suffixChar 后缀
+ * @return 是否被包装
+ */
+ public static boolean isWrap(CharSequence str, char prefixChar, char suffixChar) {
+ if (null == str) {
+ return false;
+ }
+
+ return str.charAt(0) == prefixChar && str.charAt(str.length() - 1) == suffixChar;
+ }
+
+ // ------------------------------------------------------------------------ pad
+
+ /**
+ * 补充字符串以满足最小长度
+ *
+ *
+ * StrUtil.padPre(null, *, *);//null
+ * StrUtil.padPre("1", 3, "ABC");//"AB1"
+ * StrUtil.padPre("123", 2, "ABC");//"12"
+ *
+ *
+ * @param str 字符串
+ * @param minLength 最小长度
+ * @param padStr 补充的字符
+ * @return 补充后的字符串
+ */
+ public static String padPre(CharSequence str, int minLength, CharSequence padStr) {
+ if (null == str) {
+ return null;
+ }
+ final int strLen = str.length();
+ if (strLen == minLength) {
+ return str.toString();
+ } else if (strLen > minLength) {
+ return subPre(str, minLength);
+ }
+
+ return repeatByLength(padStr, minLength - strLen).concat(str.toString());
+ }
+
+ /**
+ * 补充字符串以满足最小长度
+ *
+ *
+ * StrUtil.padPre(null, *, *);//null
+ * StrUtil.padPre("1", 3, '0');//"001"
+ * StrUtil.padPre("123", 2, '0');//"12"
+ *
+ *
+ * @param str 字符串
+ * @param minLength 最小长度
+ * @param padChar 补充的字符
+ * @return 补充后的字符串
+ */
+ public static String padPre(CharSequence str, int minLength, char padChar) {
+ if (null == str) {
+ return null;
+ }
+ final int strLen = str.length();
+ if (strLen == minLength) {
+ return str.toString();
+ } else if (strLen > minLength) {
+ return subPre(str, minLength);
+ }
+
+ return repeat(padChar, minLength - strLen).concat(str.toString());
+ }
+
+ /**
+ * 补充字符串以满足最小长度
+ *
+ *
+ * StrUtil.padAfter(null, *, *);//null
+ * StrUtil.padAfter("1", 3, '0');//"100"
+ * StrUtil.padAfter("123", 2, '0');//"23"
+ *
+ *
+ * @param str 字符串,如果为{@code null},直接返回null
+ * @param minLength 最小长度
+ * @param padChar 补充的字符
+ * @return 补充后的字符串
+ */
+ public static String padAfter(CharSequence str, int minLength, char padChar) {
+ if (null == str) {
+ return null;
+ }
+ final int strLen = str.length();
+ if (strLen == minLength) {
+ return str.toString();
+ } else if (strLen > minLength) {
+ return sub(str, strLen - minLength, strLen);
+ }
+
+ return str.toString().concat(repeat(padChar, minLength - strLen));
+ }
+
+ /**
+ * 补充字符串以满足最小长度
+ *
+ *
+ * StrUtil.padAfter(null, *, *);//null
+ * StrUtil.padAfter("1", 3, "ABC");//"1AB"
+ * StrUtil.padAfter("123", 2, "ABC");//"23"
+ *
+ *
+ * @param str 字符串,如果为{@code null},直接返回null
+ * @param minLength 最小长度
+ * @param padStr 补充的字符
+ * @return 补充后的字符串
+ * @since 4.3.2
+ */
+ public static String padAfter(CharSequence str, int minLength, CharSequence padStr) {
+ if (null == str) {
+ return null;
+ }
+ final int strLen = str.length();
+ if (strLen == minLength) {
+ return str.toString();
+ } else if (strLen > minLength) {
+ return subSufByLength(str, minLength);
+ }
+
+ return str.toString().concat(repeatByLength(padStr, minLength - strLen));
+ }
+
+ // ------------------------------------------------------------------------ center
+
+ /**
+ * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
+ *
+ *
+ * StrUtil.center(null, *) = null
+ * StrUtil.center("", 4) = " "
+ * StrUtil.center("ab", -1) = "ab"
+ * StrUtil.center("ab", 4) = " ab "
+ * StrUtil.center("abcd", 2) = "abcd"
+ * StrUtil.center("a", 4) = " a "
+ *
+ *
+ * @param str 字符串
+ * @param size 指定长度
+ * @return 补充后的字符串
+ * @since 4.3.2
+ */
+ public static String center(CharSequence str, final int size) {
+ return center(str, size, CharUtil.SPACE);
+ }
+
+ /**
+ * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
+ *
+ *
+ * StrUtil.center(null, *, *) = null
+ * StrUtil.center("", 4, ' ') = " "
+ * StrUtil.center("ab", -1, ' ') = "ab"
+ * StrUtil.center("ab", 4, ' ') = " ab "
+ * StrUtil.center("abcd", 2, ' ') = "abcd"
+ * StrUtil.center("a", 4, ' ') = " a "
+ * StrUtil.center("a", 4, 'y') = "yayy"
+ * StrUtil.center("abc", 7, ' ') = " abc "
+ *
+ *
+ * @param str 字符串
+ * @param size 指定长度
+ * @param padChar 两边补充的字符
+ * @return 补充后的字符串
+ * @since 4.3.2
+ */
+ public static String center(CharSequence str, final int size, char padChar) {
+ if (str == null || size <= 0) {
+ return str(str);
+ }
+ final int strLen = str.length();
+ final int pads = size - strLen;
+ if (pads <= 0) {
+ return str.toString();
+ }
+ str = padPre(str, strLen + pads / 2, padChar);
+ str = padAfter(str, size, padChar);
+ return str.toString();
+ }
+
+ /**
+ * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
+ *
+ *
+ * StrUtil.center(null, *, *) = null
+ * StrUtil.center("", 4, " ") = " "
+ * StrUtil.center("ab", -1, " ") = "ab"
+ * StrUtil.center("ab", 4, " ") = " ab "
+ * StrUtil.center("abcd", 2, " ") = "abcd"
+ * StrUtil.center("a", 4, " ") = " a "
+ * StrUtil.center("a", 4, "yz") = "yayz"
+ * StrUtil.center("abc", 7, null) = " abc "
+ * StrUtil.center("abc", 7, "") = " abc "
+ *
+ *
+ * @param str 字符串
+ * @param size 指定长度
+ * @param padStr 两边补充的字符串
+ * @return 补充后的字符串
+ */
+ public static String center(CharSequence str, final int size, CharSequence padStr) {
+ if (str == null || size <= 0) {
+ return str(str);
+ }
+ if (isEmpty(padStr)) {
+ padStr = SPACE;
+ }
+ final int strLen = str.length();
+ final int pads = size - strLen;
+ if (pads <= 0) {
+ return str.toString();
+ }
+ str = padPre(str, strLen + pads / 2, padStr);
+ str = padAfter(str, size, padStr);
+ return str.toString();
+ }
+
+ // ------------------------------------------------------------------------ str
+
+ /**
+ * {@link CharSequence} 转为字符串,null安全
+ *
+ * @param cs {@link CharSequence}
+ * @return 字符串
+ */
+ public static String str(CharSequence cs) {
+ return null == cs ? null : cs.toString();
+ }
+
+ // ------------------------------------------------------------------------ count
+
+ /**
+ * 统计指定内容中包含指定字符串的数量
+ * 参数为 {@code null} 或者 "" 返回 {@code 0}.
+ *
+ *
+ * StrUtil.count(null, *) = 0
+ * StrUtil.count("", *) = 0
+ * StrUtil.count("abba", null) = 0
+ * StrUtil.count("abba", "") = 0
+ * StrUtil.count("abba", "a") = 2
+ * StrUtil.count("abba", "ab") = 1
+ * StrUtil.count("abba", "xxx") = 0
+ *
+ *
+ * @param content 被查找的字符串
+ * @param strForSearch 需要查找的字符串
+ * @return 查找到的个数
+ */
+ public static int count(CharSequence content, CharSequence strForSearch) {
+ if (hasEmpty(content, strForSearch) || strForSearch.length() > content.length()) {
+ return 0;
+ }
+
+ int count = 0;
+ int idx = 0;
+ final String content2 = content.toString();
+ final String strForSearch2 = strForSearch.toString();
+ while ((idx = content2.indexOf(strForSearch2, idx)) > -1) {
+ count++;
+ idx += strForSearch.length();
+ }
+ return count;
+ }
+
+ /**
+ * 统计指定内容中包含指定字符的数量
+ *
+ * @param content 内容
+ * @param charForSearch 被统计的字符
+ * @return 包含数量
+ */
+ public static int count(CharSequence content, char charForSearch) {
+ int count = 0;
+ if (isEmpty(content)) {
+ return 0;
+ }
+ int contentLength = content.length();
+ for (int i = 0; i < contentLength; i++) {
+ if (charForSearch == content.charAt(i)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ // ------------------------------------------------------------------------ compare
+
+ /**
+ * 比较两个字符串,用于排序
+ *
+ *
+ * StrUtil.compare(null, null, *) = 0
+ * StrUtil.compare(null , "a", true) < 0
+ * StrUtil.compare(null , "a", false) > 0
+ * StrUtil.compare("a", null, true) > 0
+ * StrUtil.compare("a", null, false) < 0
+ * StrUtil.compare("abc", "abc", *) = 0
+ * StrUtil.compare("a", "b", *) < 0
+ * StrUtil.compare("b", "a", *) > 0
+ * StrUtil.compare("a", "B", *) > 0
+ * StrUtil.compare("ab", "abc", *) < 0
+ *
+ *
+ * @param str1 字符串1
+ * @param str2 字符串2
+ * @param nullIsLess {@code null} 值是否排在前(null是否小于非空值)
+ * @return 排序值。负数:str1 < str2,正数:str1 > str2, 0:str1 == str2
+ */
+ public static int compare(final CharSequence str1, final CharSequence str2, final boolean nullIsLess) {
+ if (str1 == str2) {
+ return 0;
+ }
+ if (str1 == null) {
+ return nullIsLess ? -1 : 1;
+ }
+ if (str2 == null) {
+ return nullIsLess ? 1 : -1;
+ }
+ return str1.toString().compareTo(str2.toString());
+ }
+
+ /**
+ * 比较两个字符串,用于排序,大小写不敏感
+ *
+ *
+ * StrUtil.compareIgnoreCase(null, null, *) = 0
+ * StrUtil.compareIgnoreCase(null , "a", true) < 0
+ * StrUtil.compareIgnoreCase(null , "a", false) > 0
+ * StrUtil.compareIgnoreCase("a", null, true) > 0
+ * StrUtil.compareIgnoreCase("a", null, false) < 0
+ * StrUtil.compareIgnoreCase("abc", "abc", *) = 0
+ * StrUtil.compareIgnoreCase("abc", "ABC", *) = 0
+ * StrUtil.compareIgnoreCase("a", "b", *) < 0
+ * StrUtil.compareIgnoreCase("b", "a", *) > 0
+ * StrUtil.compareIgnoreCase("a", "B", *) < 0
+ * StrUtil.compareIgnoreCase("A", "b", *) < 0
+ * StrUtil.compareIgnoreCase("ab", "abc", *) < 0
+ *
+ *
+ * @param str1 字符串1
+ * @param str2 字符串2
+ * @param nullIsLess {@code null} 值是否排在前(null是否小于非空值)
+ * @return 排序值。负数:str1 < str2,正数:str1 > str2, 0:str1 == str2
+ */
+ public static int compareIgnoreCase(CharSequence str1, CharSequence str2, boolean nullIsLess) {
+ if (str1 == str2) {
+ return 0;
+ }
+ if (str1 == null) {
+ return nullIsLess ? -1 : 1;
+ }
+ if (str2 == null) {
+ return nullIsLess ? 1 : -1;
+ }
+ return str1.toString().compareToIgnoreCase(str2.toString());
+ }
+
+ /**
+ * 比较两个版本
+ * null版本排在最小:即:
+ *
+ *
+ * StrUtil.compareVersion(null, "v1") < 0
+ * StrUtil.compareVersion("v1", "v1") = 0
+ * StrUtil.compareVersion(null, null) = 0
+ * StrUtil.compareVersion("v1", null) > 0
+ * StrUtil.compareVersion("1.0.0", "1.0.2") < 0
+ * StrUtil.compareVersion("1.0.2", "1.0.2a") < 0
+ * StrUtil.compareVersion("1.13.0", "1.12.1c") > 0
+ * StrUtil.compareVersion("V0.0.20170102", "V0.0.20170101") > 0
+ *
+ *
+ * @param version1 版本1
+ * @param version2 版本2
+ * @return 排序值。负数:version1 < version2,正数:version1 > version2, 0:version1 == version2
+ * @since 4.0.2
+ */
+ public static int compareVersion(CharSequence version1, CharSequence version2) {
+ return VersionComparator.INSTANCE.compare(str(version1), str(version2));
+ }
+
+ // ------------------------------------------------------------------------ append and prepend
+
+ /**
+ * 如果给定字符串不是以给定的一个或多个字符串为结尾,则在尾部添加结尾字符串
+ * 不忽略大小写
+ *
+ * @param str 被检查的字符串
+ * @param suffix 需要添加到结尾的字符串
+ * @param suffixes 需要额外检查的结尾字符串,如果以这些中的一个为结尾,则不再添加
+ * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
+ * @since 3.0.7
+ */
+ public static String appendIfMissing(final CharSequence str, final CharSequence suffix, final CharSequence... suffixes) {
+ return appendIfMissing(str, suffix, false, suffixes);
+ }
+
+ /**
+ * 如果给定字符串不是以给定的一个或多个字符串为结尾,则在尾部添加结尾字符串
+ * 忽略大小写
+ *
+ * @param str 被检查的字符串
+ * @param suffix 需要添加到结尾的字符串
+ * @param suffixes 需要额外检查的结尾字符串,如果以这些中的一个为结尾,则不再添加
+ * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
+ * @since 3.0.7
+ */
+ public static String appendIfMissingIgnoreCase(final CharSequence str, final CharSequence suffix, final CharSequence... suffixes) {
+ return appendIfMissing(str, suffix, true, suffixes);
+ }
+
+ /**
+ * 如果给定字符串不是以给定的一个或多个字符串为结尾,则在尾部添加结尾字符串
+ *
+ * @param str 被检查的字符串
+ * @param suffix 需要添加到结尾的字符串
+ * @param ignoreCase 检查结尾时是否忽略大小写
+ * @param suffixes 需要额外检查的结尾字符串,如果以这些中的一个为结尾,则不再添加
+ * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
+ * @since 3.0.7
+ */
+ public static String appendIfMissing(final CharSequence str, final CharSequence suffix, final boolean ignoreCase, final CharSequence... suffixes) {
+ if (str == null || isEmpty(suffix) || endWith(str, suffix, ignoreCase)) {
+ return str(str);
+ }
+ if (suffixes != null && suffixes.length > 0) {
+ for (final CharSequence s : suffixes) {
+ if (endWith(str, s, ignoreCase)) {
+ return str.toString();
+ }
+ }
+ }
+ return str.toString().concat(suffix.toString());
+ }
+
+ /**
+ * 如果给定字符串不是以给定的一个或多个字符串为开头,则在首部添加起始字符串
+ * 不忽略大小写
+ *
+ * @param str 被检查的字符串
+ * @param prefix 需要添加到首部的字符串
+ * @param prefixes 需要额外检查的首部字符串,如果以这些中的一个为起始,则不再添加
+ * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
+ * @since 3.0.7
+ */
+ public static String prependIfMissing(final CharSequence str, final CharSequence prefix, final CharSequence... prefixes) {
+ return prependIfMissing(str, prefix, false, prefixes);
+ }
+
+ /**
+ * 如果给定字符串不是以给定的一个或多个字符串为开头,则在首部添加起始字符串
+ * 忽略大小写
+ *
+ * @param str 被检查的字符串
+ * @param prefix 需要添加到首部的字符串
+ * @param prefixes 需要额外检查的首部字符串,如果以这些中的一个为起始,则不再添加
+ * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
+ * @since 3.0.7
+ */
+ public static String prependIfMissingIgnoreCase(final CharSequence str, final CharSequence prefix, final CharSequence... prefixes) {
+ return prependIfMissing(str, prefix, true, prefixes);
+ }
+
+ /**
+ * 如果给定字符串不是以给定的一个或多个字符串为开头,则在首部添加起始字符串
+ *
+ * @param str 被检查的字符串
+ * @param prefix 需要添加到首部的字符串
+ * @param ignoreCase 检查结尾时是否忽略大小写
+ * @param prefixes 需要额外检查的首部字符串,如果以这些中的一个为起始,则不再添加
+ * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
+ * @since 3.0.7
+ */
+ public static String prependIfMissing(final CharSequence str, final CharSequence prefix, final boolean ignoreCase, final CharSequence... prefixes) {
+ if (str == null || isEmpty(prefix) || startWith(str, prefix, ignoreCase)) {
+ return str(str);
+ }
+ if (prefixes != null && prefixes.length > 0) {
+ for (final CharSequence s : prefixes) {
+ if (startWith(str, s, ignoreCase)) {
+ return str.toString();
+ }
+ }
+ }
+ return prefix.toString().concat(str.toString());
+ }
+
+ // ------------------------------------------------------------------------ replace
+
+ /**
+ * 替换字符串中的指定字符串,忽略大小写
+ *
+ * @param str 字符串
+ * @param searchStr 被查找的字符串
+ * @param replacement 被替换的字符串
+ * @return 替换后的字符串
+ * @since 4.0.3
+ */
+ public static String replaceIgnoreCase(CharSequence str, CharSequence searchStr, CharSequence replacement) {
+ return replace(str, 0, searchStr, replacement, true);
+ }
+
+ /**
+ * 替换字符串中的指定字符串
+ *
+ * @param str 字符串
+ * @param searchStr 被查找的字符串
+ * @param replacement 被替换的字符串
+ * @return 替换后的字符串
+ * @since 4.0.3
+ */
+ public static String replace(CharSequence str, CharSequence searchStr, CharSequence replacement) {
+ return replace(str, 0, searchStr, replacement, false);
+ }
+
+ /**
+ * 替换字符串中的指定字符串
+ *
+ * @param str 字符串
+ * @param searchStr 被查找的字符串
+ * @param replacement 被替换的字符串
+ * @param ignoreCase 是否忽略大小写
+ * @return 替换后的字符串
+ * @since 4.0.3
+ */
+ public static String replace(CharSequence str, CharSequence searchStr, CharSequence replacement, boolean ignoreCase) {
+ return replace(str, 0, searchStr, replacement, ignoreCase);
+ }
+
+ /**
+ * 替换字符串中的指定字符串
+ *
+ * @param str 字符串
+ * @param fromIndex 开始位置(包括)
+ * @param searchStr 被查找的字符串
+ * @param replacement 被替换的字符串
+ * @param ignoreCase 是否忽略大小写
+ * @return 替换后的字符串
+ * @since 4.0.3
+ */
+ public static String replace(CharSequence str, int fromIndex, CharSequence searchStr, CharSequence replacement, boolean ignoreCase) {
+ if (isEmpty(str) || isEmpty(searchStr)) {
+ return str(str);
+ }
+ if (null == replacement) {
+ replacement = EMPTY;
+ }
+
+ final int strLength = str.length();
+ final int searchStrLength = searchStr.length();
+ if (fromIndex > strLength) {
+ return str(str);
+ } else if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+
+ final StrBuilder result = StrBuilder.create(strLength + 16);
+ if (0 != fromIndex) {
+ result.append(str.subSequence(0, fromIndex));
+ }
+
+ int preIndex = fromIndex;
+ int index;
+ while ((index = indexOf(str, searchStr, preIndex, ignoreCase)) > -1) {
+ result.append(str.subSequence(preIndex, index));
+ result.append(replacement);
+ preIndex = index + searchStrLength;
+ }
+
+ if (preIndex < strLength) {
+ // 结尾部分
+ result.append(str.subSequence(preIndex, strLength));
+ }
+ return result.toString();
+ }
+
+ /**
+ * 替换指定字符串的指定区间内字符为固定字符
+ *
+ * @param str 字符串
+ * @param startInclude 开始位置(包含)
+ * @param endExclude 结束位置(不包含)
+ * @param replacedChar 被替换的字符
+ * @return 替换后的字符串
+ * @since 3.2.1
+ */
+ public static String replace(CharSequence str, int startInclude, int endExclude, char replacedChar) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+ final int strLength = str.length();
+ if (startInclude > strLength) {
+ return str(str);
+ }
+ if (endExclude > strLength) {
+ endExclude = strLength;
+ }
+ if (startInclude > endExclude) {
+ // 如果起始位置大于结束位置,不替换
+ return str(str);
+ }
+
+ final char[] chars = new char[strLength];
+ for (int i = 0; i < strLength; i++) {
+ if (i >= startInclude && i < endExclude) {
+ chars[i] = replacedChar;
+ } else {
+ chars[i] = str.charAt(i);
+ }
+ }
+ return new String(chars);
+ }
+
+ /**
+ * 替换所有正则匹配的文本,并使用自定义函数决定如何替换
+ *
+ * @param str 要替换的字符串
+ * @param pattern 用于匹配的正则式
+ * @param replaceFun 决定如何替换的函数
+ * @return 替换后的字符串
+ * @see ReUtil#replaceAll(CharSequence, java.util.regex.Pattern, Func1)
+ * @since 4.2.2
+ */
+ public static String replace(CharSequence str, java.util.regex.Pattern pattern, Func1
+ * 提供的chars为所有需要被替换的字符,例如:"\r\n",则"\r"和"\n"都会被替换,哪怕他们单独存在
+ *
+ * @param str 被检查的字符串
+ * @param chars 需要替换的字符列表,用一个字符串表示这个字符列表
+ * @param replacedStr 替换成的字符串
+ * @return 新字符串
+ * @since 3.2.2
+ */
+ public static String replaceChars(CharSequence str, String chars, CharSequence replacedStr) {
+ if (isEmpty(str) || isEmpty(chars)) {
+ return str(str);
+ }
+ return replaceChars(str, chars.toCharArray(), replacedStr);
+ }
+
+ /**
+ * 替换字符字符数组中所有的字符为replacedStr
+ *
+ * @param str 被检查的字符串
+ * @param chars 需要替换的字符列表
+ * @param replacedStr 替换成的字符串
+ * @return 新字符串
+ * @since 3.2.2
+ */
+ public static String replaceChars(CharSequence str, char[] chars, CharSequence replacedStr) {
+ if (isEmpty(str) || ArrayUtil.isEmpty(chars)) {
+ return str(str);
+ }
+
+ final Set
+ * null字符长度定义为0
+ *
+ * @param strs 字符串数组
+ * @return 总长度
+ * @since 4.0.1
+ */
+ public static int totalLength(CharSequence... strs) {
+ int totalLength = 0;
+ for (CharSequence str : strs) {
+ totalLength += (null == str ? 0 : str.length());
+ }
+ return totalLength;
+ }
+
+ /**
+ * 限制字符串长度,如果超过指定长度,截取指定长度并在末尾加"..."
+ *
+ * @param string 字符串
+ * @param length 最大长度
+ * @return 切割后的剩余的前半部分字符串+"..."
+ * @since 4.0.10
+ */
+ public static String maxLength(CharSequence string, int length) {
+ Assert.isTrue(length > 0);
+ if (null == string) {
+ return null;
+ }
+ if (string.length() <= length) {
+ return string.toString();
+ }
+ return sub(string, 0, length) + "...";
+ }
+
+ // ------------------------------------------------------------------------ firstXXX
+
+ /**
+ * 返回第一个非{@code null} 元素
+ *
+ * @param strs 多个元素
+ * @param
+ * 例如:str = name, return Name
+ *
+ * @param str 字符串
+ * @return 字符串
+ */
+ public static String upperFirst(CharSequence str) {
+ if (null == str) {
+ return null;
+ }
+ if (str.length() > 0) {
+ char firstChar = str.charAt(0);
+ if (Character.isLowerCase(firstChar)) {
+ return Character.toUpperCase(firstChar) + subSuf(str, 1);
+ }
+ }
+ return str.toString();
+ }
+
+ /**
+ * 小写首字母
+ * 例如:str = Name, return name
+ *
+ * @param str 字符串
+ * @return 字符串
+ */
+ public static String lowerFirst(CharSequence str) {
+ if (null == str) {
+ return null;
+ }
+ if (str.length() > 0) {
+ char firstChar = str.charAt(0);
+ if (Character.isUpperCase(firstChar)) {
+ return Character.toLowerCase(firstChar) + subSuf(str, 1);
+ }
+ }
+ return str.toString();
+ }
+
+ // ------------------------------------------------------------------------ filter
+
+ /**
+ * 过滤字符串
+ *
+ * @param str 字符串
+ * @param filter 过滤器
+ * @return 过滤后的字符串
+ * @since 5.4.0
+ */
+ public static String filter(CharSequence str, final Filter
+ * 1. 大写字母包括A-Z
+ * 2. 其它非字母的Unicode符都算作大写
+ *
+ *
+ * @param str 被检查的字符串
+ * @return 是否全部为大写
+ * @since 4.2.2
+ */
+ public static boolean isUpperCase(CharSequence str) {
+ if (null == str) {
+ return false;
+ }
+ final int len = str.length();
+ for (int i = 0; i < len; i++) {
+ if (Character.isLowerCase(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 给定字符串中的字母是否全部为小写,判断依据如下:
+ *
+ *
+ * 1. 小写字母包括a-z
+ * 2. 其它非字母的Unicode符都算作小写
+ *
+ *
+ * @param str 被检查的字符串
+ * @return 是否全部为小写
+ * @since 4.2.2
+ */
+ public static boolean isLowerCase(CharSequence str) {
+ if (null == str) {
+ return false;
+ }
+ final int len = str.length();
+ for (int i = 0; i < len; i++) {
+ if (Character.isUpperCase(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 切换给定字符串中的大小写。大写转小写,小写转大写。
+ *
+ *
+ * StrUtil.swapCase(null) = null
+ * StrUtil.swapCase("") = ""
+ * StrUtil.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
+ *
+ *
+ * @param str 字符串
+ * @return 交换后的字符串
+ * @since 4.3.2
+ */
+ public static String swapCase(final String str) {
+ if (isEmpty(str)) {
+ return str;
+ }
+
+ final char[] buffer = str.toCharArray();
+
+ for (int i = 0; i < buffer.length; i++) {
+ final char ch = buffer[i];
+ if (Character.isUpperCase(ch)) {
+ buffer[i] = Character.toLowerCase(ch);
+ } else if (Character.isTitleCase(ch)) {
+ buffer[i] = Character.toLowerCase(ch);
+ } else if (Character.isLowerCase(ch)) {
+ buffer[i] = Character.toUpperCase(ch);
+ }
+ }
+ return new String(buffer);
+ }
+
+ /**
+ * 将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
+ * 例如:
+ *
+ *
+ * HelloWorld=》hello_world
+ * Hello_World=》hello_world
+ * HelloWorld_test=》hello_world_test
+ *
+ *
+ * @param str 转换前的驼峰式命名的字符串,也可以为下划线形式
+ * @return 转换后下划线方式命名的字符串
+ */
+ public static String toUnderlineCase(CharSequence str) {
+ return toSymbolCase(str, CharUtil.UNDERLINE);
+ }
+
+ /**
+ * 将驼峰式命名的字符串转换为使用符号连接方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
+ *
+ * @param str 转换前的驼峰式命名的字符串,也可以为符号连接形式
+ * @param symbol 连接符
+ * @return 转换后符号连接方式命名的字符串
+ * @since 4.0.10
+ */
+ public static String toSymbolCase(CharSequence str, char symbol) {
+ if (str == null) {
+ return null;
+ }
+
+ final int length = str.length();
+ final StrBuilder sb = new StrBuilder();
+ char c;
+ for (int i = 0; i < length; i++) {
+ c = str.charAt(i);
+ final Character preChar = (i > 0) ? str.charAt(i - 1) : null;
+ if (Character.isUpperCase(c)) {
+ // 遇到大写字母处理
+ final Character nextChar = (i < str.length() - 1) ? str.charAt(i + 1) : null;
+ if (null != preChar && Character.isUpperCase(preChar)) {
+ // 前一个字符为大写,则按照一个词对待,例如AB
+ sb.append(c);
+ } else if (null != nextChar && (false == Character.isLowerCase(nextChar))) {
+ // 后一个为非小写字母,按照一个词对待
+ if (null != preChar && symbol != preChar) {
+ // 前一个是非大写时按照新词对待,加连接符,例如xAB
+ sb.append(symbol);
+ }
+ sb.append(c);
+ } else {
+ // 前后都为非大写按照新词对待
+ if (null != preChar && symbol != preChar) {
+ // 前一个非连接符,补充连接符
+ sb.append(symbol);
+ }
+ sb.append(Character.toLowerCase(c));
+ }
+ } else {
+ if (symbol != c
+ && sb.length() > 0
+ && Character.isUpperCase(sb.charAt(-1))
+ && Character.isLowerCase(c)) {
+ // 当结果中前一个字母为大写,当前为小写(非数字或字符),说明此字符为新词开始(连接符也表示新词)
+ sb.append(symbol);
+ }
+ // 小写或符号
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world=》helloWorld
+ *
+ * @param name 转换前的下划线大写方式命名的字符串
+ * @return 转换后的驼峰式命名的字符串
+ */
+ public static String toCamelCase(CharSequence name) {
+ if (null == name) {
+ return null;
+ }
+
+ final String name2 = name.toString();
+ if (contains(name2, CharUtil.UNDERLINE)) {
+ final int length = name2.length();
+ final StringBuilder sb = new StringBuilder(length);
+ boolean upperCase = false;
+ for (int i = 0; i < length; i++) {
+ char c = name2.charAt(i);
+
+ if (c == CharUtil.UNDERLINE) {
+ upperCase = true;
+ } else if (upperCase) {
+ sb.append(Character.toUpperCase(c));
+ upperCase = false;
+ } else {
+ sb.append(Character.toLowerCase(c));
+ }
+ }
+ return sb.toString();
+ } else {
+ return name2;
+ }
+ }
+
+ // ------------------------------------------------------------------------ isSurround
+
+ /**
+ * 给定字符串是否被字符包围
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 是否包围,空串不包围
+ */
+ public static boolean isSurround(CharSequence str, CharSequence prefix, CharSequence suffix) {
+ if (StrUtil.isBlank(str)) {
+ return false;
+ }
+ if (str.length() < (prefix.length() + suffix.length())) {
+ return false;
+ }
+
+ final String str2 = str.toString();
+ return str2.startsWith(prefix.toString()) && str2.endsWith(suffix.toString());
+ }
+
+ /**
+ * 给定字符串是否被字符包围
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 是否包围,空串不包围
+ */
+ public static boolean isSurround(CharSequence str, char prefix, char suffix) {
+ if (StrUtil.isBlank(str)) {
+ return false;
+ }
+ if (str.length() < 2) {
+ return false;
+ }
+
+ return str.charAt(0) == prefix && str.charAt(str.length() - 1) == suffix;
+ }
+
+ // ------------------------------------------------------------------------ builder
+
+ /**
+ * 创建StringBuilder对象
+ *
+ * @param strs 初始字符串列表
+ * @return StringBuilder对象
+ */
+ public static StringBuilder builder(CharSequence... strs) {
+ final StringBuilder sb = new StringBuilder();
+ for (CharSequence str : strs) {
+ sb.append(str);
+ }
+ return sb;
+ }
+
+ /**
+ * 创建StrBuilder对象
+ *
+ * @param strs 初始字符串列表
+ * @return StrBuilder对象
+ */
+ public static StrBuilder strBuilder(CharSequence... strs) {
+ return StrBuilder.create(strs);
+ }
+
+ // ------------------------------------------------------------------------ getter and setter
+
+ /**
+ * 获得set或get或is方法对应的标准属性名
+ * 例如:setName 返回 name
+ *
+ *
+ * getName =》name
+ * setName =》name
+ * isName =》name
+ *
+ *
+ * @param getOrSetMethodName Get或Set方法名
+ * @return 如果是set或get方法名,返回field, 否则null
+ */
+ public static String getGeneralField(CharSequence getOrSetMethodName) {
+ final String getOrSetMethodNameStr = getOrSetMethodName.toString();
+ if (getOrSetMethodNameStr.startsWith("get") || getOrSetMethodNameStr.startsWith("set")) {
+ return removePreAndLowerFirst(getOrSetMethodName, 3);
+ } else if (getOrSetMethodNameStr.startsWith("is")) {
+ return removePreAndLowerFirst(getOrSetMethodName, 2);
+ }
+ return null;
+ }
+
+ /**
+ * 生成set方法名
+ * 例如:name 返回 setName
+ *
+ * @param fieldName 属性名
+ * @return setXxx
+ */
+ public static String genSetter(CharSequence fieldName) {
+ return upperFirstAndAddPre(fieldName, "set");
+ }
+
+ /**
+ * 生成get方法名
+ *
+ * @param fieldName 属性名
+ * @return getXxx
+ */
+ public static String genGetter(CharSequence fieldName) {
+ return upperFirstAndAddPre(fieldName, "get");
+ }
+
+ // ------------------------------------------------------------------------ other
+
+ /**
+ * 连接多个字符串为一个
+ *
+ * @param isNullToEmpty 是否null转为""
+ * @param strs 字符串数组
+ * @return 连接后的字符串
+ * @since 4.1.0
+ */
+ public static String concat(boolean isNullToEmpty, CharSequence... strs) {
+ final StrBuilder sb = new StrBuilder();
+ for (CharSequence str : strs) {
+ sb.append(isNullToEmpty ? nullToEmpty(str) : str);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 将给定字符串,变成 "xxx...xxx" 形式的字符串
+ *
+ * @param str 字符串
+ * @param maxLength 最大长度
+ * @return 截取后的字符串
+ */
+ public static String brief(CharSequence str, int maxLength) {
+ if (null == str) {
+ return null;
+ }
+ if (str.length() <= maxLength) {
+ return str.toString();
+ }
+ int w = maxLength / 2;
+ int l = str.length() + 3;
+
+ final String str2 = str.toString();
+ return format("{}...{}", str2.substring(0, maxLength - w), str2.substring(l - w));
+ }
+
+ /**
+ * 以 conjunction 为分隔符将多个对象转换为字符串
+ *
+ * @param conjunction 分隔符
+ * @param objs 数组
+ * @return 连接后的字符串
+ * @see ArrayUtil#join(Object, CharSequence)
+ */
+ public static String join(CharSequence conjunction, Object... objs) {
+ return ArrayUtil.join(objs, conjunction);
+ }
+
+ /**
+ * 字符串的每一个字符是否都与定义的匹配器匹配
+ *
+ * @param value 字符串
+ * @param matcher 匹配器
+ * @return 是否全部匹配
+ * @since 3.2.3
+ */
+ public static boolean isAllCharMatch(CharSequence value, cn.hutool.core.lang.Matcher
+ * 当moveLength大于0向右位移,小于0向左位移,0不位移
+ * 当moveLength大于字符串长度时采取循环位移策略,即位移到头后从头(尾)位移,例如长度为10,位移13则表示位移3
+ *
+ * @param str 字符串
+ * @param startInclude 起始位置(包括)
+ * @param endExclude 结束位置(不包括)
+ * @param moveLength 移动距离,负数表示左移,正数为右移
+ * @return 位移后的字符串
+ * @since 4.0.7
+ */
+ public static String move(CharSequence str, int startInclude, int endExclude, int moveLength) {
+ if (isEmpty(str)) {
+ return str(str);
+ }
+ int len = str.length();
+ if (Math.abs(moveLength) > len) {
+ // 循环位移,当越界时循环
+ moveLength = moveLength % len;
+ }
+ final StrBuilder strBuilder = StrBuilder.create(len);
+ if (moveLength > 0) {
+ int endAfterMove = Math.min(endExclude + moveLength, str.length());
+ strBuilder.append(str.subSequence(0, startInclude))//
+ .append(str.subSequence(endExclude, endAfterMove))//
+ .append(str.subSequence(startInclude, endExclude))//
+ .append(str.subSequence(endAfterMove, str.length()));
+ } else if (moveLength < 0) {
+ int startAfterMove = Math.max(startInclude + moveLength, 0);
+ strBuilder.append(str.subSequence(0, startAfterMove))//
+ .append(str.subSequence(startInclude, endExclude))//
+ .append(str.subSequence(startAfterMove, startInclude))//
+ .append(str.subSequence(endExclude, str.length()));
+ } else {
+ return str(str);
+ }
+ return strBuilder.toString();
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java
index dae62696b..94aa03ce5 100644
--- a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java
@@ -239,7 +239,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
*/
public StrBuilder insert(int index, CharSequence csq) {
if (null == csq) {
- csq = "null";
+ csq = StrUtil.EMPTY;
}
int len = csq.length();
moveDataAfterIndex(index, csq.length());
@@ -523,7 +523,8 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
* @param minimumCapacity 最小容量
*/
private void ensureCapacity(int minimumCapacity) {
- if (minimumCapacity > value.length) {
+ // overflow-conscious code
+ if (minimumCapacity - value.length > 0) {
expandCapacity(minimumCapacity);
}
}
@@ -535,8 +536,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
* @param minimumCapacity 需要扩展的最小容量
*/
private void expandCapacity(int minimumCapacity) {
- int newCapacity = value.length * 2 + 2;
- if (newCapacity < minimumCapacity) {
+ int newCapacity = (value.length << 1) + 2;
+ // overflow-conscious code
+ if (newCapacity - minimumCapacity < 0) {
newCapacity = minimumCapacity;
}
if (newCapacity < 0) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/IdcardUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/IdcardUtil.java
index 7fdfb75e5..af9dc87de 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/IdcardUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/IdcardUtil.java
@@ -83,6 +83,8 @@ public class IdcardUtil {
CITY_CODES.put("71", "台湾");
CITY_CODES.put("81", "香港");
CITY_CODES.put("82", "澳门");
+ //issue#1277,台湾身份证号码以83开头,但是行政区划为71
+ CITY_CODES.put("83", "台湾");
CITY_CODES.put("91", "国外");
TW_FIRST_CODE.put("A", 10);
@@ -354,7 +356,7 @@ public class IdcardUtil {
sum = sum + Integer.parseInt(String.valueOf(c)) * iflag;
iflag--;
}
- if ("A".equals(end.toUpperCase())) {
+ if ("A".equalsIgnoreCase(end)) {
sum += 10;
} else {
sum += Integer.parseInt(end);
@@ -508,7 +510,7 @@ public class IdcardUtil {
}
/**
- * 根据身份编号获取户籍省份,只支持15或18位身份证号码
+ * 根据身份编号获取市级编码,只支持15或18位身份证号码
*
* @param idcard 身份编码
* @return 市级编码。
@@ -656,9 +658,9 @@ public class IdcardUtil {
}
/**
- * 获取省份代码
+ * 获取市级编码
*
- * @return 省份代码
+ * @return 市级编码
*/
public String getCityCode() {
return this.cityCode;
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java
index 96f17de45..aa8896bf1 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/NumberUtil.java
@@ -41,7 +41,7 @@ public class NumberUtil {
/**
* 默认除法运算精度
*/
- private static final int DEFAUT_DIV_SCALE = 10;
+ private static final int DEFAULT_DIV_SCALE = 10;
/**
* 0-20对应的阶乘,超过20的阶乘会超过Long.MAX_VALUE
@@ -484,7 +484,7 @@ public class NumberUtil {
* @return 两个参数的商
*/
public static double div(float v1, float v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -495,7 +495,7 @@ public class NumberUtil {
* @return 两个参数的商
*/
public static double div(float v1, double v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -506,7 +506,7 @@ public class NumberUtil {
* @return 两个参数的商
*/
public static double div(double v1, float v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -517,7 +517,7 @@ public class NumberUtil {
* @return 两个参数的商
*/
public static double div(double v1, double v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -528,7 +528,7 @@ public class NumberUtil {
* @return 两个参数的商
*/
public static double div(Double v1, Double v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -540,7 +540,7 @@ public class NumberUtil {
* @since 3.1.0
*/
public static BigDecimal div(Number v1, Number v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -551,7 +551,7 @@ public class NumberUtil {
* @return 两个参数的商
*/
public static BigDecimal div(String v1, String v2) {
- return div(v1, v2, DEFAUT_DIV_SCALE);
+ return div(v1, v2, DEFAULT_DIV_SCALE);
}
/**
@@ -1516,7 +1516,7 @@ public class NumberUtil {
*/
public static int processMultiple(int selectNum, int minNum) {
int result;
- result = mathSubnode(selectNum, minNum) / mathNode(selectNum - minNum);
+ result = mathSubNode(selectNum, minNum) / mathNode(selectNum - minNum);
return result;
}
@@ -2494,11 +2494,11 @@ public class NumberUtil {
}
// ------------------------------------------------------------------------------------------- Private method start
- private static int mathSubnode(int selectNum, int minNum) {
+ private static int mathSubNode(int selectNum, int minNum) {
if (selectNum == minNum) {
return 1;
} else {
- return selectNum * mathSubnode(selectNum - 1, minNum);
+ return selectNum * mathSubNode(selectNum - 1, minNum);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
index 8ae304433..0cd995851 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java
@@ -383,7 +383,7 @@ public class ObjectUtil {
* 克隆对象
* 如果对象实现Cloneable接口,调用其clone方法
* 如果实现Serializable接口,执行深度克隆
- * 否则返回null
+ * 否则返回{@code null}
*
* @param
+ * 此方法用于包括结束位置的分页方法
+ * 例如:
+ *
+ *
+ * 页码:0,每页10 =》 [0, 10]
+ * 页码:1,每页10 =》 [10, 20]
+ * ……
+ *
+ *
+ *
+ * 页码:1,每页10 =》 [0, 10]
+ * 页码:2,每页10 =》 [10, 20]
+ * ……
+ *
+ *
+ * @param pageNo 页码(从0计数)
+ * @param pageSize 每页条目数
+ * @return {@link Segment}
+ * @since 5.5.3
+ */
+ public static Segmentnull
+ * 查找指定类中的指定name的字段(包括非public字段),也包括父类和Object类的字段, 字段不存在则返回{@code null}
*
* @param beanClass 被查找字段的类,不能为null
* @param name 字段名
@@ -422,7 +422,7 @@ public class ReflectUtil {
}
/**
- * 查找指定Public方法 如果找不到对应的方法或方法不为public的则返回null
+ * 查找指定Public方法 如果找不到对应的方法或方法不为public的则返回{@code null}
*
* @param clazz 类
* @param methodName 方法名
@@ -442,7 +442,7 @@ public class ReflectUtil {
* 查找指定对象中的所有方法(包括非public方法),也包括父对象和Object类的方法
*
* null
。
+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
* null
+ * 忽略大小写查找指定方法,如果找不到对应的方法则返回{@code null}
*
* null
。
+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
* null
+ * 查找指定方法 如果找不到对应的方法则返回{@code null}
*
* null
。
+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
* null
+ * 查找指定方法 如果找不到对应的方法则返回{@code null}
*
* null
。
+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
* null
+ * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null}
*
* null
+ * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null}
*
* null
+ * 按照方法名查找指定方法名的方法,只返回匹配到的第一个方法,如果找不到对应的方法则返回{@code null}
*
* null
+ * @param obj 对象,如果执行静态方法,此值为{@code null}
* @param method 方法(对象方法或static方法都可)
* @param args 参数对象
* @return 结果
@@ -878,7 +878,7 @@ public class ReflectUtil {
*
*
* @param null
+ * @param obj 对象,如果执行静态方法,此值为{@code null}
* @param method 方法(对象方法或static方法都可)
* @param args 参数对象
* @return 结果
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java
index cb4cb4b85..a9b684eea 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/StrUtil.java
@@ -1,38 +1,21 @@
package cn.hutool.core.util;
-import cn.hutool.core.comparator.VersionComparator;
-import cn.hutool.core.convert.Convert;
-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.CharSequenceUtil;
import cn.hutool.core.text.StrBuilder;
-import cn.hutool.core.text.StrFormatter;
-import cn.hutool.core.text.StrSpliter;
import cn.hutool.core.text.TextSimilarity;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.regex.Pattern;
/**
* 字符串工具类
*
* @author xiaoleilu
*/
-public class StrUtil {
-
- public static final int INDEX_NOT_FOUND = -1;
+public class StrUtil extends CharSequenceUtil {
/**
* 字符常量:空格符 {@code ' '}
@@ -105,16 +88,11 @@ public class StrUtil {
public static final char C_COLON = CharUtil.COLON;
/**
- * 字符常量:艾特 '@'
+ * 字符常量:艾特 {@code '@'}
*/
public static final char C_AT = CharUtil.AT;
- /**
- * 字符串常量:空格符 {@code " "}
- */
- public static final String SPACE = " ";
-
/**
* 字符串常量:制表符 {@code "\t"}
*/
@@ -141,17 +119,6 @@ public class StrUtil {
*/
public static final String BACKSLASH = "\\";
- /**
- * 字符串常量:空字符串 {@code ""}
- */
- public static final String EMPTY = "";
-
- /**
- * 字符串常量:{@code "null"}
- * 注意:{@code "null" != null}
- */
- public static final String NULL = "null";
-
/**
* 字符串常量:回车符 {@code "\r"}
* 解释:该字符常用于表示 Linux 系统和 MacOS 系统下的文本换行
@@ -210,7 +177,7 @@ public class StrUtil {
public static final String COLON = ":";
/**
- * 字符串常量:艾特 "@"
+ * 字符串常量:艾特 {@code "@"}
*/
public static final String AT = "@";
@@ -218,88 +185,40 @@ public class StrUtil {
/**
* 字符串常量:HTML 空格转义 {@code " " -> " "}
*/
- public static final String HTML_NBSP = " ";
+ public static final String HTML_NBSP = XmlUtil.NBSP;
/**
* 字符串常量:HTML And 符转义 {@code "&" -> "&"}
*/
- public static final String HTML_AMP = "&";
+ public static final String HTML_AMP = XmlUtil.AMP;
/**
* 字符串常量:HTML 双引号转义 {@code """ -> "\""}
*/
- public static final String HTML_QUOTE = """;
+ public static final String HTML_QUOTE = XmlUtil.QUOTE;
/**
* 字符串常量:HTML 单引号转义 {@code "&apos" -> "'"}
*/
- public static final String HTML_APOS = "'";
+ public static final String HTML_APOS = XmlUtil.APOS;
/**
* 字符串常量:HTML 小于号转义 {@code "<" -> "<"}
*/
- public static final String HTML_LT = "<";
+ public static final String HTML_LT = XmlUtil.LT;
/**
* 字符串常量:HTML 大于号转义 {@code ">" -> ">"}
*/
- public static final String HTML_GT = ">";
+ public static final String HTML_GT = XmlUtil.GT;
/**
- * 字符串常量:空 JSON "{}"
+ * 字符串常量:空 JSON {@code "{}"}
*/
public static final String EMPTY_JSON = "{}";
-
// ------------------------------------------------------------------------ Blank
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * @param str 被检测的字符串
- * @return 若为空白,则返回 true
- * @see #isEmpty(CharSequence)
- */
- public static boolean isBlank(CharSequence str) {
- int length;
-
- if ((str == null) || ((length = str.length()) == 0)) {
- return true;
- }
-
- for (int i = 0; i < length; i++) {
- // 只要有一个非空字符即为非空字符串
- if (false == CharUtil.isBlankChar(str.charAt(i))) {
- return false;
- }
- }
-
- return true;
- }
-
/**
*
@@ -332,137 +251,8 @@ public class StrUtil {
}
return false;
}
-
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * @param strs 字符串列表
- * @return 是否包含空字符串
- */
- public static boolean hasBlank(CharSequence... strs) {
- if (ArrayUtil.isEmpty(strs)) {
- return true;
- }
-
- for (CharSequence str : strs) {
- if (isBlank(str)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- * @param strs 字符串列表
- * @return 所有字符串是否为空白
- */
- public static boolean isAllBlank(CharSequence... strs) {
- if (ArrayUtil.isEmpty(strs)) {
- return true;
- }
-
- for (CharSequence str : strs) {
- if (isNotBlank(str)) {
- return false;
- }
- }
- return true;
- }
-
// ------------------------------------------------------------------------ Empty
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * @param str 被检测的字符串
- * @return 是否为空
- * @see #isBlank(CharSequence)
- */
- public static boolean isEmpty(CharSequence str) {
- return str == null || str.length() == 0;
- }
-
/**
*
*
@@ -493,307 +283,8 @@ public class StrUtil {
return false;
}
- /**
- *
- *
- *
- *
- *
- *
- * null
,则返回指定默认字符串,否则返回字符串本身。
- *
- *
- * nullToDefault(null, "default") = "default"
- * nullToDefault("", "default") = ""
- * nullToDefault(" ", "default") = " "
- * nullToDefault("bat", "default") = "bat"
- *
- *
- * @param str 要转换的字符串
- * @param defaultStr 默认字符串
- * @return 字符串本身或指定的默认字符串
- */
- public static String nullToDefault(CharSequence str, String defaultStr) {
- return (str == null) ? defaultStr : str.toString();
- }
-
- /**
- * 如果字符串是null
或者"",则返回指定默认字符串,否则返回字符串本身。
- *
- *
- * emptyToDefault(null, "default") = "default"
- * emptyToDefault("", "default") = "default"
- * emptyToDefault(" ", "default") = " "
- * emptyToDefault("bat", "default") = "bat"
- *
- *
- * @param str 要转换的字符串
- * @param defaultStr 默认字符串
- * @return 字符串本身或指定的默认字符串
- * @since 4.1.0
- */
- public static String emptyToDefault(CharSequence str, String defaultStr) {
- return isEmpty(str) ? defaultStr : str.toString();
- }
-
- /**
- * 如果字符串是null
或者""或者空白,则返回指定默认字符串,否则返回字符串本身。
- *
- *
- * emptyToDefault(null, "default") = "default"
- * emptyToDefault("", "default") = "default"
- * emptyToDefault(" ", "default") = "default"
- * emptyToDefault("bat", "default") = "bat"
- *
- *
- * @param str 要转换的字符串
- * @param defaultStr 默认字符串
- * @return 字符串本身或指定的默认字符串
- * @since 4.1.0
- */
- public static String blankToDefault(CharSequence str, String defaultStr) {
- return isBlank(str) ? defaultStr : str.toString();
- }
-
- /**
- * 当给定字符串为空字符串时,转换为null
- *
- * @param str 被转换的字符串
- * @return 转换后的字符串
- */
- public static String emptyToNull(CharSequence str) {
- return isEmpty(str) ? null : str.toString();
- }
-
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- * @param strs 字符串列表
- * @return 是否包含空字符串
- */
- public static boolean hasEmpty(CharSequence... strs) {
- if (ArrayUtil.isEmpty(strs)) {
- return true;
- }
-
- for (CharSequence str : strs) {
- if (isEmpty(str)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- * @param strs 字符串列表
- * @return 所有字符串是否为空白
- */
- public static boolean isAllEmpty(CharSequence... strs) {
- if (ArrayUtil.isEmpty(strs)) {
- return true;
- }
-
- for (CharSequence str : strs) {
- if (isNotEmpty(str)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- *
- *
- *
- *
- *
- *
- *
- *
- * @param args 字符串数组
- * @return 所有字符串是否都不为为空白
- * @since 5.3.6
- */
- public static boolean isAllNotEmpty(CharSequence... args) {
- return false == hasEmpty(args);
- }
-
- /**
- * 是否存都不为{@code null}或空对象或空白符的对象,通过{@link StrUtil#hasBlank(CharSequence...)} 判断元素
- *
- * @param args 被检查的对象,一个或者多个
- * @return 是否都不为空
- * @since 5.3.6
- */
- public static boolean isAllNotBlank(CharSequence... args) {
- return false == hasBlank(args);
- }
-
- /**
- * 检查字符串是否为null、“null”、“undefined”
- *
- * @param str 被检查的字符串
- * @return 是否为null、“null”、“undefined”
- * @since 4.0.10
- */
- public static boolean isNullOrUndefined(CharSequence str) {
- if (null == str) {
- return true;
- }
- return isNullOrUndefinedStr(str);
- }
-
- /**
- * 检查字符串是否为null、“”、“null”、“undefined”
- *
- * @param str 被检查的字符串
- * @return 是否为null、“”、“null”、“undefined”
- * @since 4.0.10
- */
- public static boolean isEmptyOrUndefined(CharSequence str) {
- if (isEmpty(str)) {
- return true;
- }
- return isNullOrUndefinedStr(str);
- }
-
- /**
- * 检查字符串是否为null、空白串、“null”、“undefined”
- *
- * @param str 被检查的字符串
- * @return 是否为null、空白串、“null”、“undefined”
- * @since 4.0.10
- */
- public static boolean isBlankOrUndefined(CharSequence str) {
- if (isBlank(str)) {
- return true;
- }
- return isNullOrUndefinedStr(str);
- }
-
- /**
- * 是否为“null”、“undefined”,不做空指针检查
- *
- * @param str 字符串
- * @return 是否为“null”、“undefined”
- */
- private static boolean isNullOrUndefinedStr(CharSequence str) {
- String strString = str.toString().trim();
- return NULL.equals(strString) || "undefined".equals(strString);
- }
-
// ------------------------------------------------------------------------ Trim
- /**
- * 除去字符串头尾部的空白,如果字符串是null
,依然返回null
。
- *
- * String.trim
不同,此方法使用NumberUtil.isBlankChar
来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
- *
- *
- * trim(null) = null
- * trim("") = ""
- * trim(" ") = ""
- * trim("abc") = "abc"
- * trim(" abc ") = "abc"
- *
- *
- * @param str 要处理的字符串
- * @return 除去头尾空白的字符串,如果原字串为null
,则返回null
- */
- public static String trim(CharSequence str) {
- return (null == str) ? null : trim(str, 0);
- }
-
/**
* 给定字符串数组全部做去首尾空格
*
@@ -807,2046 +298,11 @@ public class StrUtil {
for (int i = 0; i < strs.length; i++) {
str = strs[i];
if (null != str) {
- strs[i] = str.trim();
+ strs[i] = trim(str);
}
}
}
- /**
- * 除去字符串头尾部的空白,如果字符串是{@code null},返回""
。
- *
- *
- * StrUtil.trimToEmpty(null) = ""
- * StrUtil.trimToEmpty("") = ""
- * StrUtil.trimToEmpty(" ") = ""
- * StrUtil.trimToEmpty("abc") = "abc"
- * StrUtil.trimToEmpty(" abc ") = "abc"
- *
- *
- * @param str 字符串
- * @return 去除两边空白符后的字符串, 如果为null返回""
- * @since 3.1.1
- */
- public static String trimToEmpty(CharSequence str) {
- return str == null ? EMPTY : trim(str);
- }
-
- /**
- * 除去字符串头尾部的空白,如果字符串是{@code null}或者"",返回{@code null}。
- *
- *
- * StrUtil.trimToNull(null) = null
- * StrUtil.trimToNull("") = null
- * StrUtil.trimToNull(" ") = null
- * StrUtil.trimToNull("abc") = "abc"
- * StrUtil.trimToEmpty(" abc ") = "abc"
- *
- *
- * @param str 字符串
- * @return 去除两边空白符后的字符串, 如果为空返回null
- * @since 3.2.1
- */
- public static String trimToNull(CharSequence str) {
- final String trimStr = trim(str);
- return EMPTY.equals(trimStr) ? null : trimStr;
- }
-
- /**
- * 除去字符串头部的空白,如果字符串是null
,则返回null
。
- *
- * String.trim
不同,此方法使用CharUtil.isBlankChar
来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
- *
- *
- * trimStart(null) = null
- * trimStart("") = ""
- * trimStart("abc") = "abc"
- * trimStart(" abc") = "abc"
- * trimStart("abc ") = "abc "
- * trimStart(" abc ") = "abc "
- *
- *
- * @param str 要处理的字符串
- * @return 除去空白的字符串,如果原字串为null
或结果字符串为""
,则返回 null
- */
- public static String trimStart(CharSequence str) {
- return trim(str, -1);
- }
-
- /**
- * 除去字符串尾部的空白,如果字符串是null
,则返回null
。
- *
- * String.trim
不同,此方法使用CharUtil.isBlankChar
来判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。
- *
- *
- * trimEnd(null) = null
- * trimEnd("") = ""
- * trimEnd("abc") = "abc"
- * trimEnd(" abc") = " abc"
- * trimEnd("abc ") = "abc"
- * trimEnd(" abc ") = " abc"
- *
- *
- * @param str 要处理的字符串
- * @return 除去空白的字符串,如果原字串为null
或结果字符串为""
,则返回 null
- */
- public static String trimEnd(CharSequence str) {
- return trim(str, 1);
- }
-
- /**
- * 除去字符串头尾部的空白符,如果字符串是null
,依然返回null
。
- *
- * @param str 要处理的字符串
- * @param mode -1
表示trimStart,0
表示trim全部, 1
表示trimEnd
- * @return 除去指定字符后的的字符串,如果原字串为null
,则返回null
- */
- public static String trim(CharSequence str, int mode) {
- if (str == null) {
- return null;
- }
-
- int length = str.length();
- int start = 0;
- int end = length;
-
- // 扫描字符串头部
- if (mode <= 0) {
- while ((start < end) && (CharUtil.isBlankChar(str.charAt(start)))) {
- start++;
- }
- }
-
- // 扫描字符串尾部
- if (mode >= 0) {
- while ((start < end) && (CharUtil.isBlankChar(str.charAt(end - 1)))) {
- end--;
- }
- }
-
- if ((start > 0) || (end < length)) {
- return str.toString().substring(start, end);
- }
-
- return str.toString();
- }
-
- /**
- * 字符串是否以给定字符开始
- *
- * @param str 字符串
- * @param c 字符
- * @return 是否开始
- */
- public static boolean startWith(CharSequence str, char c) {
- if (isEmpty(str)) {
- return false;
- }
- return c == str.charAt(0);
- }
-
- /**
- * 是否以指定字符串开头
- * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
- *
- * @param str 被监测字符串
- * @param prefix 开头字符串
- * @param ignoreCase 是否忽略大小写
- * @return 是否以指定字符串开头
- * @since 5.4.3
- */
- public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
- return startWith(str, prefix, ignoreCase, false);
- }
-
- /**
- * 是否以指定字符串开头
- * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
- *
- * @param str 被监测字符串
- * @param prefix 开头字符串
- * @param ignoreCase 是否忽略大小写
- * @param ignoreEquals 是否忽略字符串相等的情况
- * @return 是否以指定字符串开头
- * @since 5.4.3
- */
- public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase, boolean ignoreEquals) {
- if (null == str || null == prefix) {
- if (false == ignoreEquals) {
- return false;
- }
- return null == str && null == prefix;
- }
-
- boolean isStartWith;
- if (ignoreCase) {
- isStartWith = str.toString().toLowerCase().startsWith(prefix.toString().toLowerCase());
- } else {
- isStartWith = str.toString().startsWith(prefix.toString());
- }
-
- if (isStartWith) {
- return (false == ignoreEquals) || (false == equals(str, prefix, ignoreCase));
- }
- return false;
- }
-
- /**
- * 是否以指定字符串开头
- *
- * @param str 被监测字符串
- * @param prefix 开头字符串
- * @return 是否以指定字符串开头
- */
- public static boolean startWith(CharSequence str, CharSequence prefix) {
- return startWith(str, prefix, false);
- }
-
- /**
- * 是否以指定字符串开头,忽略相等字符串的情况
- *
- * @param str 被监测字符串
- * @param prefix 开头字符串
- * @return 是否以指定字符串开头并且两个字符串不相等
- */
- public static boolean startWithIgnoreEquals(CharSequence str, CharSequence prefix) {
- return startWith(str, prefix, false, true);
- }
-
- /**
- * 是否以指定字符串开头,忽略大小写
- *
- * @param str 被监测字符串
- * @param prefix 开头字符串
- * @return 是否以指定字符串开头
- */
- public static boolean startWithIgnoreCase(CharSequence str, CharSequence prefix) {
- return startWith(str, prefix, true);
- }
-
- /**
- * 给定字符串是否以任何一个字符串开始
- * 给定字符串和数组为空都返回false
- *
- * @param str 给定字符串
- * @param prefixes 需要检测的开始字符串
- * @return 给定字符串是否以任何一个字符串开始
- * @since 3.0.6
- */
- public static boolean startWithAny(CharSequence str, CharSequence... prefixes) {
- if (isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
- return false;
- }
-
- for (CharSequence suffix : prefixes) {
- if (startWith(str, suffix, false)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 字符串是否以给定字符结尾
- *
- * @param str 字符串
- * @param c 字符
- * @return 是否结尾
- */
- public static boolean endWith(CharSequence str, char c) {
- if (isEmpty(str)) {
- return false;
- }
- return c == str.charAt(str.length() - 1);
- }
-
- /**
- * 是否以指定字符串结尾
- * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
- *
- * @param str 被监测字符串
- * @param suffix 结尾字符串
- * @param isIgnoreCase 是否忽略大小写
- * @return 是否以指定字符串结尾
- */
- public static boolean endWith(CharSequence str, CharSequence suffix, boolean isIgnoreCase) {
- if (null == str || null == suffix) {
- return null == str && null == suffix;
- }
-
- if (isIgnoreCase) {
- return str.toString().toLowerCase().endsWith(suffix.toString().toLowerCase());
- } else {
- return str.toString().endsWith(suffix.toString());
- }
- }
-
- /**
- * 是否以指定字符串结尾
- *
- * @param str 被监测字符串
- * @param suffix 结尾字符串
- * @return 是否以指定字符串结尾
- */
- public static boolean endWith(CharSequence str, CharSequence suffix) {
- return endWith(str, suffix, false);
- }
-
- /**
- * 是否以指定字符串结尾,忽略大小写
- *
- * @param str 被监测字符串
- * @param suffix 结尾字符串
- * @return 是否以指定字符串结尾
- */
- public static boolean endWithIgnoreCase(CharSequence str, CharSequence suffix) {
- return endWith(str, suffix, true);
- }
-
- /**
- * 给定字符串是否以任何一个字符串结尾
- * 给定字符串和数组为空都返回false
- *
- * @param str 给定字符串
- * @param suffixes 需要检测的结尾字符串
- * @return 给定字符串是否以任何一个字符串结尾
- * @since 3.0.6
- */
- public static boolean endWithAny(CharSequence str, CharSequence... suffixes) {
- if (isEmpty(str) || ArrayUtil.isEmpty(suffixes)) {
- return false;
- }
-
- for (CharSequence suffix : suffixes) {
- if (endWith(str, suffix, false)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 指定字符是否在字符串中出现过
- *
- * @param str 字符串
- * @param searchChar 被查找的字符
- * @return 是否包含
- * @since 3.1.2
- */
- public static boolean contains(CharSequence str, char searchChar) {
- return indexOf(str, searchChar) > -1;
- }
-
- /**
- * 指定字符串是否在字符串中出现过
- *
- * @param str 字符串
- * @param searchStr 被查找的字符串
- * @return 是否包含
- * @since 5.1.1
- */
- public static boolean contains(CharSequence str, CharSequence searchStr) {
- if (null == str || null == searchStr) {
- return false;
- }
- return str.toString().contains(searchStr);
- }
-
- /**
- * 查找指定字符串是否包含指定字符串列表中的任意一个字符串
- *
- * @param str 指定字符串
- * @param testStrs 需要检查的字符串数组
- * @return 是否包含任意一个字符串
- * @since 3.2.0
- */
- public static boolean containsAny(CharSequence str, CharSequence... testStrs) {
- return null != getContainsStr(str, testStrs);
- }
-
- /**
- * 查找指定字符串是否包含指定字符列表中的任意一个字符
- *
- * @param str 指定字符串
- * @param testChars 需要检查的字符数组
- * @return 是否包含任意一个字符
- * @since 4.1.11
- */
- public static boolean containsAny(CharSequence str, char... testChars) {
- if (false == isEmpty(str)) {
- int len = str.length();
- for (int i = 0; i < len; i++) {
- if (ArrayUtil.contains(testChars, str.charAt(i))) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * 检查指定字符串中是否只包含给定的字符
- *
- * @param str 字符串
- * @param testChars 检查的字符
- * @return 字符串含有非检查的字符,返回false
- * @since 4.4.1
- */
- public static boolean containsOnly(CharSequence str, char... testChars) {
- if (false == isEmpty(str)) {
- int len = str.length();
- for (int i = 0; i < len; i++) {
- if (false == ArrayUtil.contains(testChars, str.charAt(i))) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * 给定字符串是否包含空白符(空白符包括空格、制表符、全角空格和不间断空格)
- * 如果给定字符串为null或者"",则返回false
- *
- * @param str 字符串
- * @return 是否包含空白符
- * @since 4.0.8
- */
- public static boolean containsBlank(CharSequence str) {
- if (null == str) {
- return false;
- }
- final int length = str.length();
- if (0 == length) {
- return false;
- }
-
- for (int i = 0; i < length; i += 1) {
- if (CharUtil.isBlankChar(str.charAt(i))) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串
- *
- * @param str 指定字符串
- * @param testStrs 需要检查的字符串数组
- * @return 被包含的第一个字符串
- * @since 3.2.0
- */
- public static String getContainsStr(CharSequence str, CharSequence... testStrs) {
- if (isEmpty(str) || ArrayUtil.isEmpty(testStrs)) {
- return null;
- }
- for (CharSequence checkStr : testStrs) {
- if (str.toString().contains(checkStr)) {
- return checkStr.toString();
- }
- }
- return null;
- }
-
- /**
- * 是否包含特定字符,忽略大小写,如果给定两个参数都为null
,返回true
- *
- * @param str 被检测字符串
- * @param testStr 被测试是否包含的字符串
- * @return 是否包含
- */
- public static boolean containsIgnoreCase(CharSequence str, CharSequence testStr) {
- if (null == str) {
- // 如果被监测字符串和
- return null == testStr;
- }
- return str.toString().toLowerCase().contains(testStr.toString().toLowerCase());
- }
-
- /**
- * 查找指定字符串是否包含指定字符串列表中的任意一个字符串
- * 忽略大小写
- *
- * @param str 指定字符串
- * @param testStrs 需要检查的字符串数组
- * @return 是否包含任意一个字符串
- * @since 3.2.0
- */
- public static boolean containsAnyIgnoreCase(CharSequence str, CharSequence... testStrs) {
- return null != getContainsStrIgnoreCase(str, testStrs);
- }
-
- /**
- * 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串
- * 忽略大小写
- *
- * @param str 指定字符串
- * @param testStrs 需要检查的字符串数组
- * @return 被包含的第一个字符串
- * @since 3.2.0
- */
- public static String getContainsStrIgnoreCase(CharSequence str, CharSequence... testStrs) {
- if (isEmpty(str) || ArrayUtil.isEmpty(testStrs)) {
- return null;
- }
- for (CharSequence testStr : testStrs) {
- if (containsIgnoreCase(str, testStr)) {
- return testStr.toString();
- }
- }
- return null;
- }
-
- /**
- * 获得set或get或is方法对应的标准属性名
- * 例如:setName 返回 name
- *
- *
- * getName =》name
- * setName =》name
- * isName =》name
- *
- *
- * @param getOrSetMethodName Get或Set方法名
- * @return 如果是set或get方法名,返回field, 否则null
- */
- public static String getGeneralField(CharSequence getOrSetMethodName) {
- final String getOrSetMethodNameStr = getOrSetMethodName.toString();
- if (getOrSetMethodNameStr.startsWith("get") || getOrSetMethodNameStr.startsWith("set")) {
- return removePreAndLowerFirst(getOrSetMethodName, 3);
- } else if (getOrSetMethodNameStr.startsWith("is")) {
- return removePreAndLowerFirst(getOrSetMethodName, 2);
- }
- return null;
- }
-
- /**
- * 生成set方法名
- * 例如:name 返回 setName
- *
- * @param fieldName 属性名
- * @return setXxx
- */
- public static String genSetter(CharSequence fieldName) {
- return upperFirstAndAddPre(fieldName, "set");
- }
-
- /**
- * 生成get方法名
- *
- * @param fieldName 属性名
- * @return getXxx
- */
- public static String genGetter(CharSequence fieldName) {
- return upperFirstAndAddPre(fieldName, "get");
- }
-
- /**
- * 移除字符串中所有给定字符串
- * 例:removeAll("aa-bb-cc-dd", "-") =》 aabbccdd
- *
- * @param str 字符串
- * @param strToRemove 被移除的字符串
- * @return 移除后的字符串
- */
- public static String removeAll(CharSequence str, CharSequence strToRemove) {
- // strToRemove如果为空, 也不用继续后面的逻辑
- if (isEmpty(str) || isEmpty(strToRemove)) {
- return str(str);
- }
- return str.toString().replace(strToRemove, EMPTY);
- }
-
- /**
- * 移除字符串中所有给定字符串,当某个字符串出现多次,则全部移除
- * 例:removeAny("aa-bb-cc-dd", "a", "b") =》 --cc-dd
- *
- * @param str 字符串
- * @param strsToRemove 被移除的字符串
- * @return 移除后的字符串
- * @since 5.3.8
- */
- public static String removeAny(CharSequence str, CharSequence... strsToRemove) {
- String result = str(str);
- if (isNotEmpty(str)) {
- for (CharSequence strToRemove : strsToRemove) {
- result = removeAll(result, strToRemove);
- }
- }
- return result;
- }
-
- /**
- * 去除字符串中指定的多个字符,如有多个则全部去除
- *
- * @param str 字符串
- * @param chars 字符列表
- * @return 去除后的字符
- * @since 4.2.2
- */
- public static String removeAll(CharSequence str, char... chars) {
- if (null == str || ArrayUtil.isEmpty(chars)) {
- return str(str);
- }
- final int len = str.length();
- if (0 == len) {
- return str(str);
- }
- final StringBuilder builder = builder(len);
- char c;
- for (int i = 0; i < len; i++) {
- c = str.charAt(i);
- if (false == ArrayUtil.contains(chars, c)) {
- builder.append(c);
- }
- }
- return builder.toString();
- }
-
- /**
- * 去除所有换行符,包括:
- *
- *
- * 1. \r
- * 1. \n
- *
- *
- * @param str 字符串
- * @return 处理后的字符串
- * @since 4.2.2
- */
- public static String removeAllLineBreaks(CharSequence str) {
- return removeAll(str, C_CR, C_LF);
- }
-
- /**
- * 去掉首部指定长度的字符串并将剩余字符串首字母小写
- * 例如:str=setName, preLength=3 =》 return name
- *
- * @param str 被处理的字符串
- * @param preLength 去掉的长度
- * @return 处理后的字符串,不符合规范返回null
- */
- public static String removePreAndLowerFirst(CharSequence str, int preLength) {
- if (str == null) {
- return null;
- }
- if (str.length() > preLength) {
- char first = Character.toLowerCase(str.charAt(preLength));
- if (str.length() > preLength + 1) {
- return first + str.toString().substring(preLength + 1);
- }
- return String.valueOf(first);
- } else {
- return str.toString();
- }
- }
-
- /**
- * 去掉首部指定长度的字符串并将剩余字符串首字母小写
- * 例如:str=setName, prefix=set =》 return name
- *
- * @param str 被处理的字符串
- * @param prefix 前缀
- * @return 处理后的字符串,不符合规范返回null
- */
- public static String removePreAndLowerFirst(CharSequence str, CharSequence prefix) {
- return lowerFirst(removePrefix(str, prefix));
- }
-
- /**
- * 原字符串首字母大写并在其首部添加指定字符串 例如:str=name, preString=get =》 return getName
- *
- * @param str 被处理的字符串
- * @param preString 添加的首部
- * @return 处理后的字符串
- */
- public static String upperFirstAndAddPre(CharSequence str, String preString) {
- if (str == null || preString == null) {
- return null;
- }
- return preString + upperFirst(str);
- }
-
- /**
- * 大写首字母
- * 例如:str = name, return Name
- *
- * @param str 字符串
- * @return 字符串
- */
- public static String upperFirst(CharSequence str) {
- if (null == str) {
- return null;
- }
- if (str.length() > 0) {
- char firstChar = str.charAt(0);
- if (Character.isLowerCase(firstChar)) {
- return Character.toUpperCase(firstChar) + subSuf(str, 1);
- }
- }
- return str.toString();
- }
-
- /**
- * 小写首字母
- * 例如:str = Name, return name
- *
- * @param str 字符串
- * @return 字符串
- */
- public static String lowerFirst(CharSequence str) {
- if (null == str) {
- return null;
- }
- if (str.length() > 0) {
- char firstChar = str.charAt(0);
- if (Character.isUpperCase(firstChar)) {
- return Character.toLowerCase(firstChar) + subSuf(str, 1);
- }
- }
- return str.toString();
- }
-
- /**
- * 去掉指定前缀
- *
- * @param str 字符串
- * @param prefix 前缀
- * @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串
- */
- public static String removePrefix(CharSequence str, CharSequence prefix) {
- if (isEmpty(str) || isEmpty(prefix)) {
- return str(str);
- }
-
- final String str2 = str.toString();
- if (str2.startsWith(prefix.toString())) {
- return subSuf(str2, prefix.length());// 截取后半段
- }
- return str2;
- }
-
- /**
- * 忽略大小写去掉指定前缀
- *
- * @param str 字符串
- * @param prefix 前缀
- * @return 切掉后的字符串,若前缀不是 prefix, 返回原字符串
- */
- public static String removePrefixIgnoreCase(CharSequence str, CharSequence prefix) {
- if (isEmpty(str) || isEmpty(prefix)) {
- return str(str);
- }
-
- final String str2 = str.toString();
- if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) {
- return subSuf(str2, prefix.length());// 截取后半段
- }
- return str2;
- }
-
- /**
- * 去掉指定后缀
- *
- * @param str 字符串
- * @param suffix 后缀
- * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
- */
- public static String removeSuffix(CharSequence str, CharSequence suffix) {
- if (isEmpty(str) || isEmpty(suffix)) {
- return str(str);
- }
-
- final String str2 = str.toString();
- if (str2.endsWith(suffix.toString())) {
- return subPre(str2, str2.length() - suffix.length());// 截取前半段
- }
- return str2;
- }
-
- /**
- * 去掉指定后缀,并小写首字母
- *
- * @param str 字符串
- * @param suffix 后缀
- * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
- */
- public static String removeSufAndLowerFirst(CharSequence str, CharSequence suffix) {
- return lowerFirst(removeSuffix(str, suffix));
- }
-
- /**
- * 忽略大小写去掉指定后缀
- *
- * @param str 字符串
- * @param suffix 后缀
- * @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
- */
- public static String removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) {
- if (isEmpty(str) || isEmpty(suffix)) {
- return str(str);
- }
-
- final String str2 = str.toString();
- if (str2.toLowerCase().endsWith(suffix.toString().toLowerCase())) {
- return subPre(str2, str2.length() - suffix.length());
- }
- return str2;
- }
-
- /**
- * 去除两边的指定字符串
- *
- * @param str 被处理的字符串
- * @param prefixOrSuffix 前缀或后缀
- * @return 处理后的字符串
- * @since 3.1.2
- */
- public static String strip(CharSequence str, CharSequence prefixOrSuffix) {
- if (equals(str, prefixOrSuffix)) {
- // 对于去除相同字符的情况单独处理
- return EMPTY;
- }
- return strip(str, prefixOrSuffix, prefixOrSuffix);
- }
-
- /**
- * 去除两边的指定字符串
- *
- * @param str 被处理的字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 处理后的字符串
- * @since 3.1.2
- */
- public static String strip(CharSequence str, CharSequence prefix, CharSequence suffix) {
- if (isEmpty(str)) {
- return str(str);
- }
-
- int from = 0;
- int to = str.length();
-
- String str2 = str.toString();
- if (startWith(str2, prefix)) {
- from = prefix.length();
- }
- if (endWith(str2, suffix)) {
- to -= suffix.length();
- }
-
- return str2.substring(Math.min(from, to), Math.max(from, to));
- }
-
- /**
- * 去除两边的指定字符串,忽略大小写
- *
- * @param str 被处理的字符串
- * @param prefixOrSuffix 前缀或后缀
- * @return 处理后的字符串
- * @since 3.1.2
- */
- public static String stripIgnoreCase(CharSequence str, CharSequence prefixOrSuffix) {
- return stripIgnoreCase(str, prefixOrSuffix, prefixOrSuffix);
- }
-
- /**
- * 去除两边的指定字符串,忽略大小写
- *
- * @param str 被处理的字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 处理后的字符串
- * @since 3.1.2
- */
- public static String stripIgnoreCase(CharSequence str, CharSequence prefix, CharSequence suffix) {
- if (isEmpty(str)) {
- return str(str);
- }
- int from = 0;
- int to = str.length();
-
- String str2 = str.toString();
- if (startWithIgnoreCase(str2, prefix)) {
- from = prefix.length();
- }
- if (endWithIgnoreCase(str2, suffix)) {
- to -= suffix.length();
- }
- return str2.substring(from, to);
- }
-
- /**
- * 如果给定字符串不是以prefix开头的,在开头补充 prefix
- *
- * @param str 字符串
- * @param prefix 前缀
- * @return 补充后的字符串
- */
- public static String addPrefixIfNot(CharSequence str, CharSequence prefix) {
- if (isEmpty(str) || isEmpty(prefix)) {
- return str(str);
- }
-
- final String str2 = str.toString();
- final String prefix2 = prefix.toString();
- if (false == str2.startsWith(prefix2)) {
- return prefix2.concat(str2);
- }
- return str2;
- }
-
- /**
- * 如果给定字符串不是以suffix结尾的,在尾部补充 suffix
- *
- * @param str 字符串
- * @param suffix 后缀
- * @return 补充后的字符串
- */
- public static String addSuffixIfNot(CharSequence str, CharSequence suffix) {
- if (isEmpty(str) || isEmpty(suffix)) {
- return str(str);
- }
-
- final String str2 = str.toString();
- final String suffix2 = suffix.toString();
- if (false == str2.endsWith(suffix2)) {
- return str2.concat(suffix2);
- }
- return str2;
- }
-
- /**
- * 清理空白字符
- *
- * @param str 被清理的字符串
- * @return 清理后的字符串
- */
- public static String cleanBlank(CharSequence str) {
- return filter(str, c -> false == CharUtil.isBlankChar(c));
- }
-
- // ------------------------------------------------------------------------------ Split
-
- /**
- * 切分字符串
- *
- * @param str 被切分的字符串
- * @param separator 分隔符字符
- * @return 切分后的数组
- */
- public static String[] splitToArray(CharSequence str, char separator) {
- return splitToArray(str, separator, 0);
- }
-
- /**
- * 切分字符串为long数组
- *
- * @param str 被切分的字符串
- * @param separator 分隔符
- * @return 切分后long数组
- * @since 4.0.6
- */
- public static long[] splitToLong(CharSequence str, char separator) {
- return Convert.convert(long[].class, splitTrim(str, separator));
- }
-
- /**
- * 切分字符串为long数组
- *
- * @param str 被切分的字符串
- * @param separator 分隔符字符串
- * @return 切分后long数组
- * @since 4.0.6
- */
- public static long[] splitToLong(CharSequence str, CharSequence separator) {
- return Convert.convert(long[].class, splitTrim(str, separator));
- }
-
- /**
- * 切分字符串为int数组
- *
- * @param str 被切分的字符串
- * @param separator 分隔符
- * @return 切分后long数组
- * @since 4.0.6
- */
- public static int[] splitToInt(CharSequence str, char separator) {
- return Convert.convert(int[].class, splitTrim(str, separator));
- }
-
- /**
- * 切分字符串为int数组
- *
- * @param str 被切分的字符串
- * @param separator 分隔符字符串
- * @return 切分后long数组
- * @since 4.0.6
- */
- public static int[] splitToInt(CharSequence str, CharSequence separator) {
- return Convert.convert(int[].class, splitTrim(str, separator));
- }
-
- /**
- * 切分字符串
- * a#b#c =》 [a,b,c]
- * a##b#c =》 [a,"",b,c]
- *
- * @param str 被切分的字符串
- * @param separator 分隔符字符
- * @return 切分后的集合
- */
- public static List
- * index从0开始计算,最后一个字符为-1
- * 如果from和to位置一样,返回 ""
- * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length
- * 如果经过修正的index中from大于to,则互换from和to example:
- * abcdefgh 2 3 =》 c
- * abcdefgh 2 -3 =》 cde
- *
- * @param str String
- * @param fromIndexInclude 开始的index(包括)
- * @param toIndexExclude 结束的index(不包括)
- * @return 字串
- */
- public static String sub(CharSequence str, int fromIndexInclude, int toIndexExclude) {
- if (isEmpty(str)) {
- return str(str);
- }
- int len = str.length();
-
- if (fromIndexInclude < 0) {
- fromIndexInclude = len + fromIndexInclude;
- if (fromIndexInclude < 0) {
- fromIndexInclude = 0;
- }
- } else if (fromIndexInclude > len) {
- fromIndexInclude = len;
- }
-
- if (toIndexExclude < 0) {
- toIndexExclude = len + toIndexExclude;
- if (toIndexExclude < 0) {
- toIndexExclude = len;
- }
- } else if (toIndexExclude > len) {
- toIndexExclude = len;
- }
-
- if (toIndexExclude < fromIndexInclude) {
- int tmp = fromIndexInclude;
- fromIndexInclude = toIndexExclude;
- toIndexExclude = tmp;
- }
-
- if (fromIndexInclude == toIndexExclude) {
- return EMPTY;
- }
-
- return str.toString().substring(fromIndexInclude, toIndexExclude);
- }
-
- /**
- * 通过CodePoint截取字符串,可以截断Emoji
- *
- * @param str String
- * @param fromIndex 开始的index(包括)
- * @param toIndex 结束的index(不包括)
- * @return 字串
- */
- public static String subByCodePoint(CharSequence str, int fromIndex, int toIndex) {
- if (isEmpty(str)) {
- return str(str);
- }
-
- if (fromIndex < 0 || fromIndex > toIndex) {
- throw new IllegalArgumentException();
- }
-
- if (fromIndex == toIndex) {
- return EMPTY;
- }
-
- final StringBuilder sb = new StringBuilder();
- final int subLen = toIndex - fromIndex;
- str.toString().codePoints().skip(fromIndex).limit(subLen).forEach(v -> sb.append(Character.toChars(v)));
- return sb.toString();
- }
-
- /**
- * 截取部分字符串,这里一个汉字的长度认为是2
- *
- * @param str 字符串
- * @param len 切割的位置
- * @param suffix 切割后加上后缀
- * @return 切割后的字符串
- * @since 3.1.1
- */
- public static String subPreGbk(CharSequence str, int len, CharSequence suffix) {
- if (isEmpty(str)) {
- return str(str);
- }
-
- byte[] b;
- int counterOfDoubleByte = 0;
- b = str.toString().getBytes(CharsetUtil.CHARSET_GBK);
- if (b.length <= len) {
- return str.toString();
- }
- for (int i = 0; i < len; i++) {
- if (b[i] < 0) {
- counterOfDoubleByte++;
- }
- }
-
- if (counterOfDoubleByte % 2 != 0) {
- len += 1;
- }
- return new String(b, 0, len, CharsetUtil.CHARSET_GBK) + suffix;
- }
-
- /**
- * 限制字符串长度,如果超过指定长度,截取指定长度并在末尾加"..."
- *
- * @param string 字符串
- * @param length 最大长度
- * @return 切割后的剩余的前半部分字符串+"..."
- * @since 4.0.10
- */
- public static String maxLength(CharSequence string, int length) {
- Assert.isTrue(length > 0);
- if (null == string) {
- return null;
- }
- if (string.length() <= length) {
- return string.toString();
- }
- return sub(string, 0, length) + "...";
- }
-
- /**
- * 切割指定位置之前部分的字符串
- *
- * @param string 字符串
- * @param toIndexExclude 切割到的位置(不包括)
- * @return 切割后的剩余的前半部分字符串
- */
- public static String subPre(CharSequence string, int toIndexExclude) {
- return sub(string, 0, toIndexExclude);
- }
-
- /**
- * 切割指定位置之后部分的字符串
- *
- * @param string 字符串
- * @param fromIndex 切割开始的位置(包括)
- * @return 切割后后剩余的后半部分字符串
- */
- public static String subSuf(CharSequence string, int fromIndex) {
- if (isEmpty(string)) {
- return null;
- }
- return sub(string, fromIndex, string.length());
- }
-
- /**
- * 切割指定长度的后部分的字符串
- *
- *
- * StrUtil.subSufByLength("abcde", 3) = "cde"
- * StrUtil.subSufByLength("abcde", 0) = ""
- * StrUtil.subSufByLength("abcde", -5) = ""
- * StrUtil.subSufByLength("abcde", -1) = ""
- * StrUtil.subSufByLength("abcde", 5) = "abcde"
- * StrUtil.subSufByLength("abcde", 10) = "abcde"
- * StrUtil.subSufByLength(null, 3) = null
- *
- *
- * @param string 字符串
- * @param length 切割长度
- * @return 切割后后剩余的后半部分字符串
- * @since 4.0.1
- */
- public static String subSufByLength(CharSequence string, int length) {
- if (isEmpty(string)) {
- return null;
- }
- if (length <= 0) {
- return EMPTY;
- }
- return sub(string, -length, string.length());
- }
-
- /**
- * 截取字符串,从指定位置开始,截取指定长度的字符串
- * author weibaohui
- *
- * @param input 原始字符串
- * @param fromIndex 开始的index,包括
- * @param length 要截取的长度
- * @return 截取后的字符串
- */
- public static String subWithLength(String input, int fromIndex, int length) {
- return sub(input, fromIndex, fromIndex + length);
- }
-
- /**
- * 截取分隔字符串之前的字符串,不包括分隔字符串
- * 如果给定的字符串为空串(null或"")或者分隔字符串为null,返回原字符串
- * 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串,举例如下:
- *
- *
- * StrUtil.subBefore(null, *, false) = null
- * StrUtil.subBefore("", *, false) = ""
- * StrUtil.subBefore("abc", "a", false) = ""
- * StrUtil.subBefore("abcba", "b", false) = "a"
- * StrUtil.subBefore("abc", "c", false) = "ab"
- * StrUtil.subBefore("abc", "d", false) = "abc"
- * StrUtil.subBefore("abc", "", false) = ""
- * StrUtil.subBefore("abc", null, false) = "abc"
- *
- *
- * @param string 被查找的字符串
- * @param separator 分隔字符串(不包括)
- * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
- * @return 切割后的字符串
- * @since 3.1.1
- */
- public static String subBefore(CharSequence string, CharSequence separator, boolean isLastSeparator) {
- if (isEmpty(string) || separator == null) {
- return null == string ? null : string.toString();
- }
-
- final String str = string.toString();
- final String sep = separator.toString();
- if (sep.isEmpty()) {
- return EMPTY;
- }
- final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
- if (INDEX_NOT_FOUND == pos) {
- return str;
- }
- if (0 == pos) {
- return EMPTY;
- }
- return str.substring(0, pos);
- }
-
- /**
- * 截取分隔字符串之前的字符串,不包括分隔字符串
- * 如果给定的字符串为空串(null或"")或者分隔字符串为null,返回原字符串
- * 如果分隔字符串未找到,返回原字符串,举例如下:
- *
- *
- * StrUtil.subBefore(null, *, false) = null
- * StrUtil.subBefore("", *, false) = ""
- * StrUtil.subBefore("abc", 'a', false) = ""
- * StrUtil.subBefore("abcba", 'b', false) = "a"
- * StrUtil.subBefore("abc", 'c', false) = "ab"
- * StrUtil.subBefore("abc", 'd', false) = "abc"
- *
- *
- * @param string 被查找的字符串
- * @param separator 分隔字符串(不包括)
- * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
- * @return 切割后的字符串
- * @since 4.1.15
- */
- public static String subBefore(CharSequence string, char separator, boolean isLastSeparator) {
- if (isEmpty(string)) {
- return null == string ? null : EMPTY;
- }
-
- final String str = string.toString();
- final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
- if (INDEX_NOT_FOUND == pos) {
- return str;
- }
- if (0 == pos) {
- return EMPTY;
- }
- return str.substring(0, pos);
- }
-
- /**
- * 截取分隔字符串之后的字符串,不包括分隔字符串
- * 如果给定的字符串为空串(null或""),返回原字符串
- * 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串,举例如下:
- *
- *
- * StrUtil.subAfter(null, *, false) = null
- * StrUtil.subAfter("", *, false) = ""
- * StrUtil.subAfter(*, null, false) = ""
- * StrUtil.subAfter("abc", "a", false) = "bc"
- * StrUtil.subAfter("abcba", "b", false) = "cba"
- * StrUtil.subAfter("abc", "c", false) = ""
- * StrUtil.subAfter("abc", "d", false) = ""
- * StrUtil.subAfter("abc", "", false) = "abc"
- *
- *
- * @param string 被查找的字符串
- * @param separator 分隔字符串(不包括)
- * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
- * @return 切割后的字符串
- * @since 3.1.1
- */
- public static String subAfter(CharSequence string, CharSequence separator, boolean isLastSeparator) {
- if (isEmpty(string)) {
- return null == string ? null : EMPTY;
- }
- if (separator == null) {
- return EMPTY;
- }
- final String str = string.toString();
- final String sep = separator.toString();
- final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
- if (INDEX_NOT_FOUND == pos || (string.length() - 1) == pos) {
- return EMPTY;
- }
- return str.substring(pos + separator.length());
- }
-
- /**
- * 截取分隔字符串之后的字符串,不包括分隔字符串
- * 如果给定的字符串为空串(null或""),返回原字符串
- * 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串,举例如下:
- *
- *
- * StrUtil.subAfter(null, *, false) = null
- * StrUtil.subAfter("", *, false) = ""
- * StrUtil.subAfter("abc", 'a', false) = "bc"
- * StrUtil.subAfter("abcba", 'b', false) = "cba"
- * StrUtil.subAfter("abc", 'c', false) = ""
- * StrUtil.subAfter("abc", 'd', false) = ""
- *
- *
- * @param string 被查找的字符串
- * @param separator 分隔字符串(不包括)
- * @param isLastSeparator 是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
- * @return 切割后的字符串
- * @since 4.1.15
- */
- public static String subAfter(CharSequence string, char separator, boolean isLastSeparator) {
- if (isEmpty(string)) {
- return null == string ? null : EMPTY;
- }
- final String str = string.toString();
- final int pos = isLastSeparator ? str.lastIndexOf(separator) : str.indexOf(separator);
- if (INDEX_NOT_FOUND == pos) {
- return EMPTY;
- }
- return str.substring(pos + 1);
- }
-
- /**
- * 截取指定字符串中间部分,不包括标识字符串
- *
- * StrUtil.subBetween("wx[b]yz", "[", "]") = "b"
- * StrUtil.subBetween(null, *, *) = null
- * StrUtil.subBetween(*, null, *) = null
- * StrUtil.subBetween(*, *, null) = null
- * StrUtil.subBetween("", "", "") = ""
- * StrUtil.subBetween("", "", "]") = null
- * StrUtil.subBetween("", "[", "]") = null
- * StrUtil.subBetween("yabcz", "", "") = ""
- * StrUtil.subBetween("yabcz", "y", "z") = "abc"
- * StrUtil.subBetween("yabczyabcz", "y", "z") = "abc"
- *
- *
- * @param str 被切割的字符串
- * @param before 截取开始的字符串标识
- * @param after 截取到的字符串标识
- * @return 截取后的字符串
- * @since 3.1.1
- */
- public static String subBetween(CharSequence str, CharSequence before, CharSequence after) {
- if (str == null || before == null || after == null) {
- return null;
- }
-
- final String str2 = str.toString();
- final String before2 = before.toString();
- final String after2 = after.toString();
-
- final int start = str2.indexOf(before2);
- if (start != INDEX_NOT_FOUND) {
- final int end = str2.indexOf(after2, start + before2.length());
- if (end != INDEX_NOT_FOUND) {
- return str2.substring(start + before2.length(), end);
- }
- }
- return null;
- }
-
- /**
- * 截取指定字符串中间部分,不包括标识字符串
- *
- * StrUtil.subBetween(null, *) = null
- * StrUtil.subBetween("", "") = ""
- * StrUtil.subBetween("", "tag") = null
- * StrUtil.subBetween("tagabctag", null) = null
- * StrUtil.subBetween("tagabctag", "") = ""
- * StrUtil.subBetween("tagabctag", "tag") = "abc"
- *
- *
- * @param str 被切割的字符串
- * @param beforeAndAfter 截取开始和结束的字符串标识
- * @return 截取后的字符串
- * @since 3.1.1
- */
- public static String subBetween(CharSequence str, CharSequence beforeAndAfter) {
- return subBetween(str, beforeAndAfter, beforeAndAfter);
- }
-
- /**
- * 截取指定字符串多段中间部分,不包括标识字符串
- *
- * StrUtil.subBetweenAll("wx[b]y[z]", "[", "]") = ["b","z"]
- * StrUtil.subBetweenAll(null, *, *) = []
- * StrUtil.subBetweenAll(*, null, *) = []
- * StrUtil.subBetweenAll(*, *, null) = []
- * StrUtil.subBetweenAll("", "", "") = []
- * StrUtil.subBetweenAll("", "", "]") = []
- * StrUtil.subBetweenAll("", "[", "]") = []
- * StrUtil.subBetweenAll("yabcz", "", "") = []
- * StrUtil.subBetweenAll("yabcz", "y", "z") = ["abc"]
- * StrUtil.subBetweenAll("yabczyabcz", "y", "z") = ["abc","abc"]
- * StrUtil.subBetweenAll("[yabc[zy]abcz]", "[", "]"); = ["zy"] 重叠时只截取内部,
- *
- *
- * @param str 被切割的字符串
- * @param prefix 截取开始的字符串标识
- * @param suffix 截取到的字符串标识
- * @return 截取后的字符串
- * @author dahuoyzs
- * @since 5.2.5
- */
- public static String[] subBetweenAll(CharSequence str, CharSequence prefix, CharSequence suffix) {
- if (hasEmpty(str, prefix, suffix) ||
- // 不包含起始字符串,则肯定没有子串
- false == contains(str, prefix)) {
- return new String[0];
- }
-
- final List
- *
- * StrUtil.subBetweenAll(null, *) = []
- * StrUtil.subBetweenAll(*, null) = []
- * StrUtil.subBetweenAll(*, *) = []
- * StrUtil.subBetweenAll("", "") = []
- * StrUtil.subBetweenAll("", "#") = []
- * StrUtil.subBetweenAll("gotanks", "") = []
- * StrUtil.subBetweenAll("#gotanks#", "#") = ["gotanks"]
- * StrUtil.subBetweenAll("#hello# #world#!", "#") = ["hello", "world"]
- * StrUtil.subBetweenAll("#hello# world#!", "#"); = ["hello"]
- *
- *
- * @param str 被切割的字符串
- * @param prefixAndSuffix 截取开始和结束的字符串标识
- * @return 截取后的字符串
- * @author gotanks
- * @since 5.5.0
- */
- public static String[] subBetweenAll(CharSequence str, CharSequence prefixAndSuffix) {
- return subBetweenAll(str, prefixAndSuffix, prefixAndSuffix);
- }
-
- /**
- * 给定字符串是否被字符包围
- *
- * @param str 字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 是否包围,空串不包围
- */
- public static boolean isSurround(CharSequence str, CharSequence prefix, CharSequence suffix) {
- if (StrUtil.isBlank(str)) {
- return false;
- }
- if (str.length() < (prefix.length() + suffix.length())) {
- return false;
- }
-
- final String str2 = str.toString();
- return str2.startsWith(prefix.toString()) && str2.endsWith(suffix.toString());
- }
-
- /**
- * 给定字符串是否被字符包围
- *
- * @param str 字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 是否包围,空串不包围
- */
- public static boolean isSurround(CharSequence str, char prefix, char suffix) {
- if (StrUtil.isBlank(str)) {
- return false;
- }
- if (str.length() < 2) {
- return false;
- }
-
- return str.charAt(0) == prefix && str.charAt(str.length() - 1) == suffix;
- }
-
- /**
- * 重复某个字符
- *
- * @param c 被重复的字符
- * @param count 重复的数目,如果小于等于0则返回""
- * @return 重复字符字符串
- */
- public static String repeat(char c, int count) {
- if (count <= 0) {
- return EMPTY;
- }
-
- char[] result = new char[count];
- for (int i = 0; i < count; i++) {
- result[i] = c;
- }
- return new String(result);
- }
-
- /**
- * 重复某个字符串
- *
- * @param str 被重复的字符
- * @param count 重复的数目
- * @return 重复字符字符串
- */
- public static String repeat(CharSequence str, int count) {
- if (null == str) {
- return null;
- }
- if (count <= 0 || str.length() == 0) {
- return EMPTY;
- }
- if (count == 1) {
- return str.toString();
- }
-
- // 检查
- final int len = str.length();
- final long longSize = (long) len * (long) count;
- final int size = (int) longSize;
- if (size != longSize) {
- throw new ArrayIndexOutOfBoundsException("Required String length is too large: " + longSize);
- }
-
- final char[] array = new char[size];
- str.toString().getChars(0, len, array, 0);
- int n;
- for (n = len; n < size - n; n <<= 1) {// n <<= 1相当于n *2
- System.arraycopy(array, 0, array, n, n);
- }
- System.arraycopy(array, 0, array, n, size - n);
- return new String(array);
- }
-
- /**
- * 重复某个字符串到指定长度
- *
- * @param str 被重复的字符
- * @param padLen 指定长度
- * @return 重复字符字符串
- * @since 4.3.2
- */
- public static String repeatByLength(CharSequence str, int padLen) {
- if (null == str) {
- return null;
- }
- if (padLen <= 0) {
- return StrUtil.EMPTY;
- }
- final int strLen = str.length();
- if (strLen == padLen) {
- return str.toString();
- } else if (strLen > padLen) {
- return subPre(str, padLen);
- }
-
- // 重复,直到达到指定长度
- final char[] padding = new char[padLen];
- for (int i = 0; i < padLen; i++) {
- padding[i] = str.charAt(i % strLen);
- }
- return new String(padding);
- }
-
- /**
- * 重复某个字符串并通过分界符连接
- *
- *
- * StrUtil.repeatAndJoin("?", 5, ",") = "?,?,?,?,?"
- * StrUtil.repeatAndJoin("?", 0, ",") = ""
- * StrUtil.repeatAndJoin("?", 5, null) = "?????"
- *
- *
- * @param str 被重复的字符串
- * @param count 数量
- * @param conjunction 分界符
- * @return 连接后的字符串
- * @since 4.0.1
- */
- public static String repeatAndJoin(CharSequence str, int count, CharSequence conjunction) {
- if (count <= 0) {
- return EMPTY;
- }
- final StrBuilder builder = StrBuilder.create();
- boolean isFirst = true;
- while (count-- > 0) {
- if (isFirst) {
- isFirst = false;
- } else if (isNotEmpty(conjunction)) {
- builder.append(conjunction);
- }
- builder.append(str);
- }
- return builder.toString();
- }
-
- /**
- * 比较两个字符串(大小写敏感)。
- *
- *
- * equals(null, null) = true
- * equals(null, "abc") = false
- * equals("abc", null) = false
- * equals("abc", "abc") = true
- * equals("abc", "ABC") = false
- *
- *
- * @param str1 要比较的字符串1
- * @param str2 要比较的字符串2
- * @return 如果两个字符串相同,或者都是null
,则返回true
- */
- public static boolean equals(CharSequence str1, CharSequence str2) {
- return equals(str1, str2, false);
- }
-
- /**
- * 比较两个字符串(大小写不敏感)。
- *
- *
- * equalsIgnoreCase(null, null) = true
- * equalsIgnoreCase(null, "abc") = false
- * equalsIgnoreCase("abc", null) = false
- * equalsIgnoreCase("abc", "abc") = true
- * equalsIgnoreCase("abc", "ABC") = true
- *
- *
- * @param str1 要比较的字符串1
- * @param str2 要比较的字符串2
- * @return 如果两个字符串相同,或者都是null
,则返回true
- */
- public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
- return equals(str1, str2, true);
- }
-
- /**
- * 比较两个字符串是否相等。
- *
- * @param str1 要比较的字符串1
- * @param str2 要比较的字符串2
- * @param ignoreCase 是否忽略大小写
- * @return 如果两个字符串相同,或者都是null
,则返回true
- * @since 3.2.0
- */
- public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
- if (null == str1) {
- // 只有两个都为null才判断相等
- return str2 == null;
- }
- if (null == str2) {
- // 字符串2空,字符串1非空,直接false
- return false;
- }
-
- if (ignoreCase) {
- return str1.toString().equalsIgnoreCase(str2.toString());
- } else {
- return str1.toString().contentEquals(str2);
- }
- }
-
- /**
- * 给定字符串是否与提供的中任一字符串相同(忽略大小写),相同则返回{@code true},没有相同的返回{@code false}
- * 如果参与比对的字符串列表为空,返回{@code false}
- *
- * @param str1 给定需要检查的字符串
- * @param strs 需要参与比对的字符串列表
- * @return 是否相同
- * @since 4.3.2
- */
- public static boolean equalsAnyIgnoreCase(CharSequence str1, CharSequence... strs) {
- return equalsAny(str1, true, strs);
- }
-
- /**
- * 给定字符串是否与提供的中任一字符串相同,相同则返回{@code true},没有相同的返回{@code false}
- * 如果参与比对的字符串列表为空,返回{@code false}
- *
- * @param str1 给定需要检查的字符串
- * @param strs 需要参与比对的字符串列表
- * @return 是否相同
- * @since 4.3.2
- */
- public static boolean equalsAny(CharSequence str1, CharSequence... strs) {
- return equalsAny(str1, false, strs);
- }
-
- /**
- * 给定字符串是否与提供的中任一字符串相同,相同则返回{@code true},没有相同的返回{@code false}
- * 如果参与比对的字符串列表为空,返回{@code false}
- *
- * @param str1 给定需要检查的字符串
- * @param ignoreCase 是否忽略大小写
- * @param strs 需要参与比对的字符串列表
- * @return 是否相同
- * @since 4.3.2
- */
- public static boolean equalsAny(CharSequence str1, boolean ignoreCase, CharSequence... strs) {
- if (ArrayUtil.isEmpty(strs)) {
- return false;
- }
-
- for (CharSequence str : strs) {
- if (equals(str1, str, ignoreCase)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 格式化文本, {} 表示占位符
- * 此方法只是简单将占位符 {} 按照顺序替换为参数
- * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
- * 例:
- * 通常使用:format("this is {} for {}", "a", "b") =》 this is a for b
- * 转义{}: format("this is \\{} for {}", "a", "b") =》 this is \{} for a
- * 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b
- *
- * @param template 文本模板,被替换的部分用 {} 表示,如果模板为null,返回"null"
- * @param params 参数值
- * @return 格式化后的文本,如果模板为null,返回"null"
- */
- public static String format(CharSequence template, Object... params) {
- if (null == template) {
- return NULL;
- }
- if (ArrayUtil.isEmpty(params) || isBlank(template)) {
- return template.toString();
- }
- return StrFormatter.format(template.toString(), params);
- }
-
- /**
- * 有序的格式化文本,使用{number}做为占位符
- * 通常使用:format("this is {0} for {1}", "a", "b") =》 this is a for b
- *
- * @param pattern 文本格式
- * @param arguments 参数
- * @return 格式化后的文本
- */
- public static String indexedFormat(CharSequence pattern, Object... arguments) {
- return MessageFormat.format(pattern.toString(), arguments);
- }
-
- /**
- * 格式化文本,使用 {varName} 占位
- * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
- *
- * @param template 文本模板,被替换的部分用 {key} 表示
- * @param map 参数值对
- * @return 格式化后的文本
- */
- public static String format(CharSequence template, Map, ?> map) {
- return format(template, map, true);
- }
-
- /**
- * 格式化文本,使用 {varName} 占位
- * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
- *
- * @param template 文本模板,被替换的部分用 {key} 表示
- * @param map 参数值对
- * @param ignoreNull 是否忽略 {@code null} 值,忽略则 {@code null} 值对应的变量不被替换,否则替换为""
- * @return 格式化后的文本
- * @since 5.4.3
- */
- public static String format(CharSequence template, Map, ?> map, boolean ignoreNull) {
- if (null == template) {
- return null;
- }
- if (null == map || map.isEmpty()) {
- return template.toString();
- }
-
- String template2 = template.toString();
- String value;
- for (Entry, ?> entry : map.entrySet()) {
- value = utf8Str(entry.getValue());
- if (null == value && ignoreNull) {
- continue;
- }
- template2 = replace(template2, "{" + entry.getKey() + "}", value);
- }
- return template2;
- }
-
- /**
- * 编码字符串,编码为UTF-8
- *
- * @param str 字符串
- * @return 编码后的字节码
- */
- public static byte[] utf8Bytes(CharSequence str) {
- return bytes(str, CharsetUtil.CHARSET_UTF_8);
- }
-
- /**
- * 编码字符串
- * 使用系统默认编码
- *
- * @param str 字符串
- * @return 编码后的字节码
- */
- public static byte[] bytes(CharSequence str) {
- return bytes(str, Charset.defaultCharset());
- }
-
- /**
- * 编码字符串
- *
- * @param str 字符串
- * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
- * @return 编码后的字节码
- */
- public static byte[] bytes(CharSequence str, String charset) {
- return bytes(str, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));
- }
-
- /**
- * 编码字符串
- *
- * @param str 字符串
- * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
- * @return 编码后的字节码
- */
- public static byte[] bytes(CharSequence str, Charset charset) {
- if (str == null) {
- return null;
- }
-
- if (null == charset) {
- return str.toString().getBytes();
- }
- return str.toString().getBytes(charset);
- }
-
/**
* 将对象转为字符串
*
@@ -3000,16 +456,6 @@ public class StrUtil {
return charset.decode(data).toString();
}
- /**
- * {@link CharSequence} 转为字符串,null安全
- *
- * @param cs {@link CharSequence}
- * @return 字符串
- */
- public static String str(CharSequence cs) {
- return null == cs ? null : cs.toString();
- }
-
/**
* 调用对象的toString方法,null会返回“null”
*
@@ -3021,556 +467,6 @@ public class StrUtil {
return null == obj ? NULL : obj.toString();
}
- /**
- * 字符串转换为byteBuffer
- *
- * @param str 字符串
- * @param charset 编码
- * @return byteBuffer
- */
- public static ByteBuffer byteBuffer(CharSequence str, String charset) {
- return ByteBuffer.wrap(bytes(str, charset));
- }
-
- /**
- * 以 conjunction 为分隔符将多个对象转换为字符串
- *
- * @param conjunction 分隔符
- * @param objs 数组
- * @return 连接后的字符串
- * @see ArrayUtil#join(Object, CharSequence)
- */
- public static String join(CharSequence conjunction, Object... objs) {
- return ArrayUtil.join(objs, conjunction);
- }
-
- /**
- * 将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
- * 例如:
- *
- *
- * HelloWorld=》hello_world
- * Hello_World=》hello_world
- * HelloWorld_test=》hello_world_test
- *
- *
- * @param str 转换前的驼峰式命名的字符串,也可以为下划线形式
- * @return 转换后下划线方式命名的字符串
- */
- public static String toUnderlineCase(CharSequence str) {
- return toSymbolCase(str, CharUtil.UNDERLINE);
- }
-
- /**
- * 将驼峰式命名的字符串转换为使用符号连接方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
- *
- * @param str 转换前的驼峰式命名的字符串,也可以为符号连接形式
- * @param symbol 连接符
- * @return 转换后符号连接方式命名的字符串
- * @since 4.0.10
- */
- public static String toSymbolCase(CharSequence str, char symbol) {
- if (str == null) {
- return null;
- }
-
- final int length = str.length();
- final StrBuilder sb = new StrBuilder();
- char c;
- for (int i = 0; i < length; i++) {
- c = str.charAt(i);
- final Character preChar = (i > 0) ? str.charAt(i - 1) : null;
- if (Character.isUpperCase(c)) {
- // 遇到大写字母处理
- final Character nextChar = (i < str.length() - 1) ? str.charAt(i + 1) : null;
- if (null != preChar && Character.isUpperCase(preChar)) {
- // 前一个字符为大写,则按照一个词对待,例如AB
- sb.append(c);
- } else if (null != nextChar && (false == Character.isLowerCase(nextChar))) {
- // 后一个为非小写字母,按照一个词对待
- if (null != preChar && symbol != preChar) {
- // 前一个是非大写时按照新词对待,加连接符,例如xAB
- sb.append(symbol);
- }
- sb.append(c);
- } else {
- // 前后都为非大写按照新词对待
- if (null != preChar && symbol != preChar) {
- // 前一个非连接符,补充连接符
- sb.append(symbol);
- }
- sb.append(Character.toLowerCase(c));
- }
- } else {
- if (symbol != c
- && sb.length() > 0
- && Character.isUpperCase(sb.charAt(-1))
- && Character.isLowerCase(c)) {
- // 当结果中前一个字母为大写,当前为小写(非数字或字符),说明此字符为新词开始(连接符也表示新词)
- sb.append(symbol);
- }
- // 小写或符号
- sb.append(c);
- }
- }
- return sb.toString();
- }
-
- /**
- * 将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
- * 例如:hello_world=》helloWorld
- *
- * @param name 转换前的下划线大写方式命名的字符串
- * @return 转换后的驼峰式命名的字符串
- */
- public static String toCamelCase(CharSequence name) {
- if (null == name) {
- return null;
- }
-
- String name2 = name.toString();
- if (name2.contains(UNDERLINE)) {
- final StringBuilder sb = new StringBuilder(name2.length());
- boolean upperCase = false;
- for (int i = 0; i < name2.length(); i++) {
- char c = name2.charAt(i);
-
- if (c == CharUtil.UNDERLINE) {
- upperCase = true;
- } else if (upperCase) {
- sb.append(Character.toUpperCase(c));
- upperCase = false;
- } else {
- sb.append(Character.toLowerCase(c));
- }
- }
- return sb.toString();
- } else {
- return name2;
- }
- }
-
- /**
- * 包装指定字符串
- * 当前缀和后缀一致时使用此方法
- *
- * @param str 被包装的字符串
- * @param prefixAndSuffix 前缀和后缀
- * @return 包装后的字符串
- * @since 3.1.0
- */
- public static String wrap(CharSequence str, CharSequence prefixAndSuffix) {
- return wrap(str, prefixAndSuffix, prefixAndSuffix);
- }
-
- /**
- * 包装指定字符串
- *
- * @param str 被包装的字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 包装后的字符串
- */
- public static String wrap(CharSequence str, CharSequence prefix, CharSequence suffix) {
- return nullToEmpty(prefix).concat(nullToEmpty(str)).concat(nullToEmpty(suffix));
- }
-
- /**
- * 使用单个字符包装多个字符串
- *
- * @param prefixAndSuffix 前缀和后缀
- * @param strs 多个字符串
- * @return 包装的字符串数组
- * @since 5.4.1
- */
- public static String[] wrapAllWithPair(CharSequence prefixAndSuffix, CharSequence... strs) {
- return wrapAll(prefixAndSuffix, prefixAndSuffix, strs);
- }
-
- /**
- * 包装多个字符串
- *
- * @param prefix 前缀
- * @param suffix 后缀
- * @param strs 多个字符串
- * @return 包装的字符串数组
- * @since 4.0.7
- */
- public static String[] wrapAll(CharSequence prefix, CharSequence suffix, CharSequence... strs) {
- final String[] results = new String[strs.length];
- for (int i = 0; i < strs.length; i++) {
- results[i] = wrap(strs[i], prefix, suffix);
- }
- return results;
- }
-
- /**
- * 包装指定字符串,如果前缀或后缀已经包含对应的字符串,则不再包装
- *
- * @param str 被包装的字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 包装后的字符串
- */
- public static String wrapIfMissing(CharSequence str, CharSequence prefix, CharSequence suffix) {
- int len = 0;
- if (isNotEmpty(str)) {
- len += str.length();
- }
- if (isNotEmpty(prefix)) {
- len += str.length();
- }
- if (isNotEmpty(suffix)) {
- len += str.length();
- }
- StringBuilder sb = new StringBuilder(len);
- if (isNotEmpty(prefix) && false == startWith(str, prefix)) {
- sb.append(prefix);
- }
- if (isNotEmpty(str)) {
- sb.append(str);
- }
- if (isNotEmpty(suffix) && false == endWith(str, suffix)) {
- sb.append(suffix);
- }
- return sb.toString();
- }
-
- /**
- * 使用成对的字符包装多个字符串,如果已经包装,则不再包装
- *
- * @param prefixAndSuffix 前缀和后缀
- * @param strs 多个字符串
- * @return 包装的字符串数组
- * @since 5.4.1
- */
- public static String[] wrapAllWithPairIfMissing(CharSequence prefixAndSuffix, CharSequence... strs) {
- return wrapAllIfMissing(prefixAndSuffix, prefixAndSuffix, strs);
- }
-
- /**
- * 包装多个字符串,如果已经包装,则不再包装
- *
- * @param prefix 前缀
- * @param suffix 后缀
- * @param strs 多个字符串
- * @return 包装的字符串数组
- * @since 4.0.7
- */
- public static String[] wrapAllIfMissing(CharSequence prefix, CharSequence suffix, CharSequence... strs) {
- final String[] results = new String[strs.length];
- for (int i = 0; i < strs.length; i++) {
- results[i] = wrapIfMissing(strs[i], prefix, suffix);
- }
- return results;
- }
-
- /**
- * 去掉字符包装,如果未被包装则返回原字符串
- *
- * @param str 字符串
- * @param prefix 前置字符串
- * @param suffix 后置字符串
- * @return 去掉包装字符的字符串
- * @since 4.0.1
- */
- public static String unWrap(CharSequence str, String prefix, String suffix) {
- if (isWrap(str, prefix, suffix)) {
- return sub(str, prefix.length(), str.length() - suffix.length());
- }
- return str.toString();
- }
-
- /**
- * 去掉字符包装,如果未被包装则返回原字符串
- *
- * @param str 字符串
- * @param prefix 前置字符
- * @param suffix 后置字符
- * @return 去掉包装字符的字符串
- * @since 4.0.1
- */
- public static String unWrap(CharSequence str, char prefix, char suffix) {
- if (isEmpty(str)) {
- return str(str);
- }
- if (str.charAt(0) == prefix && str.charAt(str.length() - 1) == suffix) {
- return sub(str, 1, str.length() - 1);
- }
- return str.toString();
- }
-
- /**
- * 去掉字符包装,如果未被包装则返回原字符串
- *
- * @param str 字符串
- * @param prefixAndSuffix 前置和后置字符
- * @return 去掉包装字符的字符串
- * @since 4.0.1
- */
- public static String unWrap(CharSequence str, char prefixAndSuffix) {
- return unWrap(str, prefixAndSuffix, prefixAndSuffix);
- }
-
- /**
- * 指定字符串是否被包装
- *
- * @param str 字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @return 是否被包装
- */
- public static boolean isWrap(CharSequence str, String prefix, String suffix) {
- if (ArrayUtil.hasNull(str, prefix, suffix)) {
- return false;
- }
- final String str2 = str.toString();
- return str2.startsWith(prefix) && str2.endsWith(suffix);
- }
-
- /**
- * 指定字符串是否被同一字符包装(前后都有这些字符串)
- *
- * @param str 字符串
- * @param wrapper 包装字符串
- * @return 是否被包装
- */
- public static boolean isWrap(CharSequence str, String wrapper) {
- return isWrap(str, wrapper, wrapper);
- }
-
- /**
- * 指定字符串是否被同一字符包装(前后都有这些字符串)
- *
- * @param str 字符串
- * @param wrapper 包装字符
- * @return 是否被包装
- */
- public static boolean isWrap(CharSequence str, char wrapper) {
- return isWrap(str, wrapper, wrapper);
- }
-
- /**
- * 指定字符串是否被包装
- *
- * @param str 字符串
- * @param prefixChar 前缀
- * @param suffixChar 后缀
- * @return 是否被包装
- */
- public static boolean isWrap(CharSequence str, char prefixChar, char suffixChar) {
- if (null == str) {
- return false;
- }
-
- return str.charAt(0) == prefixChar && str.charAt(str.length() - 1) == suffixChar;
- }
-
- /**
- * 补充字符串以满足最小长度
- *
- *
- * StrUtil.padPre(null, *, *);//null
- * StrUtil.padPre("1", 3, "ABC");//"AB1"
- * StrUtil.padPre("123", 2, "ABC");//"12"
- *
- *
- * @param str 字符串
- * @param minLength 最小长度
- * @param padStr 补充的字符
- * @return 补充后的字符串
- */
- public static String padPre(CharSequence str, int minLength, CharSequence padStr) {
- if (null == str) {
- return null;
- }
- final int strLen = str.length();
- if (strLen == minLength) {
- return str.toString();
- } else if (strLen > minLength) {
- return subPre(str, minLength);
- }
-
- return repeatByLength(padStr, minLength - strLen).concat(str.toString());
- }
-
- /**
- * 补充字符串以满足最小长度
- *
- *
- * StrUtil.padPre(null, *, *);//null
- * StrUtil.padPre("1", 3, '0');//"001"
- * StrUtil.padPre("123", 2, '0');//"12"
- *
- *
- * @param str 字符串
- * @param minLength 最小长度
- * @param padChar 补充的字符
- * @return 补充后的字符串
- */
- public static String padPre(CharSequence str, int minLength, char padChar) {
- if (null == str) {
- return null;
- }
- final int strLen = str.length();
- if (strLen == minLength) {
- return str.toString();
- } else if (strLen > minLength) {
- return subPre(str, minLength);
- }
-
- return repeat(padChar, minLength - strLen).concat(str.toString());
- }
-
- /**
- * 补充字符串以满足最小长度
- *
- *
- * StrUtil.padAfter(null, *, *);//null
- * StrUtil.padAfter("1", 3, '0');//"100"
- * StrUtil.padAfter("123", 2, '0');//"23"
- *
- *
- * @param str 字符串,如果为null
,直接返回null
- * @param minLength 最小长度
- * @param padChar 补充的字符
- * @return 补充后的字符串
- */
- public static String padAfter(CharSequence str, int minLength, char padChar) {
- if (null == str) {
- return null;
- }
- final int strLen = str.length();
- if (strLen == minLength) {
- return str.toString();
- } else if (strLen > minLength) {
- return sub(str, strLen - minLength, strLen);
- }
-
- return str.toString().concat(repeat(padChar, minLength - strLen));
- }
-
- /**
- * 补充字符串以满足最小长度
- *
- *
- * StrUtil.padAfter(null, *, *);//null
- * StrUtil.padAfter("1", 3, "ABC");//"1AB"
- * StrUtil.padAfter("123", 2, "ABC");//"23"
- *
- *
- * @param str 字符串,如果为null
,直接返回null
- * @param minLength 最小长度
- * @param padStr 补充的字符
- * @return 补充后的字符串
- * @since 4.3.2
- */
- public static String padAfter(CharSequence str, int minLength, CharSequence padStr) {
- if (null == str) {
- return null;
- }
- final int strLen = str.length();
- if (strLen == minLength) {
- return str.toString();
- } else if (strLen > minLength) {
- return subSufByLength(str, minLength);
- }
-
- return str.toString().concat(repeatByLength(padStr, minLength - strLen));
- }
-
- /**
- * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
- *
- *
- * StrUtil.center(null, *) = null
- * StrUtil.center("", 4) = " "
- * StrUtil.center("ab", -1) = "ab"
- * StrUtil.center("ab", 4) = " ab "
- * StrUtil.center("abcd", 2) = "abcd"
- * StrUtil.center("a", 4) = " a "
- *
- *
- * @param str 字符串
- * @param size 指定长度
- * @return 补充后的字符串
- * @since 4.3.2
- */
- public static String center(CharSequence str, final int size) {
- return center(str, size, CharUtil.SPACE);
- }
-
- /**
- * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
- *
- *
- * StrUtil.center(null, *, *) = null
- * StrUtil.center("", 4, ' ') = " "
- * StrUtil.center("ab", -1, ' ') = "ab"
- * StrUtil.center("ab", 4, ' ') = " ab "
- * StrUtil.center("abcd", 2, ' ') = "abcd"
- * StrUtil.center("a", 4, ' ') = " a "
- * StrUtil.center("a", 4, 'y') = "yayy"
- * StrUtil.center("abc", 7, ' ') = " abc "
- *
- *
- * @param str 字符串
- * @param size 指定长度
- * @param padChar 两边补充的字符
- * @return 补充后的字符串
- * @since 4.3.2
- */
- public static String center(CharSequence str, final int size, char padChar) {
- if (str == null || size <= 0) {
- return str(str);
- }
- final int strLen = str.length();
- final int pads = size - strLen;
- if (pads <= 0) {
- return str.toString();
- }
- str = padPre(str, strLen + pads / 2, padChar);
- str = padAfter(str, size, padChar);
- return str.toString();
- }
-
- /**
- * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
- *
- *
- * StrUtil.center(null, *, *) = null
- * StrUtil.center("", 4, " ") = " "
- * StrUtil.center("ab", -1, " ") = "ab"
- * StrUtil.center("ab", 4, " ") = " ab "
- * StrUtil.center("abcd", 2, " ") = "abcd"
- * StrUtil.center("a", 4, " ") = " a "
- * StrUtil.center("a", 4, "yz") = "yayz"
- * StrUtil.center("abc", 7, null) = " abc "
- * StrUtil.center("abc", 7, "") = " abc "
- *
- *
- * @param str 字符串
- * @param size 指定长度
- * @param padStr 两边补充的字符串
- * @return 补充后的字符串
- */
- public static String center(CharSequence str, final int size, CharSequence padStr) {
- if (str == null || size <= 0) {
- return str(str);
- }
- if (isEmpty(padStr)) {
- padStr = SPACE;
- }
- final int strLen = str.length();
- final int pads = size - strLen;
- if (pads <= 0) {
- return str.toString();
- }
- str = padPre(str, strLen + pads / 2, padStr);
- str = padAfter(str, size, padStr);
- return str.toString();
- }
-
/**
* 创建StringBuilder对象
*
@@ -3611,30 +507,6 @@ public class StrUtil {
return StrBuilder.create(capacity);
}
- /**
- * 创建StringBuilder对象
- *
- * @param strs 初始字符串列表
- * @return StringBuilder对象
- */
- public static StringBuilder builder(CharSequence... strs) {
- final StringBuilder sb = new StringBuilder();
- for (CharSequence str : strs) {
- sb.append(str);
- }
- return sb;
- }
-
- /**
- * 创建StrBuilder对象
- *
- * @param strs 初始字符串列表
- * @return StrBuilder对象
- */
- public static StrBuilder strBuilder(CharSequence... strs) {
- return StrBuilder.create(strs);
- }
-
/**
* 获得StringReader
*
@@ -3657,566 +529,6 @@ public class StrUtil {
return new StringWriter();
}
- /**
- * 统计指定内容中包含指定字符串的数量
- * 参数为 {@code null} 或者 "" 返回 {@code 0}.
- *
- *
- * StrUtil.count(null, *) = 0
- * StrUtil.count("", *) = 0
- * StrUtil.count("abba", null) = 0
- * StrUtil.count("abba", "") = 0
- * StrUtil.count("abba", "a") = 2
- * StrUtil.count("abba", "ab") = 1
- * StrUtil.count("abba", "xxx") = 0
- *
- *
- * @param content 被查找的字符串
- * @param strForSearch 需要查找的字符串
- * @return 查找到的个数
- */
- public static int count(CharSequence content, CharSequence strForSearch) {
- if (hasEmpty(content, strForSearch) || strForSearch.length() > content.length()) {
- return 0;
- }
-
- int count = 0;
- int idx = 0;
- final String content2 = content.toString();
- final String strForSearch2 = strForSearch.toString();
- while ((idx = content2.indexOf(strForSearch2, idx)) > -1) {
- count++;
- idx += strForSearch.length();
- }
- return count;
- }
-
- /**
- * 统计指定内容中包含指定字符的数量
- *
- * @param content 内容
- * @param charForSearch 被统计的字符
- * @return 包含数量
- */
- public static int count(CharSequence content, char charForSearch) {
- int count = 0;
- if (isEmpty(content)) {
- return 0;
- }
- int contentLength = content.length();
- for (int i = 0; i < contentLength; i++) {
- if (charForSearch == content.charAt(i)) {
- count++;
- }
- }
- return count;
- }
-
- /**
- * 将字符串切分为N等份
- *
- * @param str 字符串
- * @param partLength 每等份的长度
- * @return 切分后的数组
- * @since 3.0.6
- */
- public static String[] cut(CharSequence str, int partLength) {
- if (null == str) {
- return null;
- }
- int len = str.length();
- if (len < partLength) {
- return new String[]{str.toString()};
- }
- int part = NumberUtil.count(len, partLength);
- final String[] array = new String[part];
-
- final String str2 = str.toString();
- for (int i = 0; i < part; i++) {
- array[i] = str2.substring(i * partLength, (i == part - 1) ? len : (partLength + i * partLength));
- }
- return array;
- }
-
- /**
- * 将给定字符串,变成 "xxx...xxx" 形式的字符串
- *
- * @param str 字符串
- * @param maxLength 最大长度
- * @return 截取后的字符串
- */
- public static String brief(CharSequence str, int maxLength) {
- if (null == str) {
- return null;
- }
- if (str.length() <= maxLength) {
- return str.toString();
- }
- int w = maxLength / 2;
- int l = str.length() + 3;
-
- final String str2 = str.toString();
- return format("{}...{}", str2.substring(0, maxLength - w), str2.substring(l - w));
- }
-
- /**
- * 比较两个字符串,用于排序
- *
- *
- * StrUtil.compare(null, null, *) = 0
- * StrUtil.compare(null , "a", true) < 0
- * StrUtil.compare(null , "a", false) > 0
- * StrUtil.compare("a", null, true) > 0
- * StrUtil.compare("a", null, false) < 0
- * StrUtil.compare("abc", "abc", *) = 0
- * StrUtil.compare("a", "b", *) < 0
- * StrUtil.compare("b", "a", *) > 0
- * StrUtil.compare("a", "B", *) > 0
- * StrUtil.compare("ab", "abc", *) < 0
- *
- *
- * @param str1 字符串1
- * @param str2 字符串2
- * @param nullIsLess {@code null} 值是否排在前(null是否小于非空值)
- * @return 排序值。负数:str1 < str2,正数:str1 > str2, 0:str1 == str2
- */
- public static int compare(final CharSequence str1, final CharSequence str2, final boolean nullIsLess) {
- if (str1 == str2) {
- return 0;
- }
- if (str1 == null) {
- return nullIsLess ? -1 : 1;
- }
- if (str2 == null) {
- return nullIsLess ? 1 : -1;
- }
- return str1.toString().compareTo(str2.toString());
- }
-
- /**
- * 比较两个字符串,用于排序,大小写不敏感
- *
- *
- * StrUtil.compareIgnoreCase(null, null, *) = 0
- * StrUtil.compareIgnoreCase(null , "a", true) < 0
- * StrUtil.compareIgnoreCase(null , "a", false) > 0
- * StrUtil.compareIgnoreCase("a", null, true) > 0
- * StrUtil.compareIgnoreCase("a", null, false) < 0
- * StrUtil.compareIgnoreCase("abc", "abc", *) = 0
- * StrUtil.compareIgnoreCase("abc", "ABC", *) = 0
- * StrUtil.compareIgnoreCase("a", "b", *) < 0
- * StrUtil.compareIgnoreCase("b", "a", *) > 0
- * StrUtil.compareIgnoreCase("a", "B", *) < 0
- * StrUtil.compareIgnoreCase("A", "b", *) < 0
- * StrUtil.compareIgnoreCase("ab", "abc", *) < 0
- *
- *
- * @param str1 字符串1
- * @param str2 字符串2
- * @param nullIsLess {@code null} 值是否排在前(null是否小于非空值)
- * @return 排序值。负数:str1 < str2,正数:str1 > str2, 0:str1 == str2
- */
- public static int compareIgnoreCase(CharSequence str1, CharSequence str2, boolean nullIsLess) {
- if (str1 == str2) {
- return 0;
- }
- if (str1 == null) {
- return nullIsLess ? -1 : 1;
- }
- if (str2 == null) {
- return nullIsLess ? 1 : -1;
- }
- return str1.toString().compareToIgnoreCase(str2.toString());
- }
-
- /**
- * 比较两个版本
- * null版本排在最小:即:
- *
- *
- * StrUtil.compareVersion(null, "v1") < 0
- * StrUtil.compareVersion("v1", "v1") = 0
- * StrUtil.compareVersion(null, null) = 0
- * StrUtil.compareVersion("v1", null) > 0
- * StrUtil.compareVersion("1.0.0", "1.0.2") < 0
- * StrUtil.compareVersion("1.0.2", "1.0.2a") < 0
- * StrUtil.compareVersion("1.13.0", "1.12.1c") > 0
- * StrUtil.compareVersion("V0.0.20170102", "V0.0.20170101") > 0
- *
- *
- * @param version1 版本1
- * @param version2 版本2
- * @return 排序值。负数:version1 < version2,正数:version1 > version2, 0:version1 == version2
- * @since 4.0.2
- */
- public static int compareVersion(CharSequence version1, CharSequence version2) {
- return VersionComparator.INSTANCE.compare(str(version1), str(version2));
- }
-
- /**
- * 指定范围内查找指定字符
- *
- * @param str 字符串
- * @param searchChar 被查找的字符
- * @return 位置
- */
- public static int indexOf(final CharSequence str, char searchChar) {
- return indexOf(str, searchChar, 0);
- }
-
- /**
- * 指定范围内查找指定字符
- *
- * @param str 字符串
- * @param searchChar 被查找的字符
- * @param start 起始位置,如果小于0,从0开始查找
- * @return 位置
- */
- public static int indexOf(CharSequence str, char searchChar, int start) {
- if (str instanceof String) {
- return ((String) str).indexOf(searchChar, start);
- } else {
- return indexOf(str, searchChar, start, -1);
- }
- }
-
- /**
- * 指定范围内查找指定字符
- *
- * @param str 字符串
- * @param searchChar 被查找的字符
- * @param start 起始位置,如果小于0,从0开始查找
- * @param end 终止位置,如果超过str.length()则默认查找到字符串末尾
- * @return 位置
- */
- public static int indexOf(final CharSequence str, char searchChar, int start, int end) {
- if (isEmpty(str)) {
- return INDEX_NOT_FOUND;
- }
- final int len = str.length();
- if (start < 0 || start > len) {
- start = 0;
- }
- if (end > len || end < 0) {
- end = len;
- }
- for (int i = start; i < end; i++) {
- if (str.charAt(i) == searchChar) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * 指定范围内查找字符串,忽略大小写
- *
- *
- * StrUtil.indexOfIgnoreCase(null, *, *) = -1
- * StrUtil.indexOfIgnoreCase(*, null, *) = -1
- * StrUtil.indexOfIgnoreCase("", "", 0) = 0
- * StrUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
- * StrUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
- * StrUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
- * StrUtil.indexOfIgnoreCase("abc", "", 9) = -1
- *
- *
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @return 位置
- * @since 3.2.1
- */
- public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
- return indexOfIgnoreCase(str, searchStr, 0);
- }
-
- /**
- * 指定范围内查找字符串
- *
- *
- * StrUtil.indexOfIgnoreCase(null, *, *) = -1
- * StrUtil.indexOfIgnoreCase(*, null, *) = -1
- * StrUtil.indexOfIgnoreCase("", "", 0) = 0
- * StrUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
- * StrUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
- * StrUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
- * StrUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
- * StrUtil.indexOfIgnoreCase("abc", "", 9) = -1
- *
- *
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @param fromIndex 起始位置
- * @return 位置
- * @since 3.2.1
- */
- public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int fromIndex) {
- return indexOf(str, searchStr, fromIndex, true);
- }
-
- /**
- * 指定范围内查找字符串
- *
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @param fromIndex 起始位置
- * @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;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * 指定范围内查找字符串,忽略大小写
- *
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @return 位置
- * @since 3.2.1
- */
- public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
- return lastIndexOfIgnoreCase(str, searchStr, str.length());
- }
-
- /**
- * 指定范围内查找字符串,忽略大小写
- * fromIndex 为搜索起始位置,从后往前计数
- *
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @param fromIndex 起始位置,从后往前计数
- * @return 位置
- * @since 3.2.1
- */
- public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int fromIndex) {
- return lastIndexOf(str, searchStr, fromIndex, true);
- }
-
- /**
- * 指定范围内查找字符串
- * fromIndex 为搜索起始位置,从后往前计数
- *
- * @param str 字符串
- * @param searchStr 需要查找位置的字符串
- * @param fromIndex 起始位置,从后往前计数
- * @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;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * 返回字符串 searchStr 在字符串 str 中第 ordinal 次出现的位置。
- *
- *
- * 此方法来自:Apache-Commons-Lang
- *
- * StrUtil.ordinalIndexOf(null, *, *) = -1
- * StrUtil.ordinalIndexOf(*, null, *) = -1
- * StrUtil.ordinalIndexOf("", "", *) = 0
- * StrUtil.ordinalIndexOf("aabaabaa", "a", 1) = 0
- * StrUtil.ordinalIndexOf("aabaabaa", "a", 2) = 1
- * StrUtil.ordinalIndexOf("aabaabaa", "b", 1) = 2
- * StrUtil.ordinalIndexOf("aabaabaa", "b", 2) = 5
- * StrUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1
- * StrUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4
- * StrUtil.ordinalIndexOf("aabaabaa", "", 1) = 0
- * StrUtil.ordinalIndexOf("aabaabaa", "", 2) = 0
- *
- *
- * @param str 被检查的字符串,可以为null
- * @param searchStr 被查找的字符串,可以为null
- * @param ordinal 第几次出现的位置
- * @return 查找到的位置
- * @since 3.2.3
- */
- public static int ordinalIndexOf(String str, String searchStr, int ordinal) {
- if (str == null || searchStr == null || ordinal <= 0) {
- return INDEX_NOT_FOUND;
- }
- if (searchStr.length() == 0) {
- return 0;
- }
- int found = 0;
- int index = INDEX_NOT_FOUND;
- do {
- index = str.indexOf(searchStr, index + 1);
- if (index < 0) {
- return index;
- }
- found++;
- } while (found < ordinal);
- return index;
- }
-
- // ------------------------------------------------------------------------------------------------------------------ Append and prepend
-
- /**
- * 如果给定字符串不是以给定的一个或多个字符串为结尾,则在尾部添加结尾字符串
- * 不忽略大小写
- *
- * @param str 被检查的字符串
- * @param suffix 需要添加到结尾的字符串
- * @param suffixes 需要额外检查的结尾字符串,如果以这些中的一个为结尾,则不再添加
- * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
- * @since 3.0.7
- */
- public static String appendIfMissing(final CharSequence str, final CharSequence suffix, final CharSequence... suffixes) {
- return appendIfMissing(str, suffix, false, suffixes);
- }
-
- /**
- * 如果给定字符串不是以给定的一个或多个字符串为结尾,则在尾部添加结尾字符串
- * 忽略大小写
- *
- * @param str 被检查的字符串
- * @param suffix 需要添加到结尾的字符串
- * @param suffixes 需要额外检查的结尾字符串,如果以这些中的一个为结尾,则不再添加
- * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
- * @since 3.0.7
- */
- public static String appendIfMissingIgnoreCase(final CharSequence str, final CharSequence suffix, final CharSequence... suffixes) {
- return appendIfMissing(str, suffix, true, suffixes);
- }
-
- /**
- * 如果给定字符串不是以给定的一个或多个字符串为结尾,则在尾部添加结尾字符串
- *
- * @param str 被检查的字符串
- * @param suffix 需要添加到结尾的字符串
- * @param ignoreCase 检查结尾时是否忽略大小写
- * @param suffixes 需要额外检查的结尾字符串,如果以这些中的一个为结尾,则不再添加
- * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
- * @since 3.0.7
- */
- public static String appendIfMissing(final CharSequence str, final CharSequence suffix, final boolean ignoreCase, final CharSequence... suffixes) {
- if (str == null || isEmpty(suffix) || endWith(str, suffix, ignoreCase)) {
- return str(str);
- }
- if (suffixes != null && suffixes.length > 0) {
- for (final CharSequence s : suffixes) {
- if (endWith(str, s, ignoreCase)) {
- return str.toString();
- }
- }
- }
- return str.toString().concat(suffix.toString());
- }
-
- /**
- * 如果给定字符串不是以给定的一个或多个字符串为开头,则在首部添加起始字符串
- * 不忽略大小写
- *
- * @param str 被检查的字符串
- * @param prefix 需要添加到首部的字符串
- * @param prefixes 需要额外检查的首部字符串,如果以这些中的一个为起始,则不再添加
- * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
- * @since 3.0.7
- */
- public static String prependIfMissing(final CharSequence str, final CharSequence prefix, final CharSequence... prefixes) {
- return prependIfMissing(str, prefix, false, prefixes);
- }
-
- /**
- * 如果给定字符串不是以给定的一个或多个字符串为开头,则在首部添加起始字符串
- * 忽略大小写
- *
- * @param str 被检查的字符串
- * @param prefix 需要添加到首部的字符串
- * @param prefixes 需要额外检查的首部字符串,如果以这些中的一个为起始,则不再添加
- * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
- * @since 3.0.7
- */
- public static String prependIfMissingIgnoreCase(final CharSequence str, final CharSequence prefix, final CharSequence... prefixes) {
- return prependIfMissing(str, prefix, true, prefixes);
- }
-
- /**
- * 如果给定字符串不是以给定的一个或多个字符串为开头,则在首部添加起始字符串
- *
- * @param str 被检查的字符串
- * @param prefix 需要添加到首部的字符串
- * @param ignoreCase 检查结尾时是否忽略大小写
- * @param prefixes 需要额外检查的首部字符串,如果以这些中的一个为起始,则不再添加
- * @return 如果已经结尾,返回原字符串,否则返回添加结尾的字符串
- * @since 3.0.7
- */
- public static String prependIfMissing(final CharSequence str, final CharSequence prefix, final boolean ignoreCase, final CharSequence... prefixes) {
- if (str == null || isEmpty(prefix) || startWith(str, prefix, ignoreCase)) {
- return str(str);
- }
- if (prefixes != null && prefixes.length > 0) {
- for (final CharSequence s : prefixes) {
- if (startWith(str, s, ignoreCase)) {
- return str.toString();
- }
- }
- }
- return prefix.toString().concat(str.toString());
- }
-
/**
* 反转字符串
* 例如:abcd =》dcba
@@ -4229,6 +541,8 @@ public class StrUtil {
return new String(ArrayUtil.reverse(str.toCharArray()));
}
+ // ------------------------------------------------------------------------ fill
+
/**
* 将已有字符串填充为规定长度,如果已有字符串超过这个长度则返回这个字符串
* 字符填充于字符串前
@@ -4277,258 +591,6 @@ public class StrUtil {
return isPre ? filledStr.concat(str) : str.concat(filledStr);
}
- /**
- * 截取两个字符串的不同部分(长度一致),判断截取的子串是否相同
- * 任意一个字符串为null返回false
- *
- * @param str1 第一个字符串
- * @param start1 第一个字符串开始的位置
- * @param str2 第二个字符串
- * @param start2 第二个字符串开始的位置
- * @param length 截取长度
- * @param ignoreCase 是否忽略大小写
- * @return 子串是否相同
- * @since 3.2.1
- */
- public static boolean isSubEquals(CharSequence str1, int start1, CharSequence str2, int start2, int length, boolean ignoreCase) {
- if (null == str1 || null == str2) {
- return false;
- }
-
- return str1.toString().regionMatches(ignoreCase, start1, str2.toString(), start2, length);
- }
-
- /**
- * 字符串的每一个字符是否都与定义的匹配器匹配
- *
- * @param value 字符串
- * @param matcher 匹配器
- * @return 是否全部匹配
- * @since 3.2.3
- */
- public static boolean isAllCharMatch(CharSequence value, Matcher
- * 提供的chars为所有需要被替换的字符,例如:"\r\n",则"\r"和"\n"都会被替换,哪怕他们单独存在
- *
- * @param str 被检查的字符串
- * @param chars 需要替换的字符列表,用一个字符串表示这个字符列表
- * @param replacedStr 替换成的字符串
- * @return 新字符串
- * @since 3.2.2
- */
- public static String replaceChars(CharSequence str, String chars, CharSequence replacedStr) {
- if (isEmpty(str) || isEmpty(chars)) {
- return str(str);
- }
- return replaceChars(str, chars.toCharArray(), replacedStr);
- }
-
- /**
- * 替换字符字符数组中所有的字符为replacedStr
- *
- * @param str 被检查的字符串
- * @param chars 需要替换的字符列表
- * @param replacedStr 替换成的字符串
- * @return 新字符串
- * @since 3.2.2
- */
- public static String replaceChars(CharSequence str, char[] chars, CharSequence replacedStr) {
- if (isEmpty(str) || ArrayUtil.isEmpty(chars)) {
- return str(str);
- }
-
- final Set
- * 如果字符串为null,返回false
- * 如果给定的位置大于字符串长度,返回false
- * 如果给定的位置小于0,返回false
- *
- * @param str 字符串
- * @param position 位置
- * @param c 需要对比的字符
- * @return 字符串指定位置的字符是否与给定字符相同
- * @since 3.3.1
- */
- public static boolean equalsCharAt(CharSequence str, int position, char c) {
- if (null == str || position < 0) {
- return false;
- }
- return str.length() > position && c == str.charAt(position);
- }
-
- /**
- * 给定字符串数组的总长度
- * null字符长度定义为0
- *
- * @param strs 字符串数组
- * @return 总长度
- * @since 4.0.1
- */
- public static int totalLength(CharSequence... strs) {
- int totalLength = 0;
- for (CharSequence str : strs) {
- totalLength += (null == str ? 0 : str.length());
- }
- return totalLength;
- }
-
- /**
- * 循环位移指定位置的字符串为指定距离
- * 当moveLength大于0向右位移,小于0向左位移,0不位移
- * 当moveLength大于字符串长度时采取循环位移策略,即位移到头后从头(尾)位移,例如长度为10,位移13则表示位移3
- *
- * @param str 字符串
- * @param startInclude 起始位置(包括)
- * @param endExclude 结束位置(不包括)
- * @param moveLength 移动距离,负数表示左移,正数为右移
- * @return 位移后的字符串
- * @since 4.0.7
- */
- public static String move(CharSequence str, int startInclude, int endExclude, int moveLength) {
- if (isEmpty(str)) {
- return str(str);
- }
- int len = str.length();
- if (Math.abs(moveLength) > len) {
- // 循环位移,当越界时循环
- moveLength = moveLength % len;
- }
- final StrBuilder strBuilder = StrBuilder.create(len);
- if (moveLength > 0) {
- int endAfterMove = Math.min(endExclude + moveLength, str.length());
- strBuilder.append(str.subSequence(0, startInclude))//
- .append(str.subSequence(endExclude, endAfterMove))//
- .append(str.subSequence(startInclude, endExclude))//
- .append(str.subSequence(endAfterMove, str.length()));
- } else if (moveLength < 0) {
- int startAfterMove = Math.max(startInclude + moveLength, 0);
- strBuilder.append(str.subSequence(0, startAfterMove))//
- .append(str.subSequence(startInclude, endExclude))//
- .append(str.subSequence(startAfterMove, startInclude))//
- .append(str.subSequence(endExclude, str.length()));
- } else {
- return str(str);
- }
- return strBuilder.toString();
- }
-
/**
* 生成随机UUID
*
@@ -4641,190 +628,44 @@ public class StrUtil {
}
/**
- * 连接多个字符串为一个
+ * 格式化文本,使用 {varName} 占位
+ * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
*
- * @param isNullToEmpty 是否null转为""
- * @param strs 字符串数组
- * @return 连接后的字符串
- * @since 4.1.0
+ * @param template 文本模板,被替换的部分用 {key} 表示
+ * @param map 参数值对
+ * @return 格式化后的文本
*/
- public static String concat(boolean isNullToEmpty, CharSequence... strs) {
- final StrBuilder sb = new StrBuilder();
- for (CharSequence str : strs) {
- sb.append(isNullToEmpty ? nullToEmpty(str) : str);
- }
- return sb.toString();
+ public static String format(CharSequence template, Map, ?> map) {
+ return format(template, map, true);
}
/**
- * 给定字符串中的字母是否全部为大写,判断依据如下:
+ * 格式化文本,使用 {varName} 占位
+ * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
*
- *
- * 1. 大写字母包括A-Z
- * 2. 其它非字母的Unicode符都算作大写
- *
- *
- * @param str 被检查的字符串
- * @return 是否全部为大写
- * @since 4.2.2
+ * @param template 文本模板,被替换的部分用 {key} 表示
+ * @param map 参数值对
+ * @param ignoreNull 是否忽略 {@code null} 值,忽略则 {@code null} 值对应的变量不被替换,否则替换为""
+ * @return 格式化后的文本
+ * @since 5.4.3
*/
- public static boolean isUpperCase(CharSequence str) {
- if (null == str) {
- return false;
+ public static String format(CharSequence template, Map, ?> map, boolean ignoreNull) {
+ if (null == template) {
+ return null;
}
- final int len = str.length();
- for (int i = 0; i < len; i++) {
- if (Character.isLowerCase(str.charAt(i))) {
- return false;
+ if (null == map || map.isEmpty()) {
+ return template.toString();
+ }
+
+ String template2 = template.toString();
+ String value;
+ for (Map.Entry, ?> entry : map.entrySet()) {
+ value = utf8Str(entry.getValue());
+ if (null == value && ignoreNull) {
+ continue;
}
+ template2 = replace(template2, "{" + entry.getKey() + "}", value);
}
- return true;
- }
-
- /**
- * 给定字符串中的字母是否全部为小写,判断依据如下:
- *
- *
- * 1. 小写字母包括a-z
- * 2. 其它非字母的Unicode符都算作小写
- *
- *
- * @param str 被检查的字符串
- * @return 是否全部为小写
- * @since 4.2.2
- */
- public static boolean isLowerCase(CharSequence str) {
- if (null == str) {
- return false;
- }
- final int len = str.length();
- for (int i = 0; i < len; i++) {
- if (Character.isUpperCase(str.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * 获取字符串的长度,如果为null返回0
- *
- * @param cs a 字符串
- * @return 字符串的长度,如果为null返回0
- * @since 4.3.2
- */
- public static int length(CharSequence cs) {
- return cs == null ? 0 : cs.length();
- }
-
- /**
- * 给定字符串转为bytes后的byte数(byte长度)
- *
- * @param cs 字符串
- * @param charset 编码
- * @return byte长度
- * @since 4.5.2
- */
- public static int byteLength(CharSequence cs, Charset charset) {
- return cs == null ? 0 : cs.toString().getBytes(charset).length;
- }
-
- /**
- * 切换给定字符串中的大小写。大写转小写,小写转大写。
- *
- *
- * StrUtil.swapCase(null) = null
- * StrUtil.swapCase("") = ""
- * StrUtil.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
- *
- *
- * @param str 字符串
- * @return 交换后的字符串
- * @since 4.3.2
- */
- public static String swapCase(final String str) {
- if (isEmpty(str)) {
- return str;
- }
-
- final char[] buffer = str.toCharArray();
-
- for (int i = 0; i < buffer.length; i++) {
- final char ch = buffer[i];
- if (Character.isUpperCase(ch)) {
- buffer[i] = Character.toLowerCase(ch);
- } else if (Character.isTitleCase(ch)) {
- buffer[i] = Character.toLowerCase(ch);
- } else if (Character.isLowerCase(ch)) {
- buffer[i] = Character.toUpperCase(ch);
- }
- }
- return new String(buffer);
- }
-
- /**
- * 过滤字符串
- *
- * @param str 字符串
- * @param filter 过滤器
- * @return 过滤后的字符串
- * @since 5.4.0
- */
- public static String filter(CharSequence str, final Filternull
生成随机密钥
+ * @param key 密钥,如果为{@code null}生成随机密钥
* @return {@link HMac}
* @since 3.3.0
*/
@@ -638,7 +638,7 @@ public final class SecureUtil {
* 创建HMac对象,调用digest方法可获得hmac值
*
* @param algorithm {@link HmacAlgorithm}
- * @param key 密钥,如果为null
生成随机密钥
+ * @param key 密钥,如果为{@code null}生成随机密钥
* @return {@link HMac}
* @since 3.0.3
*/
@@ -650,7 +650,7 @@ public final class SecureUtil {
* 创建HMac对象,调用digest方法可获得hmac值
*
* @param algorithm {@link HmacAlgorithm}
- * @param key 密钥{@link SecretKey},如果为null
生成随机密钥
+ * @param key 密钥{@link SecretKey},如果为{@code null}生成随机密钥
* @return {@link HMac}
* @since 3.0.3
*/
@@ -664,7 +664,7 @@ public final class SecureUtil {
* HmacMD5加密:hmacMd5(key).digest(data)
* HmacMD5加密并转为16进制字符串:hmacMd5(key).digestHex(data)
*
- * @param key 加密密钥,如果为null
生成随机密钥
+ * @param key 加密密钥,如果为{@code null}生成随机密钥
* @return {@link HMac}
* @since 3.3.0
*/
@@ -678,7 +678,7 @@ public final class SecureUtil {
* HmacMD5加密:hmacMd5(key).digest(data)
* HmacMD5加密并转为16进制字符串:hmacMd5(key).digestHex(data)
*
- * @param key 加密密钥,如果为null
生成随机密钥
+ * @param key 加密密钥,如果为{@code null}生成随机密钥
* @return {@link HMac}
*/
public static HMac hmacMd5(byte[] key) {
@@ -703,7 +703,7 @@ public final class SecureUtil {
* HmacSHA1加密:hmacSha1(key).digest(data)
* HmacSHA1加密并转为16进制字符串:hmacSha1(key).digestHex(data)
*
- * @param key 加密密钥,如果为null
生成随机密钥
+ * @param key 加密密钥,如果为{@code null}生成随机密钥
* @return {@link HMac}
* @since 3.3.0
*/
@@ -717,7 +717,7 @@ public final class SecureUtil {
* HmacSHA1加密:hmacSha1(key).digest(data)
* HmacSHA1加密并转为16进制字符串:hmacSha1(key).digestHex(data)
*
- * @param key 加密密钥,如果为null
生成随机密钥
+ * @param key 加密密钥,如果为{@code null}生成随机密钥
* @return {@link HMac}
*/
public static HMac hmacSha1(byte[] key) {
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/Digester.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/Digester.java
index bddc47151..eb749e080 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/Digester.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/Digester.java
@@ -1,14 +1,5 @@
package cn.hutool.crypto.digest;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.nio.charset.Charset;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Provider;
-
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
@@ -19,6 +10,15 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.SecureUtil;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+
/**
* 摘要算法
* 注意:此对象实例化后为非线程安全!
@@ -84,7 +84,7 @@ public class Digester implements Serializable {
*
* @param algorithm 算法
* @param provider 算法提供者,null表示JDK默认,可以引入Bouncy Castle等来提供更多算法支持
- * @return {@link Digester}
+ * @return Digester
* @throws CryptoException Cause by IOException
*/
public Digester init(String algorithm, Provider provider) {
diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml
index d04060350..cf326dac6 100644
--- a/hutool-db/pom.xml
+++ b/hutool-db/pom.xml
@@ -9,7 +9,7 @@
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
@@ -777,25 +795,59 @@ public abstract class AbstractDb implements Serializable {
}
}
+ /**
+ * 分页查询
+ *
+ * @param
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
*
* @param fields 返回的字段列表,null则返回所有字段
* @param where 条件实体类(包含表名)
- * @param page 分页对象
- * @param numPerPage 每页条目数
+ * @param pageNumber 页码
+ * @param pageSize 每页结果数
* @return 结果对象
* @throws SQLException SQL执行异常
*/
- public PageResult
+ * 批量插入必须严格保持Entity的结构一致,不一致会导致插入数据出现不可预知的结果
+ * 此方法不会关闭Connection
+ *
+ * @param conn 数据库连接
+ * @param records 记录列表,记录KV必须严格一致
+ * @return 插入行数
+ * @throws SQLException SQL执行异常
+ */
+ public int[] insert(Connection conn, Entity... records) throws SQLException {
+ checkConn(conn);
+ if (ArrayUtil.isEmpty(records)) {
+ return new int[]{0};
+ }
+
+ PreparedStatement ps = null;
+ try {
+ if(1 == records.length){
+ //单条单独处理
+ ps = dialect.psForInsert(conn, records[0]);
+ return new int[]{ps.executeUpdate()};
+ }
+
+ // 批量
+ ps = dialect.psForInsertBatch(conn, records);
+ return ps.executeBatch();
+ } finally {
+ DbUtil.close(ps);
+ }
+ }
+
+ /**
+ * 插入数据
+ * 此方法不会关闭Connection
+ *
+ * @param conn 数据库连接
+ * @param record 记录
+ * @param generatedKeysHandler 自增主键处理器,用于定义返回自增主键的范围和类型
+ * @return 主键列表
+ * @throws SQLException SQL执行异常
+ */
+ public
+ * 此方法不会关闭Connection
+ *
+ * @param conn 数据库连接
+ * @param where 条件
+ * @return 影响行数
+ * @throws SQLException SQL执行异常
+ */
+ public int del(Connection conn, Entity where) throws SQLException {
+ checkConn(conn);
+ if (CollUtil.isEmpty(where)) {
+ //不允许做全表删除
+ throw new SQLException("Empty entity provided!");
+ }
+
+ PreparedStatement ps = null;
+ try {
+ ps = dialect.psForDelete(conn, Query.of(where));
+ return ps.executeUpdate();
+ } finally {
+ DbUtil.close(ps);
+ }
+ }
+
+ /**
+ * 更新数据
+ * 此方法不会关闭Connection
+ *
+ * @param conn 数据库连接
+ * @param record 记录
+ * @param where 条件
+ * @return 影响行数
+ * @throws SQLException SQL执行异常
+ */
+ public int update(Connection conn, Entity record, Entity where) throws SQLException {
+ checkConn(conn);
+ if (CollUtil.isEmpty(record)) {
+ throw new SQLException("Empty entity provided!");
+ }
+ if (CollUtil.isEmpty(where)) {
+ //不允许做全表更新
+ throw new SQLException("Empty where provided!");
+ }
+
+ //表名可以从被更新记录的Entity中获得,也可以从Where中获得
+ String tableName = record.getTableName();
+ if (StrUtil.isBlank(tableName)) {
+ tableName = where.getTableName();
+ record.setTableName(tableName);
+ }
+
+ final Query query = new Query(SqlUtil.buildConditions(where), tableName);
+ PreparedStatement ps = null;
+ try {
+ ps = dialect.psForUpdate(conn, record, query);
+ return ps.executeUpdate();
+ } finally {
+ DbUtil.close(ps);
+ }
+ }
+
+ /**
+ * 查询
+ * 此方法不会关闭Connection
+ *
+ * @param
+ * 此方法不会关闭Connection
+ *
+ * @param
+ * 此方法不会关闭Connection
+ *
+ * @param
+ * 如果忽略,则在Entity中调用getXXX时,字段值忽略大小写,默认忽略
+ *
+ * @param caseInsensitive 否在结果中忽略大小写
+ * @since 5.2.4
+ */
+ public void setCaseInsensitive(boolean caseInsensitive) {
+ this.caseInsensitive = caseInsensitive;
+ }
+
+ /**
+ * @return SQL方言
+ */
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ /**
+ * 设置SQL方言
+ *
+ * @param dialect 方言
+ */
+ public void setDialect(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ /**
+ * 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突
+ *
+ * @param wrapperChar 包装字符,字符会在SQL生成时位于表名和字段名两边,null时表示取消包装
+ */
+ public void setWrapper(Character wrapperChar) {
+ setWrapper(new Wrapper(wrapperChar));
+ }
+
+ /**
+ * 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突
+ *
+ * @param wrapper 包装器,null表示取消包装
+ */
+ public void setWrapper(Wrapper wrapper) {
+ this.dialect.setWrapper(wrapper);
+ }
+ //---------------------------------------------------------------------------- Getters and Setters end
+
+ //---------------------------------------------------------------------------- Private method start
+ private void checkConn(Connection conn) {
+ Assert.notNull(conn, "Connection object must be not null!");
+ }
+ //---------------------------------------------------------------------------- Private method start
+}
diff --git a/hutool-db/src/main/java/cn/hutool/db/Page.java b/hutool-db/src/main/java/cn/hutool/db/Page.java
index d53b6bc99..c524584b8 100644
--- a/hutool-db/src/main/java/cn/hutool/db/Page.java
+++ b/hutool-db/src/main/java/cn/hutool/db/Page.java
@@ -1,5 +1,6 @@
package cn.hutool.db;
+import cn.hutool.core.lang.Segment;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.db.sql.Order;
@@ -9,26 +10,44 @@ import java.util.Arrays;
/**
* 分页对象
- *
- * @author Looly
*
+ * @author Looly
*/
-public class Page implements Serializable {
+public class Page implements Segment
- * 此方法不会关闭Connection
- *
- * @param conn 数据库连接
- * @param record 记录
- * @return 插入行数
- * @throws SQLException SQL执行异常
- */
- public int insert(Connection conn, Entity record) throws SQLException {
- checkConn(conn);
- if (CollUtil.isEmpty(record)) {
- throw new SQLException("Empty entity provided!");
- }
- PreparedStatement ps = null;
- try {
- ps = dialect.psForInsert(conn, record);
- return ps.executeUpdate();
- } finally {
- DbUtil.close(ps);
- }
- }
-
/**
* 插入或更新数据
* 此方法不会关闭Connection
@@ -152,33 +118,16 @@ public class SqlConnRunner implements Serializable {
}
/**
- * 批量插入数据
- * 批量插入必须严格保持Entity的结构一致,不一致会导致插入数据出现不可预知的结果
+ * 插入数据
* 此方法不会关闭Connection
*
- * @param conn 数据库连接
- * @param records 记录列表,记录KV必须严格一致
+ * @param conn 数据库连接
+ * @param record 记录
* @return 插入行数
* @throws SQLException SQL执行异常
*/
- public int[] insert(Connection conn, Entity... records) throws SQLException {
- checkConn(conn);
- if (ArrayUtil.isEmpty(records)) {
- return new int[]{0};
- }
-
- //单条单独处理
- if (1 == records.length) {
- return new int[]{insert(conn, records[0])};
- }
-
- PreparedStatement ps = null;
- try {
- ps = dialect.psForInsertBatch(conn, records);
- return ps.executeBatch();
- } finally {
- DbUtil.close(ps);
- }
+ public int insert(Connection conn, Entity record) throws SQLException {
+ return insert(conn, new Entity[]{record})[0];
}
/**
@@ -191,19 +140,7 @@ public class SqlConnRunner implements Serializable {
* @throws SQLException SQL执行异常
*/
public List