一、Java中StringUtil工具
在Java开发中,字符串处理是一项非常常见且必不可少的任务。为了方便进行字符串的操作和处理,通常会使用一些工具类来提供各种功能。本文将提供一个基于String的StringUtil
的工具类,它继承自Spring的StringUtils
类,并提供了一系列实用的方法来处理字符串。
二、Java中字符串处理要求的一般方法
在Java中,字符串处理通常包括以下一些常见的操作:
- 字符串的拼接、分割和替换。
- 字符串的格式化,例如按照特定的模板进行格式化。
- 字符串的空白处理,判断字符串是否为空或只包含空白字符。
- 字符串的查找和匹配,包括判断字符串是否包含特定的子串或是否符合特定的模式。
- 字符串的大小写转换。
- 字符串的截取和提取。
三、主要内容
StringUtil
工具类提供了许多方法来满足上述字符串处理的需求。它包含了一些判断字符串是否为空、是否为数字、是否包含特定字符等的方法。此外,还提供了字符串的格式化、分割、拼接、随机生成、转义HTML、清理字符等功能。
四、包含的全部方法(53项)
isBlank
方法:用于检查给定的CharSequence
是否只包含空白字符。isNotBlank
方法:检查CharSequence
是否不为空、不为null且不包含只空白字符。isAnyBlank
方法:判断多个CharSequence
中是否有任意一个为空白。isNoneBlank
方法:判断多个CharSequence
是否全不为空白。isAllBlank
方法:判断多个CharSequence
是否全为空白。isNumeric
方法:判断字符串是否为数字。format
方法:有两个重载形式,一个用于将字符串中特定模式的字符转换成map
中对应的值,另一个用于按照log
格式的规则进行格式化。join
方法:用于将Collection
或String
数组转换为以逗号或指定分隔符分隔的字符串。simpleMatch
方法:判断字符串是否符合指定的表达式模式。randomUUID
方法:生成UUID。escapeHtml
方法:转义HTML用于安全过滤。cleanChars
方法:清理字符串,去除某些不可见字符。cleanText
方法:清理字符串,去除某些不可见字符和一些SQL特殊字符。cleanIdentifier
方法:获取标识符,用于参数清理。random
方法:生成指定长度和类型的随机数。indexedFormat
方法:有序地格式化文本,使用{number}
做为占位符。format
方法:使用{varName}
占位符格式化文本。split
方法:切分字符串,可选择是否去除切分后每个元素两边的空白符、是否去除空白项以及限制分片数。splitTrim
方法:与split
方法类似,但会去除切分后每个元素两边的空白符和空白项。contains
方法:判断指定字符是否在字符串中出现过。containsAny
方法:查找指定字符串是否包含指定字符串列表中的任意一个字符串。getContainsStr
方法:查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串。containsIgnoreCase
方法:判断是否包含特定字符,忽略大小写。containsAnyIgnoreCase
方法:查找指定字符串是否包含指定字符串列表中的任意一个字符串,忽略大小写。getContainsStrIgnoreCase
方法:查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串,忽略大小写。sub
方法:改进JDK的subString
方法,对索引进行了修正。subBefore
方法:截取分隔字符串之前的字符串,不包括分隔字符串。subAfter
方法:截取分隔字符串之后的字符串,不包括分隔字符串。subBetween
方法:截取指定字符串中间部分,不包括标识字符串。removePrefix
方法:去掉指定前缀。removePrefixIgnoreCase
方法:忽略大小写去掉指定前缀。removeSuffix
方法:去掉指定后缀。removeSufAndLowerFirst
方法:去掉指定后缀,并小写首字母。removeSuffixIgnoreCase
方法:忽略大小写去掉指定后缀。firstCharToLower
方法:首字母变小写。firstCharToUpper
方法:首字母变大写。subPre
方法:切割指定位置之前部分的字符串。subSuf
方法:切割指定位置之后部分的字符串。indexOf
方法:指定范围内查找指定字符。indexOfIgnoreCase
方法:指定范围内查找字符串,忽略大小写。lastIndexOfIgnoreCase
方法:指定范围内反向查找字符串,忽略大小写。ordinalIndexOf
方法:返回字符串在另一个字符串中第ordinal
次出现的位置。isSubEquals
方法:判断两个字符串的子串是否相同,可选择是否忽略大小写。equals
方法:比较两个字符串是否相等,可选择是否忽略大小写。builder
方法:创建StringBuilder
对象,可选择指定初始大小或初始字符串列表。appendBuilder
方法:向StringBuilder
对象中追加字符串。getReader
方法:获得StringReader
。getWriter
方法:获得StringWriter
。count
方法:统计指定内容中包含指定字符串或字符的数量。underlineToHump
方法:下划线转驼峰。humpToUnderline
方法:驼峰转下划线。lineToHump
方法:横线转驼峰。humpToLine
方法:驼峰转横线。
五、总结
本文StringUtil
工具类提供了丰富的方法来处理字符串,涵盖了字符串的各种常见操作。通过使用这些方法,可以方便地进行字符串的判断、格式化、分割、拼接、查找等操作,提高了开发效率。在实际开发中,可以根据具体的需求选择合适的方法来处理字符串,使代码更加简洁和可读。同时,需要注意方法的参数和返回值,以确保正确使用这些方法。希望这个工具类能够对Java开发者在字符串处理方面提供帮助。
六、源码
附上工具类源码:
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.util.HtmlUtils;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* 继承自Spring util的工具类,封装String常用操作
*
* @author Rzc
*/
public class StringUtil extends org.springframework.util.StringUtils {
public static final int INDEX_NOT_FOUND = -1;
/**
* Check whether the given {@code CharSequence} contains actual <em>text</em>.
* <p>More specifically, this method returns {@code true} if the
* {@code CharSequence} is not {@code null}, its length is greater than
* 0, and it contains at least one non-whitespace character.
* <pre class="code">
* StringUtil.isBlank(null) = true
* StringUtil.isBlank("") = true
* StringUtil.isBlank(" ") = true
* StringUtil.isBlank("12345") = false
* StringUtil.isBlank(" 12345 ") = false
* </pre>
*
* @param cs the {@code CharSequence} to check (may be {@code null})
* @return {@code true} if the {@code CharSequence} is not {@code null},
* its length is greater than 0, and it does not contain whitespace only
* @see Character#isWhitespace
*/
public static boolean isBlank(final CharSequence cs) {
return !StringUtil.hasText(cs);
}
/**
* <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p>
* <pre>
* StringUtil.isNotBlank(null) = false
* StringUtil.isNotBlank("") = false
* StringUtil.isNotBlank(" ") = false
* StringUtil.isNotBlank("bob") = true
* StringUtil.isNotBlank(" bob ") = true
* </pre>
*
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is
* not empty and not null and not whitespace
* @see Character#isWhitespace
*/
public static boolean isNotBlank(final CharSequence cs) {
return StringUtil.hasText(cs);
}
/**
* 有 任意 一个 Blank
*
* @param css CharSequence
* @return boolean
*/
public static boolean isAnyBlank(final CharSequence... css) {
if (ObjectUtil.isEmpty(css)) {
return true;
}
return Stream.of(css).anyMatch(StringUtil::isBlank);
}
/**
* 是否全非 Blank
*
* @param css CharSequence
* @return boolean
*/
public static boolean isNoneBlank(final CharSequence... css) {
if (ObjectUtil.isEmpty(css)) {
return false;
}
return Stream.of(css).allMatch(StringUtil::isNotBlank);
}
/**
* 是否全为 Blank
*
* @param css CharSequence
* @return boolean
*/
public static boolean isAllBlank(final CharSequence... css) {
return Stream.of(css).allMatch(StringUtil::isBlank);
}
/**
* 判断一个字符串是否是数字
*
* @param cs the CharSequence to check, may be null
* @return {boolean}
*/
public static boolean isNumeric(final CharSequence cs) {
if (isBlank(cs)) {
return false;
}
for (int i = cs.length(); --i >= 0; ) {
int chr = cs.charAt(i);
if (chr < 48 || chr > 57) {
return false;
}
}
return true;
}
/**
* 将字符串中特定模式的字符转换成map中对应的值
* <p>
* use: format("my name is ${name}, and i like ${like}!", {"name":"s.l.m", "like": "Java"})
*
* @param message 需要转换的字符串
* @param params 转换所需的键值对集合
* @return 转换后的字符串
*/
public static String format(@Nullable String message, @Nullable Map<String, ?> params) {
// message 为 null 返回空字符串
if (message == null) {
return StringPool.EMPTY;
}
// 参数为 null 或者为空
if (params == null || params.isEmpty()) {
return message;
}
// 替换变量
StringBuilder sb = new StringBuilder((int) (message.length() * 1.5));
int cursor = 0;
for (int start, end; (start = message.indexOf(StringPool.DOLLAR_LEFT_BRACE, cursor)) != -1 && (end = message.indexOf(StringPool.RIGHT_BRACE, start)) != -1; ) {
sb.append(message, cursor, start);
String key = message.substring(start + 2, end);
Object value = params.get(StringUtil.trimWhitespace(key));
sb.append(value == null ? StringPool.EMPTY : value);
cursor = end + 1;
}
sb.append(message.substring(cursor));
return sb.toString();
}
/**
* 同 log 格式的 format 规则
* <p>
* use: format("my name is {}, and i like {}!", "s.l.m", "Java")
*
* @param message 需要转换的字符串
* @param arguments 需要替换的变量
* @return 转换后的字符串
*/
public static String format(@Nullable String message, @Nullable Object... arguments) {
// message 为 null 返回空字符串
if (message == null) {
return StringPool.EMPTY;
}
// 参数为 null 或者为空
if (arguments == null || arguments.length == 0) {
return message;
}
StringBuilder sb = new StringBuilder((int) (message.length() * 1.5));
int cursor = 0;
int index = 0;
int argsLength = arguments.length;
for (int start, end; (start = message.indexOf('{', cursor)) != -1 && (end = message.indexOf('}', start)) != -1 && index < argsLength; ) {
sb.append(message, cursor, start);
sb.append(arguments[index]);
cursor = end + 1;
index++;
}
sb.append(message.substring(cursor));
return sb.toString();
}
/**
* 格式化执行时间,单位为 ms 和 s,保留三位小数
*
* @param nanos 纳秒
* @return 格式化后的时间
*/
public static String format(long nanos) {
if (nanos < 1) {
return "0ms";
}
double millis = (double) nanos / (1000 * 1000);
// 不够 1 ms,最小单位为 ms
if (millis > 1000) {
return String.format("%.3fs", millis / 1000);
} else {
return String.format("%.3fms", millis);
}
}
/**
* Convert a {@code Collection} into a delimited {@code String} (e.g., CSV).
* <p>Useful for {@code toString()} implementations.
*
* @param coll the {@code Collection} to convert
* @return the delimited {@code String}
*/
public static String join(Collection<?> coll) {
return StringUtil.collectionToCommaDelimitedString(coll);
}
/**
* Convert a {@code Collection} into a delimited {@code String} (e.g. CSV).
* <p>Useful for {@code toString()} implementations.
*
* @param coll the {@code Collection} to convert
* @param delim the delimiter to use (typically a ",")
* @return the delimited {@code String}
*/
public static String join(Collection<?> coll, String delim) {
return StringUtil.collectionToDelimitedString(coll, delim);
}
/**
* Convert a {@code String} array into a comma delimited {@code String}
* (i.e., CSV).
* <p>Useful for {@code toString()} implementations.
*
* @param arr the array to display
* @return the delimited {@code String}
*/
public static String join(Object[] arr) {
return StringUtil.arrayToCommaDelimitedString(arr);
}
/**
* Convert a {@code String} array into a delimited {@code String} (e.g. CSV).
* <p>Useful for {@code toString()} implementations.
*
* @param arr the array to display
* @param delim the delimiter to use (typically a ",")
* @return the delimited {@code String}
*/
public static String join(Object[] arr, String delim) {
return StringUtil.arrayToDelimitedString(arr, delim);
}
/**
* 字符串是否符合指定的 表达式
*
* <p>
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy"
* </p>
*
* @param pattern 表达式
* @param str 字符串
* @return 是否匹配
*/
public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) {
return PatternMatchUtils.simpleMatch(pattern, str);
}
/**
* 字符串是否符合指定的 表达式
*
* <p>
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy"
* </p>
*
* @param patterns 表达式 数组
* @param str 字符串
* @return 是否匹配
*/
public static boolean simpleMatch(@Nullable String[] patterns, String str) {
return PatternMatchUtils.simpleMatch(patterns, str);
}
/**
* 生成uuid
*
* @return UUID
*/
public static String randomUUID() {
ThreadLocalRandom random = ThreadLocalRandom.current();
return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
}
/**
* 转义HTML用于安全过滤
*
* @param html html
* @return {String}
*/
public static String escapeHtml(String html) {
return StringUtil.isBlank(html) ? StringPool.EMPTY : HtmlUtils.htmlEscape(html);
}
/**
* 清理字符串,清理出某些不可见字符
*
* @param txt 字符串
* @return {String}
*/
public static String cleanChars(String txt) {
return txt.replaceAll("[ `·•�\\f\\t\\v\\s]", "");
}
/**
* 特殊字符正则,sql特殊字符和空白符
*/
private final static Pattern SPECIAL_CHARS_REGEX = Pattern.compile("[`'\"|/,;()-+*%#·•� \\s]");
/**
* 清理字符串,清理出某些不可见字符和一些sql特殊字符
*
* @param txt 文本
* @return {String}
*/
@Nullable
public static String cleanText(@Nullable String txt) {
if (txt == null) {
return null;
}
return SPECIAL_CHARS_REGEX.matcher(txt).replaceAll(StringPool.EMPTY);
}
/**
* 获取标识符,用于参数清理
*
* @param param 参数
* @return 清理后的标识符
*/
@Nullable
public static String cleanIdentifier(@Nullable String param) {
if (param == null) {
return null;
}
StringBuilder paramBuilder = new StringBuilder();
for (int i = 0; i < param.length(); i++) {
char c = param.charAt(i);
if (Character.isJavaIdentifierPart(c)) {
paramBuilder.append(c);
}
}
return paramBuilder.toString();
}
/**
* 随机数生成
*
* @param count 字符长度
* @return 随机数
*/
public static String random(int count) {
return StringUtil.random(count, RandomType.ALL);
}
/**
* 随机数生成
*
* @param count 字符长度
* @param randomType 随机数类别
* @return 随机数
*/
public static String random(int count, RandomType randomType) {
if (count == 0) {
return StringPool.EMPTY;
}
Assert.isTrue(count > 0, "Requested random string length " + count + " is less than 0.");
final Random random = Holder.SECURE_RANDOM;
char[] buffer = new char[count];
for (int i = 0; i < count; i++) {
String factor = randomType.getFactor();
buffer[i] = factor.charAt(random.nextInt(factor.length()));
}
return new String(buffer);
}
/**
* 有序的格式化文本,使用{number}做为占位符<br>
* 例:<br>
* 通常使用:format("this is {0} for {1}", "a", "b") =》 this is a for b<br>
*
* @param pattern 文本格式
* @param arguments 参数
* @return 格式化后的文本
*/
public static String indexedFormat(CharSequence pattern, Object... arguments) {
return MessageFormat.format(pattern.toString(), arguments);
}
/**
* 格式化文本,使用 {varName} 占位<br>
* 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) {
if (null == template) {
return null;
}
if (null == map || map.isEmpty()) {
return template.toString();
}
String template2 = template.toString();
for (Map.Entry<?, ?> entry : map.entrySet()) {
template2 = template2.replace("{" + entry.getKey() + "}", Func.toStr(entry.getValue()));
}
return template2;
}
/**
* 切分字符串,不去除切分后每个元素两边的空白符,不去除空白项
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数,-1不限制
* @return 切分后的集合
*/
public static List<String> split(CharSequence str, char separator, int limit) {
return split(str, separator, limit, false, false);
}
/**
* 分割 字符串 删除常见 空白符
*
* @param str 字符串
* @param delimiter 分割符
* @return 字符串数组
*/
public static String[] splitTrim(@Nullable String str, @Nullable String delimiter) {
return StringUtil.delimitedListToStringArray(str, delimiter, " \t\n\n\f");
}
/**
* 切分字符串,去除切分后每个元素两边的空白符,去除空白项
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @return 切分后的集合
* @since 3.1.2
*/
public static List<String> splitTrim(CharSequence str, char separator) {
return splitTrim(str, separator, -1);
}
/**
* 切分字符串,去除切分后每个元素两边的空白符,去除空白项
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @return 切分后的集合
* @since 3.2.0
*/
public static List<String> splitTrim(CharSequence str, CharSequence separator) {
return splitTrim(str, separator, -1);
}
/**
* 切分字符串,去除切分后每个元素两边的空白符,去除空白项
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数,-1不限制
* @return 切分后的集合
* @since 3.1.0
*/
public static List<String> splitTrim(CharSequence str, char separator, int limit) {
return split(str, separator, limit, true, true);
}
/**
* 切分字符串,去除切分后每个元素两边的空白符,去除空白项
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数,-1不限制
* @return 切分后的集合
* @since 3.2.0
*/
public static List<String> splitTrim(CharSequence str, CharSequence separator, int limit) {
return split(str, separator, limit, true, true);
}
/**
* 切分字符串,不限制分片数量
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(CharSequence str, char separator, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, 0, isTrim, ignoreEmpty);
}
/**
* 指定字符是否在字符串中出现过
*
* @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 testStrs 需要检查的字符串数组
* @return 是否包含任意一个字符串
* @since 3.2.0
*/
public static boolean containsAny(CharSequence str, CharSequence... testStrs) {
return null != getContainsStr(str, testStrs);
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串
*
* @param str 指定字符串
* @param testStrs 需要检查的字符串数组
* @return 被包含的第一个字符串
* @since 3.2.0
*/
public static String getContainsStr(CharSequence str, CharSequence... testStrs) {
if (isEmpty(str) || Func.isEmpty(testStrs)) {
return null;
}
for (CharSequence checkStr : testStrs) {
if (str.toString().contains(checkStr)) {
return checkStr.toString();
}
}
return null;
}
/**
* 是否包含特定字符,忽略大小写,如果给定两个参数都为<code>null</code>,返回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());
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串<br>
* 忽略大小写
*
* @param str 指定字符串
* @param testStrs 需要检查的字符串数组
* @return 是否包含任意一个字符串
* @since 3.2.0
*/
public static boolean containsAnyIgnoreCase(CharSequence str, CharSequence... testStrs) {
return null != getContainsStrIgnoreCase(str, testStrs);
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串,如果包含返回找到的第一个字符串<br>
* 忽略大小写
*
* @param str 指定字符串
* @param testStrs 需要检查的字符串数组
* @return 被包含的第一个字符串
* @since 3.2.0
*/
public static String getContainsStrIgnoreCase(CharSequence str, CharSequence... testStrs) {
if (isEmpty(str) || Func.isEmpty(testStrs)) {
return null;
}
for (CharSequence testStr : testStrs) {
if (containsIgnoreCase(str, testStr)) {
return testStr.toString();
}
}
return null;
}
/**
* 改进JDK subString<br>
* index从0开始计算,最后一个字符为-1<br>
* 如果from和to位置一样,返回 "" <br>
* 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length<br>
* 如果经过修正的index中from大于to,则互换from和to example: <br>
* abcdefgh 2 3 =》 c <br>
* abcdefgh 2 -3 =》 cde <br>
*
* @param str String
* @param fromIndex 开始的index(包括)
* @param toIndex 结束的index(不包括)
* @return 字串
*/
public static String sub(CharSequence str, int fromIndex, int toIndex) {
if (isEmpty(str)) {
return StringPool.EMPTY;
}
int len = str.length();
if (fromIndex < 0) {
fromIndex = len + fromIndex;
if (fromIndex < 0) {
fromIndex = 0;
}
} else if (fromIndex > len) {
fromIndex = len;
}
if (toIndex < 0) {
toIndex = len + toIndex;
if (toIndex < 0) {
toIndex = len;
}
} else if (toIndex > len) {
toIndex = len;
}
if (toIndex < fromIndex) {
int tmp = fromIndex;
fromIndex = toIndex;
toIndex = tmp;
}
if (fromIndex == toIndex) {
return StringPool.EMPTY;
}
return str.toString().substring(fromIndex, toIndex);
}
/**
* 截取分隔字符串之前的字符串,不包括分隔字符串<br>
* 如果给定的字符串为空串(null或"")或者分隔字符串为null,返回原字符串<br>
* 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串
* <p>
* 栗子:
*
* <pre>
* StringUtil.subBefore(null, *) = null
* StringUtil.subBefore("", *) = ""
* StringUtil.subBefore("abc", "a") = ""
* StringUtil.subBefore("abcba", "b") = "a"
* StringUtil.subBefore("abc", "c") = "ab"
* StringUtil.subBefore("abc", "d") = "abc"
* StringUtil.subBefore("abc", "") = ""
* StringUtil.subBefore("abc", null) = "abc"
* </pre>
*
* @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 StringPool.EMPTY;
}
final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
if (pos == INDEX_NOT_FOUND) {
return str;
}
return str.substring(0, pos);
}
/**
* 截取分隔字符串之后的字符串,不包括分隔字符串<br>
* 如果给定的字符串为空串(null或""),返回原字符串<br>
* 如果分隔字符串为空串(null或""),则返回空串,如果分隔字符串未找到,返回空串
* <p>
* 栗子:
*
* <pre>
* StringUtil.subAfter(null, *) = null
* StringUtil.subAfter("", *) = ""
* StringUtil.subAfter(*, null) = ""
* StringUtil.subAfter("abc", "a") = "bc"
* StringUtil.subAfter("abcba", "b") = "cba"
* StringUtil.subAfter("abc", "c") = ""
* StringUtil.subAfter("abc", "d") = ""
* StringUtil.subAfter("abc", "") = "abc"
* </pre>
*
* @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 : string.toString();
}
if (separator == null) {
return StringPool.EMPTY;
}
final String str = string.toString();
final String sep = separator.toString();
final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
if (pos == INDEX_NOT_FOUND) {
return StringPool.EMPTY;
}
return str.substring(pos + separator.length());
}
/**
* 截取指定字符串中间部分,不包括标识字符串<br>
* <p>
* 栗子:
*
* <pre>
* StringUtil.subBetween("wx[b]yz", "[", "]") = "b"
* StringUtil.subBetween(null, *, *) = null
* StringUtil.subBetween(*, null, *) = null
* StringUtil.subBetween(*, *, null) = null
* StringUtil.subBetween("", "", "") = ""
* StringUtil.subBetween("", "", "]") = null
* StringUtil.subBetween("", "[", "]") = null
* StringUtil.subBetween("yabcz", "", "") = ""
* StringUtil.subBetween("yabcz", "y", "z") = "abc"
* StringUtil.subBetween("yabczyabcz", "y", "z") = "abc"
* </pre>
*
* @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;
}
/**
* 截取指定字符串中间部分,不包括标识字符串<br>
* <p>
* 栗子:
*
* <pre>
* StringUtil.subBetween(null, *) = null
* StringUtil.subBetween("", "") = ""
* StringUtil.subBetween("", "tag") = null
* StringUtil.subBetween("tagabctag", null) = null
* StringUtil.subBetween("tagabctag", "") = ""
* StringUtil.subBetween("tagabctag", "tag") = "abc"
* </pre>
*
* @param str 被切割的字符串
* @param beforeAndAfter 截取开始和结束的字符串标识
* @return 截取后的字符串
* @since 3.1.1
*/
public static String subBetween(CharSequence str, CharSequence beforeAndAfter) {
return subBetween(str, beforeAndAfter, beforeAndAfter);
}
/**
* 去掉指定前缀
*
* @param str 字符串
* @param prefix 前缀
* @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串
*/
public static String removePrefix(CharSequence str, CharSequence prefix) {
if (isEmpty(str) || isEmpty(prefix)) {
return StringPool.EMPTY;
}
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 StringPool.EMPTY;
}
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 StringPool.EMPTY;
}
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 firstCharToLower(removeSuffix(str, suffix));
}
/**
* 忽略大小写去掉指定后缀
*
* @param str 字符串
* @param suffix 后缀
* @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
*/
public static String removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) {
if (isEmpty(str) || isEmpty(suffix)) {
return StringPool.EMPTY;
}
final String str2 = str.toString();
if (str2.toLowerCase().endsWith(suffix.toString().toLowerCase())) {
return subPre(str2, str2.length() - suffix.length());
}
return str2;
}
/**
* 首字母变小写
*
* @param str 字符串
* @return {String}
*/
public static String firstCharToLower(String str) {
char firstChar = str.charAt(0);
if (firstChar >= CharPool.UPPER_A && firstChar <= CharPool.UPPER_Z) {
char[] arr = str.toCharArray();
arr[0] += (CharPool.LOWER_A - CharPool.UPPER_A);
return new String(arr);
}
return str;
}
/**
* 首字母变大写
*
* @param str 字符串
* @return {String}
*/
public static String firstCharToUpper(String str) {
char firstChar = str.charAt(0);
if (firstChar >= CharPool.LOWER_A && firstChar <= CharPool.LOWER_Z) {
char[] arr = str.toCharArray();
arr[0] -= (CharPool.LOWER_A - CharPool.UPPER_A);
return new String(arr);
}
return str;
}
/**
* 切割指定位置之前部分的字符串
*
* @param string 字符串
* @param toIndex 切割到的位置(不包括)
* @return 切割后的剩余的前半部分字符串
*/
public static String subPre(CharSequence string, int toIndex) {
return sub(string, 0, toIndex);
}
/**
* 切割指定位置之后部分的字符串
*
* @param string 字符串
* @param fromIndex 切割开始的位置(包括)
* @return 切割后后剩余的后半部分字符串
*/
public static String subSuf(CharSequence string, int fromIndex) {
if (isEmpty(string)) {
return null;
}
return sub(string, fromIndex, string.length());
}
/**
* 指定范围内查找指定字符
*
* @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(final 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) {
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 -1;
}
/**
* 指定范围内查找字符串,忽略大小写<br>
*
* <pre>
* StringUtil.indexOfIgnoreCase(null, *, *) = -1
* StringUtil.indexOfIgnoreCase(*, null, *) = -1
* StringUtil.indexOfIgnoreCase("", "", 0) = 0
* StringUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
* StringUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
* StringUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
* StringUtil.indexOfIgnoreCase("abc", "", 9) = -1
* </pre>
*
* @param str 字符串
* @param searchStr 需要查找位置的字符串
* @return 位置
* @since 3.2.1
*/
public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
return indexOfIgnoreCase(str, searchStr, 0);
}
/**
* 指定范围内查找字符串
*
* <pre>
* StringUtil.indexOfIgnoreCase(null, *, *) = -1
* StringUtil.indexOfIgnoreCase(*, null, *) = -1
* StringUtil.indexOfIgnoreCase("", "", 0) = 0
* StringUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
* StringUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
* StringUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
* StringUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
* StringUtil.indexOfIgnoreCase("abc", "", 9) = -1
* </pre>
*
* @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;
}
/**
* 指定范围内查找字符串,忽略大小写<br>
*
* @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());
}
/**
* 指定范围内查找字符串,忽略大小写<br>
*
* @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);
}
/**
* 指定范围内查找字符串<br>
*
* @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 次出现的位置。<br>
* 此方法来自:Apache-Commons-Lang
* <p>
* 栗子(*代表任意字符):
*
* <pre>
* StringUtil.ordinalIndexOf(null, *, *) = -1
* StringUtil.ordinalIndexOf(*, null, *) = -1
* StringUtil.ordinalIndexOf("", "", *) = 0
* StringUtil.ordinalIndexOf("aabaabaa", "a", 1) = 0
* StringUtil.ordinalIndexOf("aabaabaa", "a", 2) = 1
* StringUtil.ordinalIndexOf("aabaabaa", "b", 1) = 2
* StringUtil.ordinalIndexOf("aabaabaa", "b", 2) = 5
* StringUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1
* StringUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4
* StringUtil.ordinalIndexOf("aabaabaa", "", 1) = 0
* StringUtil.ordinalIndexOf("aabaabaa", "", 2) = 0
* </pre>
*
* @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;
}
/**
* 截取两个字符串的不同部分(长度一致),判断截取的子串是否相同<br>
* 任意一个字符串为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);
}
/**
* 比较两个字符串(大小写敏感)。
*
* <pre>
* equalsIgnoreCase(null, null) = true
* equalsIgnoreCase(null, "abc") = false
* equalsIgnoreCase("abc", null) = false
* equalsIgnoreCase("abc", "abc") = true
* equalsIgnoreCase("abc", "ABC") = true
* </pre>
*
* @param str1 要比较的字符串1
* @param str2 要比较的字符串2
* @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
*/
public static boolean equals(CharSequence str1, CharSequence str2) {
return equals(str1, str2, false);
}
/**
* 比较两个字符串(大小写不敏感)。
*
* <pre>
* equalsIgnoreCase(null, null) = true
* equalsIgnoreCase(null, "abc") = false
* equalsIgnoreCase("abc", null) = false
* equalsIgnoreCase("abc", "abc") = true
* equalsIgnoreCase("abc", "ABC") = true
* </pre>
*
* @param str1 要比较的字符串1
* @param str2 要比较的字符串2
* @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
*/
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>,则返回<code>true</code>
* @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.equals(str2);
}
}
/**
* 创建StringBuilder对象
*
* @return {String}Builder对象
*/
public static StringBuilder builder() {
return new StringBuilder();
}
/**
* 创建StringBuilder对象
*
* @param capacity 初始大小
* @return {String}Builder对象
*/
public static StringBuilder builder(int capacity) {
return new StringBuilder(capacity);
}
/**
* 创建StringBuilder对象
*
* @param strs 初始字符串列表
* @return {String}Builder对象
*/
public static StringBuilder builder(CharSequence... strs) {
final StringBuilder sb = new StringBuilder();
for (CharSequence str : strs) {
sb.append(str);
}
return sb;
}
/**
* 创建StringBuilder对象
*
* @param sb 初始StringBuilder
* @param strs 初始字符串列表
* @return {String}Builder对象
*/
public static StringBuilder appendBuilder(StringBuilder sb, CharSequence... strs) {
for (CharSequence str : strs) {
sb.append(str);
}
return sb;
}
/**
* 获得StringReader
*
* @param str 字符串
* @return {String}Reader
*/
public static StringReader getReader(CharSequence str) {
if (null == str) {
return null;
}
return new StringReader(str.toString());
}
/**
* 获得StringWriter
*
* @return {String}Writer
*/
public static StringWriter getWriter() {
return new StringWriter();
}
/**
* 统计指定内容中包含指定字符串的数量<br>
* 参数为 {@code null} 或者 "" 返回 {@code 0}.
*
* <pre>
* StringUtil.count(null, *) = 0
* StringUtil.count("", *) = 0
* StringUtil.count("abba", null) = 0
* StringUtil.count("abba", "") = 0
* StringUtil.count("abba", "a") = 2
* StringUtil.count("abba", "ab") = 1
* StringUtil.count("abba", "xxx") = 0
* </pre>
*
* @param content 被查找的字符串
* @param strForSearch 需要查找的字符串
* @return 查找到的个数
*/
public static int count(CharSequence content, CharSequence strForSearch) {
if (Func.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;
}
/**
* 下划线转驼峰
*
* @param para 字符串
* @return {String}
*/
public static String underlineToHump(String para) {
StringBuilder result = new StringBuilder();
String[] a = para.split("_");
for (String s : a) {
if (result.length() == 0) {
result.append(s.toLowerCase());
} else {
result.append(s.substring(0, 1).toUpperCase());
result.append(s.substring(1).toLowerCase());
}
}
return result.toString();
}
/**
* 驼峰转下划线
*
* @param para 字符串
* @return {String}
*/
public static String humpToUnderline(String para) {
para = firstCharToLower(para);
StringBuilder sb = new StringBuilder(para);
int temp = 0;
for (int i = 0; i < para.length(); i++) {
if (Character.isUpperCase(para.charAt(i))) {
sb.insert(i + temp, "_");
temp += 1;
}
}
return sb.toString().toLowerCase();
}
/**
* 横线转驼峰
*
* @param para 字符串
* @return {String}
*/
public static String lineToHump(String para) {
StringBuilder result = new StringBuilder();
String[] a = para.split("-");
for (String s : a) {
if (result.length() == 0) {
result.append(s.toLowerCase());
} else {
result.append(s.substring(0, 1).toUpperCase());
result.append(s.substring(1).toLowerCase());
}
}
return result.toString();
}
/**
* 驼峰转横线
*
* @param para 字符串
* @return {String}
*/
public static String humpToLine(String para) {
para = firstCharToLower(para);
StringBuilder sb = new StringBuilder(para);
int temp = 0;
for (int i = 0; i < para.length(); i++) {
if (Character.isUpperCase(para.charAt(i))) {
sb.insert(i + temp, "-");
temp += 1;
}
}
return sb.toString().toLowerCase();
}
}