com.pdd.pop.sdk.common.util.StringUtils Maven / Gradle / Ivy
package com.pdd.pop.sdk.common.util;
import com.pdd.pop.sdk.common.constant.Constants;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 字符串工具类。
*/
public abstract class StringUtils {
/**
* An empty immutable String
array.
*/
public static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final TimeZone TZ_GMT8 = TimeZone.getTimeZone(Constants.DATE_TIMEZONE);
private static final Pattern PATTERN_CIDR = Pattern.compile("^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})/(\\d{1,2})$");
private static final String QUOT = """;
private static final String AMP = "&";
private static final String APOS = "'";
private static final String GT = ">";
private static final String LT = "<";
private StringUtils() {
}
/**
* 检查指定的字符串是否为空。
*
* - SysUtils.isEmpty(null) = true
* - SysUtils.isEmpty("") = true
* - SysUtils.isEmpty(" ") = true
* - SysUtils.isEmpty("abc") = false
*
*
* @param value 待检查的字符串
* @return true/false
*/
public static boolean isEmpty(String value) {
int strLen;
if (value == null || (strLen = value.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(value.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* 检查对象是否为数字型字符串,包含负数开头的。
*/
public static boolean isNumeric(Object obj) {
if (obj == null) {
return false;
}
char[] chars = obj.toString().toCharArray();
int length = chars.length;
if (length < 1) return false;
int i = 0;
if (length > 1 && chars[0] == '-') i = 1;
for (; i < length; i++) {
if (!Character.isDigit(chars[i])) {
return false;
}
}
return true;
}
/**
* 检查指定的字符串列表是否不为空。
*/
public static boolean areNotEmpty(String... values) {
boolean result = true;
if (values == null || values.length == 0) {
result = false;
} else {
for (String value : values) {
result &= !isEmpty(value);
}
}
return result;
}
/**
* 把通用字符编码的字符串转化为汉字编码。
*/
public static String unicodeToChinese(String unicode) {
StringBuilder out = new StringBuilder();
if (!isEmpty(unicode)) {
for (int i = 0; i < unicode.length(); i++) {
out.append(unicode.charAt(i));
}
}
return out.toString();
}
/**
* 把名称转换为小写加下划线的形式。
*/
public static String toUnderlineStyle(String name) {
StringBuilder newName = new StringBuilder();
int len = name.length();
for (int i = 0; i < len; i++) {
char c = name.charAt(i);
if (Character.isUpperCase(c)) {
if (i > 0) {
newName.append("_");
}
newName.append(Character.toLowerCase(c));
} else {
newName.append(c);
}
}
return newName.toString();
}
/**
* 把名称转换为首字母小写的驼峰形式。
*/
public static String toCamelStyle(String name) {
StringBuilder newName = new StringBuilder();
int len = name.length();
for (int i = 0; i < len; i++) {
char c = name.charAt(i);
if (i == 0) {
newName.append(Character.toLowerCase(c));
} else {
newName.append(c);
}
}
return newName.toString();
}
/**
* 把字符串解释为日期对象,采用yyyy-MM-dd HH:mm:ss的格式。
*/
public static Date parseDateTime(String str) {
DateFormat format = new SimpleDateFormat(Constants.DATE_TIME_FORMAT);
format.setTimeZone(TZ_GMT8);
try {
return format.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* 对日期进行字符串格式化,采用yyyy-MM-dd HH:mm:ss的格式。
*/
public static String formatDateTime(Date date) {
DateFormat format = new SimpleDateFormat(Constants.DATE_TIME_FORMAT);
format.setTimeZone(TZ_GMT8);
return format.format(date);
}
/**
* 对日期进行字符串格式化,采用指定的格式。
*/
public static String formatDateTime(Date date, String pattern) {
DateFormat format = new SimpleDateFormat(pattern);
format.setTimeZone(TZ_GMT8);
return format.format(date);
}
/**
* XML字符转义包括(<,>,',&,")五个字符.
*
* @param value 所需转义的字符串
* @return 转义后的字符串 @
*/
public static String escapeXml(String value) {
StringBuilder writer = new StringBuilder();
char[] chars = value.trim().toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
switch (c) {
case '<':
writer.append(LT);
break;
case '>':
writer.append(GT);
break;
case '\'':
writer.append(APOS);
break;
case '&':
writer.append(AMP);
break;
case '\"':
writer.append(QUOT);
break;
default:
if ((c == 0x9) || (c == 0xA) || (c == 0xD) || ((c >= 0x20) && (c <= 0xD7FF)) || ((c >= 0xE000) && (c <= 0xFFFD)) || ((c >= 0x10000) && (c <= 0x10FFFF)))
writer.append(c);
}
}
return writer.toString();
}
/**
* 获取类的get/set属性名称集合。
*
* @param clazz 类
* @param isGet 是否获取读方法,true为读方法,false为写方法
* @return 属性名称集合
*/
public static Set getClassProperties(Class> clazz, boolean isGet) {
Set propNames = new HashSet();
try {
if (clazz == null) {
return propNames;
}
BeanInfo info = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = info.getPropertyDescriptors();
for (PropertyDescriptor prop : props) {
String name = prop.getName();
Method method;
if (isGet) {
method = prop.getReadMethod();
} else {
method = prop.getWriteMethod();
}
if (!"class".equals(name) && method != null) {
propNames.add(name);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return propNames;
}
//-----------------------------------------------------------------------
/**
* Checks whether the String
contains only
* digit characters.
*
* Null
and empty String will return
* false
.
*
* @param str the String
to check
* @return true
if str contains only unicode numeric
*/
public static boolean isDigits(String str) {
if (StringUtils.isEmpty(str)) {
return false;
}
for (int i = 0; i < str.length(); i++) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
}
/**
* Splits the provided text into an array, separator specified.
* This is an alternative to using StringTokenizer.
*
* The separator is not included in the returned String array.
* Adjacent separators are treated as one separator.
* For more control over the split use the StrTokenizer class.
*
* A null
input String returns null
.
*
*
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
* StringUtils.split("a..b.c", '.') = ["a", "b", "c"]
* StringUtils.split("a:b:c", '.') = ["a:b:c"]
* StringUtils.split("a b c", ' ') = ["a", "b", "c"]
*
*
* @param str the String to parse, may be null
* @param separatorChar the character used as the delimiter
* @return an array of parsed Strings, null
if null String input
* @since 2.0
*/
public static String[] split(String str, char separatorChar) {
return splitWorker(str, separatorChar, false);
}
/**
* Performs the logic for the split
and
* splitPreserveAllTokens
methods that do not return a
* maximum array length.
*
* @param str the String to parse, may be null
* @param separatorChar the separate character
* @param preserveAllTokens if true
, adjacent separators are
* treated as empty token separators; if false
, adjacent
* separators are treated as one separator.
* @return an array of parsed Strings, null
if null String input
*/
private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) {
// Performance tuned for 2.0 (JDK1.4)
if (str == null) {
return null;
}
int len = str.length();
if (len == 0) {
return EMPTY_STRING_ARRAY;
}
List list = new ArrayList();
int i = 0, start = 0;
boolean match = false;
boolean lastMatch = false;
while (i < len) {
if (str.charAt(i) == separatorChar) {
if (match || preserveAllTokens) {
list.add(str.substring(start, i));
match = false;
lastMatch = true;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
if (match || (preserveAllTokens && lastMatch)) {
list.add(str.substring(start, i));
}
return (String[]) list.toArray(new String[list.size()]);
}
/**
* Performs the logic for the split
and
* splitPreserveAllTokens
methods that return a maximum array
* length.
*
* @param str the String to parse, may be null
* @param separatorChars the separate character
* @param max the maximum number of elements to include in the
* array. A zero or negative value implies no limit.
* @param preserveAllTokens if true
, adjacent separators are
* treated as empty token separators; if false
, adjacent
* separators are treated as one separator.
* @return an array of parsed Strings, null
if null String input
*/
private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {
// Performance tuned for 2.0 (JDK1.4)
// Direct code is quicker than StringTokenizer.
// Also, StringTokenizer uses isSpace() not isWhitespace()
if (str == null) {
return null;
}
int len = str.length();
if (len == 0) {
return EMPTY_STRING_ARRAY;
}
List list = new ArrayList();
int sizePlus1 = 1;
int i = 0, start = 0;
boolean match = false;
boolean lastMatch = false;
if (separatorChars == null) {
// Null separator means use whitespace
while (i < len) {
if (Character.isWhitespace(str.charAt(i))) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
} else if (separatorChars.length() == 1) {
// Optimise 1 character case
char sep = separatorChars.charAt(0);
while (i < len) {
if (str.charAt(i) == sep) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
} else {
// standard case
while (i < len) {
if (separatorChars.indexOf(str.charAt(i)) >= 0) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
}
if (match || (preserveAllTokens && lastMatch)) {
list.add(str.substring(start, i));
}
return (String[]) list.toArray(new String[list.size()]);
}
/**
* Splits the provided text into an array, separators specified.
* This is an alternative to using StringTokenizer.
*
* The separator is not included in the returned String array.
* Adjacent separators are treated as one separator.
* For more control over the split use the StrTokenizer class.
*
* A null
input String returns null
.
* A null
separatorChars splits on whitespace.
*
*
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("abc def", null) = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
*
*
* @param str the String to parse, may be null
* @param separatorChars the characters used as the delimiters,
* null
splits on whitespace
* @return an array of parsed Strings, null
if null String input
*/
public static String[] split(String str, String separatorChars) {
return splitWorker(str, separatorChars, -1, false);
}
/**
* 判断指定的IP地址是否在IP段里面。
*
* @param ipAddr IP地址
* @param cidrAddr 用CIDR表示法的IP段信息
* @return true/false
*/
public static boolean isIpInRange(String ipAddr, String cidrAddr) {
Matcher matcher = PATTERN_CIDR.matcher(cidrAddr);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid CIDR address: " + cidrAddr);
}
int[] minIpParts = new int[4];
int[] maxIpParts = new int[4];
String[] ipParts = matcher.group(1).split("\\.");
int intMask = Integer.parseInt(matcher.group(2));
for (int i = 0; i < ipParts.length; i++) {
int ipPart = Integer.parseInt(ipParts[i]);
if (intMask >= 8) {
minIpParts[i] = ipPart;
maxIpParts[i] = ipPart;
intMask -= 8;
} else if (intMask > 0) {
minIpParts[i] = ipPart >> intMask;
maxIpParts[i] = ipPart | (0xFF >> intMask);
intMask = 0;
} else {
minIpParts[i] = 1;
maxIpParts[i] = 0xFF - 1;
}
}
String[] realIpParts = ipAddr.split("\\.");
for (int i = 0; i < realIpParts.length; i++) {
int realIp = Integer.parseInt(realIpParts[i]);
if (realIp < minIpParts[i] || realIp > maxIpParts[i]) {
return false;
}
}
return true;
}
/**
* 将列表中的对象连接成字符串
*
* @param objs
* @param sep
* @return
*/
public static String join(Iterable> objs, String sep) {
StringBuilder buf = new StringBuilder();
join(buf, objs, sep);
return buf.toString();
}
/**
* 将列表中的对象连接起来。
*/
public static void join(StringBuilder buf, Iterable> objs, String sep) {
try {
join((Appendable) buf, objs, sep);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将列表中的对象连接起来。
*/
public static void join(Appendable buf, Iterable> objs, String sep) throws IOException {
if (objs == null) {
return;
}
if (sep == null) {
sep = "";
}
for (Iterator> i = objs.iterator(); i.hasNext(); ) {
buf.append(String.valueOf(i.next()));
if (i.hasNext()) {
buf.append(sep);
}
}
}
}