package com.github.looly.hutool;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* 字符串工具类
* @author xiaoleilu
*
*/
public class StrUtil {
public static final String DOT = ".";
public static final String SLASH = "/";
public static final String BACKSLASH = "\\";
public static final String EMPTY = "";
public static final String CRLF = "\r\n";
public static final String NEWLINE = "\n";
public static final String HTML_NBSP = " ";
public static final String HTML_AMP = "&";
public static final String HTML_QUOTE = """;
public static final String HTML_LT = "<";
public static final String HTML_GT = ">";
public static final String EMPTY_JSON = "{}";
/**
* 字符串是否为空白 空白的定义如下: <br>
* 1、为null <br>
* 2、为不可见字符(如空格)<br>
* 3、""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isBlank(String str) {
return str == null || str.trim().length() == 0;
}
/**
* 字符串是否为非空白 空白的定义如下: <br>
* 1、不为null <br>
* 2、不为不可见字符(如空格)<br>
* 3、不为""<br>
*
* @param str 被检测的字符串
* @return 是否为非空
*/
public static boolean isNotBlank(String str) {
return false == isBlank(str);
}
/**
* 是否包含空字符串
* @param strs 字符串列表
* @return 是否包含空字符串
*/
public static boolean hasBlank(String... strs) {
for (String str : strs) {
if(isBlank(str)) {
return true;
}
}
return false;
}
/**
* 字符串是否为空,空的定义如下
* 1、为null <br>
* 2、为""<br>
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
/**
* 字符串是否为非空白 空白的定义如下: <br>
* 1、不为null <br>
* 2、不为""<br>
*
* @param str 被检测的字符串
* @return 是否为非空
*/
public static boolean isNotEmpty(String str) {
return false == isEmpty(str);
}
/**
* 是否包含空字符串
* @param strs 字符串列表
* @return 是否包含空字符串
*/
public static boolean hasEmpty(String... strs) {
for (String str : strs) {
if(isEmpty(str)) {
return true;
}
}
return false;
}
/**
* 去除字符串两边的空格符,如果为null返回null
* @param str 字符串
* @return 处理后的字符串
*/
public static String trim(String str) {
return (null == str) ? null : str.trim();
}
/**
* 获得set或get方法对应的标准属性名<br/>
* 例如:setName 返回 name
* @param getOrSetMethodName
* @return 如果是set或get方法名,返回field, 否则null
*/
public static String getGeneralField(String getOrSetMethodName){
if(getOrSetMethodName.startsWith("get") || getOrSetMethodName.startsWith("set")) {
return cutPreAndLowerFirst(getOrSetMethodName, 3);
}
return null;
}
/**
* 生成set方法名<br/>
* 例如:name 返回 setName
* @param fieldName 属性名
* @return setXxx
*/
public static String genSetter(String fieldName){
return upperFirstAndAddPre(fieldName, "set");
}
/**
* 生成get方法名
* @param fieldName 属性名
* @return getXxx
*/
public static String genGetter(String fieldName){
return upperFirstAndAddPre(fieldName, "get");
}
/**
* 去掉首部指定长度的字符串并将剩余字符串首字母小写<br/>
* 例如:str=setName, preLength=3 -> return name
* @param str 被处理的字符串
* @param preLength 去掉的长度
* @return 处理后的字符串,不符合规范返回null
*/
public static String cutPreAndLowerFirst(String 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.substring(preLength +1);
}
return String.valueOf(first);
}
return null;
}
/**
* 原字符串首字母大写并在其首部添加指定字符串
* 例如:str=name, preString=get -> return getName
* @param str 被处理的字符串
* @param preString 添加的首部
* @return 处理后的字符串
*/
public static String upperFirstAndAddPre(String str, String preString) {
if(str == null || preString == null) {
return null;
}
return preString + upperFirst(str);
}
/**
* 大写首字母<br>
* 例如:str = name, return Name
* @param str 字符串
* @return 字符串
*/
public static String upperFirst(String str) {
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
/**
* 小写首字母<br>
* 例如:str = Name, return name
* @param str 字符串
* @return 字符串
*/
public static String lowerFirst(String str) {
return Character.toLowerCase(str.charAt(0)) + str.substring(1);
}
/**
* 去掉指定前缀
* @param str 字符串
* @param prefix 前缀
* @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串
*/
public static String removePrefix(String str, String prefix) {
if(str != null && str.startsWith(prefix)) {
return str.substring(prefix.length());
}
return str;
}
/**
* 忽略大小写去掉指定前缀
* @param str 字符串
* @param prefix 前缀
* @return 切掉后的字符串,若前缀不是 prefix, 返回原字符串
*/
public static String removePrefixIgnoreCase(String str, String prefix) {
if (str != null && str.toLowerCase().startsWith(prefix.toLowerCase())) {
return str.substring(prefix.length());
}
return str;
}
/**
* 去掉指定后缀
* @param str 字符串
* @param suffix 后缀
* @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
*/
public static String removeSuffix(String str, String suffix) {
if (str != null && str.endsWith(suffix)) {
return str.substring(0, str.length() - suffix.length());
}
return str;
}
/**
* 忽略大小写去掉指定后缀
* @param str 字符串
* @param suffix 后缀
* @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
*/
public static String removeSuffixIgnoreCase(String str, String suffix) {
if (str != null && str.toLowerCase().endsWith(suffix.toLowerCase())) {
return str.substring(0, str.length() - suffix.length());
}
return str;
}
/**
* 清理空白字符
* @param str 被清理的字符串
* @return 清理后的字符串
*/
public static String cleanBlank(String str) {
if(str == null) {
return null;
}
return str.replaceAll("\\s*", EMPTY);
}
/**
* 切分字符串<br/>
* a#b#c -> [a,b,c]
* a##b#c -> [a,"",b,c]
* @param str 被切分的字符串
* @param separator 分隔符字符
* @return 切分后的集合
*/
public static List<String> split(String str, char separator) {
return split(str, separator, 0);
}
/**
* 切分字符串
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数
* @return 切分后的集合
*/
public static List<String> split(String str, char separator, int limit){
if(str == null) {
return null;
}
List<String> list = new ArrayList<String>(limit == 0 ? 16 : limit);
if(limit == 1) {
list.add(str);
return list;
}
boolean isNotEnd = true; //未结束切分的标志
int strLen = str.length();
StringBuilder sb = new StringBuilder(strLen);
for(int i=0; i < strLen; i++) {
char c = str.charAt(i);
if(isNotEnd && c == separator) {
list.add(sb.toString());
//清空StringBuilder
sb.delete(0, sb.length());
//当达到切分上限-1的量时,将所剩字符全部作为最后一个串
if(limit !=0 && list.size() == limit-1) {
isNotEnd = false;
}
}else {
sb.append(c);
}
}
list.add(sb.toString());
return list;
}
/**
* 切分字符串<br>
* from jodd
* @param str 被切分的字符串
* @param delimiter 分隔符
* @return 字符串
*/
public static String[] split(String str, String delimiter) {
if(str == null) {
return null;
}
if(str.trim().length() == 0) {
return new String[]{str};
}
int dellen = delimiter.length(); //del length
int maxparts = (str.length() / dellen) + 2; // one more for the last
int[] positions = new int[maxparts];
int i, j = 0;
int count = 0;
positions[0] = - dellen;
while ((i = str.indexOf(delimiter, j)) != -1) {
count++;
positions[count] = i;
j = i + dellen;
}
count++;
positions[count] = str.length();
String[] result = new String[count];
for (i = 0; i < count; i++) {
result[i] = str.substring(positions[i] + dellen, positions[i + 1]);
}
return result;
}
/**
* 改进JDK subString<br>
* index从0开始计算,最后一个字符为-1<br>
* 如果from和to位置一样,返回 ""
* example:
* abcdefgh 2 3 -> c
* abcdefgh 2 -3 -> cde
* @param string String
* @param fromIndex 开始的index(包括)
* @param toIndex 结束的index(不包括)
* @return 字串
*/
public static String sub(String string, int fromIndex, int toIndex) {
int len = string.length();
if (fromIndex < 0) {
fromIndex = len + fromIndex;
if (toIndex == 0) {
toIndex = len;
}
}
if (toIndex < 0) {
toIndex = len + toIndex;
}
if(toIndex < fromIndex) {
int tmp = fromIndex;
fromIndex = toIndex;
toIndex = tmp;
}
if(fromIndex == toIndex) {
return EMPTY;
}
char[] strArray = string.toCharArray();
char[] newStrArray = Arrays.copyOfRange(strArray, fromIndex, toIndex);
return new String(newStrArray);
}
/**
* 切割前部分
* @param string 字符串
* @param toIndex 切割到的位置(不包括)
* @return 切割后的字符串
*/
public static String subPre(String string, int toIndex) {
return sub(string, 0, toIndex);
}
/**
* 切割后部分
* @param string 字符串
* @param fromIndex 切割开始的位置(包括)
* @return 切割后的字符串
*/
public static String subSuf(String string, int fromIndex) {
if(isEmpty(string)) {
return null;
}
return sub(string, fromIndex, string.length());
}
/**
* 重复某个字符
* @param c 被重复的字符
* @param count 重复的数目
* @return 重复字符字符串
*/
public static String repeat(char c, int count) {
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(String str, int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(str);
}
return sb.toString();
}
/**
* 给定字符串转换字符编码<br/>
* 如果参数为空,则返回原字符串,不报错。
* @param str 被转码的字符串
* @param sourceCharset 原字符集
* @param destCharset 目标字符集
* @return 转换后的字符串
*/
public static String convertCharset(String str, String sourceCharset, String destCharset) {
if(isBlank(str) || isBlank(sourceCharset) || isBlank(destCharset)) {
return str;
}
try {
return new String(str.getBytes(sourceCharset), destCharset);
} catch (UnsupportedEncodingException e) {
return str;
}
}
/**
* 比较两个字符串是否相同,如果为null或者空串则算不同
* @param str1 字符串1
* @param str2 字符串2
* @return 是否非空相同
*/
public static boolean equalsNotEmpty(String str1, String str2) {
if(isEmpty(str1)) {
return false;
}
return str1.equals(str2);
}
/**
* 格式化文本
* @param template 文本模板,被替换的部分用 {} 表示
* @param values 参数值
* @return 格式化后的文本
*/
public static String format(String template, Object... values) {
if(CollectionUtil.isEmpty(values) || isBlank(template)) {
return template;
}
final StringBuilder sb = new StringBuilder();
final int length = template.length();
int valueIndex = 0;
char currentChar;
for(int i = 0; i < length; i++) {
if(valueIndex >= values.length) {
sb.append(sub(template, i, length));
break;
}
currentChar = template.charAt(i);
if(currentChar == '{') {
final char nextChar = template.charAt(++i);
if(nextChar == '}') {
sb.append(values[valueIndex ++]);
}else {
sb.append('{').append(nextChar);
}
}else {
sb.append(currentChar);
}
}
return sb.toString();
}
/**
* 格式化文本
* @param template 文本模板,被替换的部分用 {key} 表示
* @param map 参数值对
* @return 格式化后的文本
*/
public static String format(String template, Map<?, ?> map) {
if(null == map || map.isEmpty()) {
return template;
}
for (Entry<?, ?> entry : map.entrySet()) {
template = template.replace("{" + entry.getKey() + "}", entry.getValue().toString());
}
return template;
}
/**
* 编码字符串
* @param str 字符串
* @param charset 字符集
* @return 编码后的字节码
*/
public static byte[] encode(String str, String charset) {
if(str == null) {
return null;
}
try {
return str.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(format("Charset [{}] unsupported!", charset));
}
}
/**
* 解码字节码
* @param data 字符串
* @param charset 字符集
* @return 解码后的字符串
*/
public static String decode(byte[] data, String charset) {
if(data == null) {
return null;
}
try {
return new String(data, charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(format("Charset [{}] unsupported!", charset));
}
}
/**
* 将多个对象字符化<br>
* 每个对象字符化后直接拼接,无分隔符
* @param objs 对象数组
* @return 字符串
*/
public static String str(Object... objs) {
StringBuilder sb = new StringBuilder();
for (Object obj : objs) {
sb.append(obj);
}
return sb.toString();
}
}