
net.apexes.commons.lang.Strings Maven / Gradle / Ivy
/*
* Copyright (c) 2018, apexes.net. All rights reserved.
*
* http://www.apexes.net
*
*/
package net.apexes.commons.lang;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author HeDYn
*
*/
public final class Strings {
private Strings() {}
private static final Random random = new Random();
public static String[] shorts(String src) {
String hex = Bytes.toHex(Secrets.md5(src));
String[] arr = new String[4];
for (int i = 0; i < 4; i++) {
// 把hex按照8位一组16进制与0x3FFFFFFF 进行位与运算
String sub = hex.substring(i * 8, i * 8 + 8);
// 这里需要使用long型来转换,因为Inteper.parseInt()只能处理31位, 首位为符号位, 如果不用long,则会越界
long hexLong = 0x3FFFFFFF & Long.parseLong(sub, 16);
StringBuilder outChars = new StringBuilder();
for (int j = 0; j < 6; j++) {
// 把得到的值与0x0000003D进行位与运算,取得字符数组chars索引
long index = 0x0000003D & hexLong;
// 把取得的字符相加
outChars.append(Radix62.ALPHABET[(int) index]);
// 每次循环按位右移5位
hexLong = hexLong >> 5;
}
// 把字符串存入对应索引的输出数组
arr[i] = outChars.toString();
}
return arr;
}
public static String hash(String src) {
StringBuilder sb = new StringBuilder();
int hash = Math.abs(MurmurHash.hash32(src));
while (hash > 0) {
int index = hash % Radix62.ALPHABET.length;
hash = hash / Radix62.ALPHABET.length;
sb.insert(0, Radix62.ALPHABET[index]);
}
return sb.toString();
}
/**
* 返回一个定长的随机字符串(只包含大小写字母、数字)
* @param length 随机字符串长度
* @return 随机字符串
*/
public static String randomString(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(Radix62.ALPHABET[random.nextInt(Radix62.ALPHABET.length)]);
}
return sb.toString();
}
/**
* 返回一个定长的随机纯字母字符串(只包含大小写字母)
* @param length 随机字符串长度
* @return 随机字符串
*/
public static String randomLetter(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(Radix62.ALPHABET[10 + random.nextInt(Radix62.ALPHABET.length - 10)]);
}
return sb.toString();
}
/**
* 生成一个定长的随机纯数字字符串
* @param length 随机字符串长度
* @return 随机字符串
*/
public static String randomNumber(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(random.nextInt(10));
}
return sb.toString();
}
private static final char[] UNALIKE_CHARACTER_SET = "23456789ABDEFGHJLMNQRTYabdefghijmnqrty".toCharArray();
/**
* 生成一个容易辨认字符组成的定长随机字符串
* @param length 随机字符串长度
* @return 随机字符串
*/
public static String randomUnalikeString(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(UNALIKE_CHARACTER_SET[random.nextInt(UNALIKE_CHARACTER_SET.length)]);
}
return sb.toString();
}
/**
* 去除字符串前面空白
* @param str 字符串
* @return 返回去除字符串前面空白后的字符串
*/
public static String ltrim(String str) {
int len = str.length();
int st = 0;
char[] val = str.toCharArray();
while ((st < len) && (val[st] <= ' ')) {
st++;
}
return (st > 0) ? str.substring(st, len) : str;
}
/**
* 去除字符串后面空白
* @param str 字符串
* @return 返回去除字符串后面空白后的字符串
*/
public static String rtrim(String str) {
int len = str.length();
char[] val = str.toCharArray();
while ((0 < len) && (val[len - 1] <= ' ')) {
len--;
}
return (len < str.length()) ? str.substring(0, len) : str;
}
/**
* 将首字节大写。注意:此方法与JavaBean规范中定义的属性的getter、setter方法名称不同。
* @param str 字符串
* @return 返回首字节大写后的字符串
*/
public static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return Character.toTitleCase(str.charAt(0)) + str.substring(1);
}
/**
* 将首字节小写。注意:此方法与JavaBean规范中定义的属性的getter、setter方法名称不同。
* @param str 字符串
* @return 返回首字节小写后的字符串
*/
public static String uncapitalized(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return Character.toLowerCase(str.charAt(0)) + str.substring(1);
}
/**
* @deprecated 使用 {@link #uncapitalized(String)}
*/
@Deprecated
public static String uncapitalize(String str) {
return uncapitalized(str);
}
/**
* 将驼峰式字符串转换为下划线小写方式。如果转换前的字符串为空,则返回空字符串。
* 转换规格是:在大写字符前增加“_”并转为小写
* 例如:helloWorld->hello_world
* @param str 转换前的驼峰式字符串
* @return 转换后下划线方式字符串
*/
public static String underscore(String str) {
if (str == null || str.trim().isEmpty()) {
return "";
}
int len = str.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char ch = str.charAt(i);
if (Character.isUpperCase(ch)) {
sb.append("_");
sb.append(Character.toLowerCase(ch));
} else {
sb.append(ch);
}
}
return sb.toString();
}
/**
* 将下划线方式字符串转为小驼峰式。如果转换前的字符串为空,则返回空字符串。
* 转换规格是:去掉“_”并大写其后的一个字符
* 例如:hello_world->helloWorld
* @param str 转换前的下划线方式字符串
* @return 转换后小驼峰式命名字符串
*/
public static String camelCase(String str) {
if (str == null || str.trim().isEmpty()) {
return "";
}
int len = str.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char ch = str.charAt(i);
if (ch == '_') {
i++;
if (i >= len) {
sb.append(ch);
break;
}
sb.append(Character.toUpperCase(str.charAt(i)));
} else {
sb.append(ch);
}
}
return sb.toString();
}
/**
* 使用 fmt 做模板格式化给定的参数,作用与 {@link MessageFormat#format(String, Object...)} 相同
* @param fmt 模板
* @param params 参数
* @return 返回格式化后的字符串
*/
public static String format(String fmt, final Object... params) {
if (params != null && params.length > 0) {
// MessageFormat 要求 It's 中的 ' 是 ''
String pattern = fmt.replaceAll("'", "''");
return MessageFormat.format(pattern, params);
}
return fmt;
}
/**
* 将数组转为用指定分隔符分隔的字符串
* @param array 数据
* @param separator 分隔符
* @return 返回用指定分隔符分隔的字符串
*/
public static String toString(Object[] array, String separator) {
StringBuilder sb = new StringBuilder();
for (Object object : array) {
if (sb.length() > 0) {
sb.append(separator);
}
sb.append(object);
}
return sb.toString();
}
/**
* 按指定长度,省略字符串部分字符
*
* @param str 字符串
* @param length 保留字符串字数
*
* @return 省略后的字符串
*/
public static String omitString(String str, int length) {
if (str == null) {
return null;
}
if (length <= 0) {
return "";
}
if (str.length() <= length) {
return str;
}
StringBuilder buf1 = new StringBuilder();
StringBuilder buf2 = new StringBuilder();
int i1 = 0;
int i2 = str.length() - 1;
char c;
length -= 3;
while (length > 0) {
c = str.charAt(i1);
length--;
buf1.append(c);
i1++;
if (length > 0 ) {
c = str.charAt(i2);
length--;
buf2.insert(0, c);
i2--;
}
}
buf1.append("...");
buf1.append(buf2);
return buf1.toString();
}
/**
* 如果指定字符串长度超过了最大长度,则对指定的字符串做截断处理
* @param text 字符串
* @param maxLength 最大长度
* @return 返回长度不超过 maxLength 的字段串
*/
public static String truncation(String text, int maxLength) {
if (text != null && text.length() > maxLength) {
return text.substring(0, maxLength);
}
return text;
}
/**
* Returns a string, of length at least {@code minLength}, consisting of {@code string} prepended
* with as many copies of {@code padChar} as are necessary to reach that length. For example,
*
*
* - {@code padStart("7", 3, '0')} returns {@code "007"}
*
- {@code padStart("2010", 3, '0')} returns {@code "2010"}
*
*
* See {@link java.util.Formatter} for a richer set of formatting capabilities.
*
* @param string the string which should appear at the end of the result
* @param minLength the minimum length the resulting string must have. Can be zero or negative, in
* which case the input string is always returned.
* @param padChar the character to insert at the beginning of the result until the minimum length
* is reached
* @return the padded string
*/
public static String padStart(String string, int minLength, char padChar) {
Checks.verifyNotNull(string, "string");
if (string.length() >= minLength) {
return string;
}
StringBuilder sb = new StringBuilder(minLength);
for (int i = string.length(); i < minLength; i++) {
sb.append(padChar);
}
sb.append(string);
return sb.toString();
}
/**
* Returns a string, of length at least {@code minLength}, consisting of {@code string} appended
* with as many copies of {@code padChar} as are necessary to reach that length. For example,
*
*
* - {@code padEnd("4.", 5, '0')} returns {@code "4.000"}
*
- {@code padEnd("2010", 3, '!')} returns {@code "2010"}
*
*
* See {@link java.util.Formatter} for a richer set of formatting capabilities.
*
* @param string the string which should appear at the beginning of the result
* @param minLength the minimum length the resulting string must have. Can be zero or negative, in
* which case the input string is always returned.
* @param padChar the character to append to the end of the result until the minimum length is
* reached
* @return the padded string
*/
public static String padEnd(String string, int minLength, char padChar) {
Checks.verifyNotNull(string, "string");
if (string.length() >= minLength) {
return string;
}
StringBuilder sb = new StringBuilder(minLength);
sb.append(string);
for (int i = string.length(); i < minLength; i++) {
sb.append(padChar);
}
return sb.toString();
}
/**
* Returns a string consisting of a specific number of concatenated copies of an input string. For
* example, {@code repeat("hey", 3)} returns the string {@code "heyheyhey"}.
*
*
Java 11+ users: use {@code string.repeat(count)} instead.
*
* @param string any non-null string
* @param count the number of times to repeat it; a nonnegative integer
* @return a string containing {@code string} repeated {@code count} times (the empty string if
* {@code count} is zero)
* @throws IllegalArgumentException if {@code count} is negative
*/
public static String repeat(String string, int count) {
Checks.verifyNotNull(string, "string");
if (count <= 1) {
return (count == 0) ? "" : string;
}
final int len = string.length();
final long longSize = (long) len * (long) count;
final int size = (int) longSize;
if (size != longSize) {
throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize);
}
final char[] array = new char[size];
string.getChars(0, len, array, 0);
int n;
for (n = len; n < size - n; n <<= 1) {
System.arraycopy(array, 0, array, n, n);
}
System.arraycopy(array, 0, array, n, size - n);
return new String(array);
}
public static String nullToEmpty(String string) {
return string == null ? "" : string;
}
public static String emptyToNull(String string) {
return string == null || string.isEmpty() ? null : string;
}
/**
* 将容器中的元素以 separator 分隔进行拼接
* @param collection 要拼接的数据
* @param separator 分隔符
* @return 返回拼接后的字符串
*/
public static String join(Collection> collection, String separator) {
return join(collection.toArray(), separator);
}
/**
* 将容器中的元素以 separator 分隔进行拼接
* @param array 要拼接的数据
* @param separator 分隔符
* @return 返回拼接后的字符串
*/
public static String join(Object[] array, String separator) {
if (array == null || array.length == 0) {
return null;
}
StringBuilder sb = new StringBuilder();
sb.append(array[0]);
for (int i = 1; i < array.length; i++) {
sb.append(separator).append(array[i]);
}
return sb.toString();
}
/**
* 解码 unicode 编码字符串
* @param unicode unicode编码的字符串
* @return 返回解码后的字符串
*/
public static String fromUnicode(String unicode) {
Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
Matcher matcher = pattern.matcher(unicode);
char ch;
while (matcher.find()) {
ch = (char) Integer.parseInt(matcher.group(2), 16);
unicode = unicode.replace(matcher.group(1), ch + "");
}
return unicode;
}
/**
* 转为 unicode 编码字符串
* @param input 要编码的字符串
* @return 返回编码后的字符串
*/
public static String toUnicode(String input) {
StringBuilder builder = new StringBuilder();
for (char ch : input.toCharArray()) {
builder.append(String.format("\\u%04x", (int) ch));
}
return builder.toString();
}
/**
* 将指定的字符串转换成适合HTML格式的字符串。
*/
public static String stringToHTMLString(String text) {
StringBuilder sb = new StringBuilder(text.length());
// true if last char was blank
boolean lastWasBlankChar = false;
int len = text.length();
char c;
for (int i = 0; i < len; i++) {
c = text.charAt(i);
if (c == ' ') {
// blank gets extra work,
// this solves the problem you get if you replace all
// blanks with , if you do that you loss
// word breaking
if (lastWasBlankChar) {
lastWasBlankChar = false;
sb.append(" ");
} else {
lastWasBlankChar = true;
sb.append(' ');
}
} else {
lastWasBlankChar = false;
//
// HTML Special Chars
if (c == '"') {
sb.append(""");
} else if (c == '&') {
sb.append("&");
} else if (c == '<') {
sb.append("<");
} else if (c == '>') {
sb.append(">");
} else if (c == '\n') {
// Handle Newline
sb.append("<br/>");
} else {
int ci = 0xffff & c;
if (ci < 160) {
// nothing special only 7 Bit
sb.append(c);
} else {
// Not 7 Bit use the unicode system
sb.append("");
sb.append(ci);
sb.append(';');
}
}
}
}
return sb.toString();
}
/**
* 获取自增型文件名。
* 如:
* a.txt --> a_1.txt
* b_5.txt --> b_6.txt
* c_2 --> c_3
*/
public static String autoincrementFilename(String filename) {
String s1;
String s2;
String suffix;
String tmp;
int m = filename.lastIndexOf('.');
if (m >= 0) {
tmp = filename.substring(0, m);
suffix = filename.substring(m);
} else {
tmp = filename;
suffix = "";
}
int n = tmp.lastIndexOf('_');
if (n >= 0) {
s1 = tmp.substring(0, n);
s2 = tmp.substring(n+1);
} else {
s1 = tmp;
s2 = "";
}
int i = 1;
if (s2.matches("^[1-9]\\d*$")) {
try {
i = Integer.parseInt(s2);
} catch (Exception ex) {
// ignore
}
i++;
return s1 + "_" + i + suffix;
}
if (m != -1) {
return tmp + "_"+ i + suffix;
}
return filename + "_"+ i;
}
/**
* 比较version1和version2,如果version1小于、等于、大于version2分别返回 -1、0、1
* @param version1 版本号1
* @param version2 版本号2
* @return 比较version1和version2,如果version1小于、等于、大于version2分别返回 -1、0、1
*/
public static int versionCompare(String version1, String version2) {
if (Checks.isBlank(version1)) {
return Checks.isBlank(version2) ? 0 : -1;
} else if (Checks.isBlank(version2)) {
return 1;
}
StringTokenizer t1 = new StringTokenizer(version1, "._");
StringTokenizer t2 = new StringTokenizer(version2, "._");
while (true) {
int n1 = 0;
int n2 = 0;
int c = 2;
if (t1.hasMoreTokens()) {
n1 = Integer.parseInt(t1.nextToken());
} else {
c--;
}
if (t2.hasMoreTokens()) {
n2 = Integer.parseInt(t2.nextToken());
} else {
c--;
}
if (c == 0) {
break;
}
int d = Integer.compare(n1, n2);
if (d != 0) {
return d;
}
}
return 0;
}
}