![JAR search and dependency download from the Maven repository](/logo.png)
com.github.hugh.util.StringUtils Maven / Gradle / Ivy
package com.github.hugh.util;
import com.github.hugh.constant.CharsetCode;
import com.github.hugh.constant.guava.CharMatchers;
import com.github.hugh.exception.ToolboxException;
import com.google.common.base.CaseFormat;
import java.io.UnsupportedEncodingException;
import java.math.RoundingMode;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 字符串工具
*
* @author hugh
* @since 1.0.6
*/
public class StringUtils {
private StringUtils() {
}
/**
* 空白信息的表达式
*
* @since 1.3.1
*/
private static final Pattern BLANK_PATTERN = Pattern.compile("\\s*|\t|\r|\n");
/**
* 空字符串
*/
private static final String EMPTY = "";
/**
* 根据Unicode编码完美的判断中文汉字和符号
*
* @param car 字符串
* @return boolean
*/
private static boolean isChinese(char car) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(car);
return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
}
/**
* 校验字符串中是否存在中文
*
* @param string 字符串
* @return boolean
*/
public static boolean isContainChinese(String string) {
if (EmptyUtils.isEmpty(string)) {
return false;
}
char[] ch = string.toCharArray();
for (char c : ch) {
if (isChinese(c)) {
return true;
}
}
return false;
}
/**
* 校验字符串不是全中文名称
*
* @param string 字符串
* @return boolean {@code true}
* @since 1.4.3
*/
public static boolean isNotContainChinese(String string) {
return !isContainChinese(string);
}
/**
* 获取字符串中的纯数字
* 不包含小数点
*
* @param str 字符串
* @return String 0-9数字
*/
public static String getNumber(String str) {
return CharMatchers.NUMBER_CHAR_MATCHER.retainFrom(str);
}
/**
* 获取字符串中的数字及小数点
* 包含字符串中的小数点
*
* @param str 字符串
* @return String 保留小数点的数值
*/
public static String getDouble(String str) {
return CharMatchers.NUMBERS_AND_DECIMAL_POINTS.retainFrom(str);
}
/**
* 截取字符最后一个之前的所有字符串
*
* - 例:字符串:{@code https://github.com/HughWick/toolbox}
* - 返回字符串:{@code https://github.com/HughWick}
*
*
* @param value 字符串
* @param cha 拼接的字符
* @return String 获取指定最后一个字符之前所有字符串
*/
public static String before(String value, String cha) {
if (EmptyUtils.isEmpty(value)) {
return value;
}
if (value.contains(cha)) {
int lastIndexOf = value.lastIndexOf(cha);
return value.substring(0, lastIndexOf);
}
return value;
}
/**
* 截取字符最后一个之后的所有字符串
*
* - 例:字符串:C:\\Users\\Lenovo\\Desktop\\0e9f4beeb6a5423585c6eabda21a63ee.jpg
* - 返回字符串:0e9f4beeb6a5423585c6eabda21a63ee.jpg
*
*
* @param value 字符串
* @param cha 拼接的字符
* @return String 获取指定最后一个字符之后所有字符串
* @since 1.2.2
*/
public static String after(String value, String cha) {
if (EmptyUtils.isEmpty(value)) {
return value;
}
if (value.contains(cha)) {
int lastIndexOf = value.lastIndexOf(cha) + 1;
return value.substring(lastIndexOf);
}
return value;
}
/**
* 计算字符串中的对应varchar的长度
*
* - 由于旧的Mysql数据库一个中文算2个字节、本方法将字符串中的中文按2个长度进行合计
*
*
* @param value 字符串
* @return int 长度
* @since 1.1.3
*/
public static int varcharSize(String value) {
var length = 0;
var chinese = "[\u0391-\uFFE5]";
for (var i = 0; i < value.length(); i++) { /* 获取字段值的长度,如果含中文字符,则每个中文字符长度为2,否则为1 */
var temp = value.substring(i, i + 1); /* 获取一个字符 */
if (temp.matches(chinese)) { /* 判断是否为中文字符 */
length += 2; /* 中文字符长度为2 */
} else {
length += 1; /* 其他字符长度为1 */
}
}
return length;
}
/**
* 左补信息
*
* @param original 原始字符串
* @param targetLength 目标长度
* @param unit 补的元素
* @return String
* @since 1.3.1
*/
public static String leftPadding(final String original, final int targetLength, final char unit) {
//1. fast-return
final int originalLength = original.length();
if (originalLength >= targetLength) {
return original;
}
//2. 复制需要补充的长度 减掉 源数据的长度 加上 源字符串
return (String.valueOf(unit).repeat(targetLength - originalLength)) +
original;
}
/**
* 左补信息
* 默认左补零 0
*
* @param original 原始字符串
* @param targetLength 目标长度
* @return String
* @since 1.3.1
*/
public static String leftPadding(final String original, final int targetLength) {
return leftPadding(original, targetLength, '0');
}
/**
* 驼峰命名转下划线,小写
* 该转换方式调用{@link CaseFormat#to(CaseFormat, String)}
*
* @param camelStr 驼峰字符串
* @return String 下划线小写字符串
* @since 1.3.1
*/
public static String camelToUnderline(String camelStr) {
if (EmptyUtils.isEmpty(camelStr)) {
return null;
}
return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, camelStr);
}
/**
* 驼峰命名转下划线,大写
* 调用{@link #camelToUnderline(String)}驼峰转下划线命名方式
*
* @param camelStr 驼峰字符串
* @return String 下划线大写字符串
* @since 1.3.1
*/
public static String camelToUnderlineUppercase(String camelStr) {
String str = camelToUnderline(camelStr);
return str == null ? null : str.toUpperCase();
}
/**
* 将字符串转换为驼峰写法.
*
* - 注:这是 mybatis-gen 源码
* - 支持字符串格式_,-,@,#,$, ,/,{@code &}
*
*
* @param inputString 输入字符串
* @param firstCharacterUppercase 首字母是否大写。
* @return String 驼峰写法
* @since 1.3.1
*/
public static String getCamelCase(String inputString, boolean firstCharacterUppercase) {
StringBuilder stringBuilder = new StringBuilder();
boolean nextUpperCase = false;
for (var i = 0; i < inputString.length(); i++) {
char charAt = inputString.charAt(i);
switch (charAt) {
case '_':
case '-':
case '@':
case '$':
case '#':
case ' ':
case '/':
case '&':
if (stringBuilder.length() > 0) {
nextUpperCase = true;
}
break;
default:
if (nextUpperCase) {
stringBuilder.append(Character.toUpperCase(charAt));
nextUpperCase = false;
} else {
stringBuilder.append(Character.toLowerCase(charAt));
}
break;
}
}
if (firstCharacterUppercase) {
stringBuilder.setCharAt(0, Character.toUpperCase(stringBuilder.charAt(0)));
}
return stringBuilder.toString();
}
/**
* 将字符串中所有空格替换为指定元素
*
* @param string 原始字符串
* @param replacement 待替换的文本
* @return String 替换后结果
* @since 1.3.1
*/
public static String replaceAnyBlank(final String string, final String replacement) {
if (EmptyUtils.isEmpty(string)) {
return string;
}
Matcher matcher = BLANK_PATTERN.matcher(string);
String result = matcher.replaceAll(replacement);
result = result.replace("\\u00A0", replacement);
return result;
}
/**
* 替换掉任意空格为空
* 调用{@link #replaceAnyBlank(String, String)}替换字符串中所有空格
*
* @param string 原始字符串
* @return String 替换后结果
* @since 1.3.1
*/
public static String replaceAnyBlank(final String string) {
return replaceAnyBlank(string, EMPTY);
}
/**
* 去除前后空格
* 由于{@link String#strip()}无法去除一些特定的字符,所以再调用一次{@link String#trim()}再次去除
*
*
* @param value 字符串
* @return String
* @since 2.0.2
*/
public static String trim(String value) {
return value.trim().strip();
}
/**
* 去除前后指定字符
*
* 调用示例:System.out.println(trim(", ashuh ",","));
*
* @param source 目标字符串
* @param beTrim 要删除的指定字符
* @return String 删除之后的字符串
* @since 1.4.0
*/
public static String trim(String source, String beTrim) {
if (source == null) {
return null;
}
source = source.strip().trim();
if (source.isEmpty()) {
return source;
}
String beginChar = source.substring(0, 1);
if (beginChar.equalsIgnoreCase(beTrim)) {
source = source.substring(1);
}
// 循环去掉字符串尾的beTrim字符
String endChar = source.substring(source.length() - 1);
if (endChar.equalsIgnoreCase(beTrim)) {
source = source.substring(0, source.length() - 1);
}
return source;
}
/**
* 将字符串按照指定的字符与次数进行切割
*
* - 注:返回结果中首字符为指定切割的字符
* - 例:源字符串 http://www.baidu.com/capture/DaHua/capture/6G0BEB9GA12F70A/2021/1/17/9946090cb09b4986af8615174e862b9e.jpg
* - 获取"/"出现的第4次后的所有字符,结果为:/DaHua/capture/6G0BEB9GA12F70A/2021/1/17/9946090cb09b4986af8615174e862b9e.jpg
*
*
* @param string 源字符串
* @param chr 匹配的字符
* @param index 次数
* @return String 字符串
* @since 1.5.6
*/
public static String after(String string, String chr, int index) {
int index1 = indexOf(string, chr, index);
return string.substring(index1);
}
/**
* 根据字符串中指定字符的次数获取对应所在的下标
*
* @param string 源字符串
* @param chr 匹配的字符
* @param index 次数
* @return int 位置下标
* @since 1.5.6
*/
public static int indexOf(String string, String chr, int index) {
Pattern pattern = Pattern.compile(chr);
Matcher findMatcher = pattern.matcher(string);
var number = 0;
while (findMatcher.find()) {
number++;
if (number == index) {//当指定出现次数满足时停止
break;
}
}
return findMatcher.start();
}
/**
* 是否以指定字符串开头,忽略大小写
*
* @param str 被监测字符串
* @param prefix 开头字符串
* @return 是否以指定字符串开头
* @since 2.1.11
*/
public static boolean startWithIgnoreCase(CharSequence str, CharSequence prefix) {
return startWith(str, prefix, true);
}
/**
* 是否以指定字符串开头
* 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
*
* @param str 被监测字符串
* @param prefix 开头字符串
* @param ignoreCase 是否忽略大小写
* @return 是否以指定字符串开头
* @since 2.1.11
*/
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 boolean 是否以指定字符串开头
* @since 2.1.11
*/
public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase, boolean ignoreEquals) {
if (null == str || null == prefix) {
if (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 (!ignoreEquals) || (!equals(str, prefix, ignoreCase));
}
return false;
}
/**
* 比较两个字符串是否相等,规则如下
*
* - str1和str2都为{@code null}
* - 忽略大小写使用{@link String#equalsIgnoreCase(String)}判断相等
* - 不忽略大小写使用{@link String#contentEquals(CharSequence)}判断相等
*
*
* @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);
}
}
/**
* 修剪掉前后各一位字符
*
* @param source 源
* @return String
* @since 2.3.1
*/
public static String trimLastPlace(String source) {
if (EmptyUtils.isEmpty(source)) {
return source;
}
return source.substring(0, source.length() - 1);
}
/**
* 修剪掉前后各一位字符
*
* @param source 源字符串
* @return String
* @since 2.3.1
*/
public static String trimLastPlace(StringBuilder source) {
return trimLastPlace(source.toString());
}
/**
* 格式化字符串向下取整
*
* 默认为不分组
*
*
* @param value 值
* @param maximumFractionDigits 小数点后最大保留个数
* @return String
* @since 2.4.5
*/
public static String retainDecimalDown(T value, int maximumFractionDigits) {
final double d1 = Double.parseDouble(String.valueOf(value));
return retainDecimal(d1, maximumFractionDigits, false, RoundingMode.DOWN);
}
/**
* 向下取整
*
* - 例值4000
* - true:4,000,000
* - false:4000
*
*
* @param value 值
* @param maximumFractionDigits 最大保留个数
* @param groupingUsed 是否用逗号分割
* @return String
* @since 2.4.5
*/
public static String retainDecimalDown(T value, int maximumFractionDigits, boolean groupingUsed) {
return retainDecimal(value, maximumFractionDigits, groupingUsed, RoundingMode.DOWN);
}
/**
* 小数点后保留指定个数格式化
*
* @param value 值
* @param maximumFractionDigits 最大保留个数
* @param groupingUsed 是否用逗号分割
* @param roundingMode 取整模型{@link RoundingMode}
* @return String
* @since 2.4.5
*/
public static String retainDecimal(T value, int maximumFractionDigits, boolean groupingUsed, RoundingMode roundingMode) {
DoubleMathUtils.numberFormat.setMaximumFractionDigits(maximumFractionDigits);
DoubleMathUtils.numberFormat.setGroupingUsed(groupingUsed);
DoubleMathUtils.numberFormat.setRoundingMode(roundingMode);
return DoubleMathUtils.numberFormat.format(value);
}
/**
* 计算字符串在 GB2312 编码下的字节长度
* 一个中文占2个字节
*
* @param str 待计算字符串
* @return 字节长度
* @since 2.6.2
*/
public static int calcGb2312Length(String str) {
return getStringLength(str, CharsetCode.GB_2312);
}
/**
* 计算使用UTF-8字符集编码的字符串的字节长度
* 一个中文占3个字节
*
* @param str 输入的字符串
* @return 字节长度
*/
public static int calcUtf8Length(String str) {
return getStringLength(str, CharsetCode.UTF_8);
}
/**
* 计算使用UTF-16字符集编码的字符串的字节长度
* 一个中文占2个字节
*
* @param str 输入的字符串
* @return 字节长度
*/
public static int calcUtf16Length(String str) {
return getStringLength(str, CharsetCode.UTF_16);
}
/**
* 计算使用GBK字符集编码的字符串的字节长度
* 一个中文占2个字节
*
* @param str 输入的字符串
* @return 字节长度
*/
public static int calcGbkLength(String str) {
return getStringLength(str, CharsetCode.GBK);
}
/**
* 计算字符串在指定字符集下的字节长度
*
* @param str 待计算字符串
* @param charsetName 字符集名称
* @return 字节长度
* @since 2.6.2
*/
public static int getStringLength(String str, String charsetName) {
try {
byte[] bytes = str.getBytes(charsetName);
return bytes.length;
} catch (UnsupportedEncodingException unsupportedEncodingException) {
throw new ToolboxException(unsupportedEncodingException);
}
}
/**
* 去除重复字符串内容
*
* @param str 字符串
* @return String
* @since 2.6.8
*/
public static String removeDuplicate(String str) {
StringBuilder stringBuffer = new StringBuilder();
int len = str.length();
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
if (str.indexOf(c) == str.lastIndexOf(c)) {//此字符第一次位置和最后位置一致 即肯定没有重复的直接添加
stringBuffer.append(c);
} else {//同理 次字符出现过多次
int disposition = str.indexOf(c);//次字符第一次出现的位置
if (disposition == i) {//第一次出现的位置和当前位置一致 即第一次出现添加
stringBuffer.append(c);
}
}
}
return stringBuffer.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy