All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.azbh111.utils.java.string.StringUtils Maven / Gradle / Ivy

The newest version!
package com.github.azbh111.utils.java.string;

import com.github.azbh111.utils.java.annotation.Nonnull;
import com.github.azbh111.utils.java.annotation.Nullable;
import com.github.azbh111.utils.java.annotation.Unsafe;
import com.github.azbh111.utils.java.charset.Charsets;
import com.github.azbh111.utils.java.string.model.CollationKeyContainer;
import com.github.azbh111.utils.java.string.model.InpuStreamFromString;
import sun.misc.SharedSecrets;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.text.CollationKey;
import java.text.Collator;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author pyz
 * @date 2019/12/19 11:20 上午
 */
public class StringUtils {
    /**
     * 手机号前缀
     * 用来验证是否是手机号
     */
    public static final List mobilePhonePrefixes = Collections.unmodifiableList(new ArrayList<>(new HashSet<>(
            Arrays.asList(
                    ("133,149,153,173,177,180,181,189,191,193,199,130," +
                            "131,132,145,155,156,166,171,175,176,185,186," +
                            "134,135,136,137,138,139,147,150,151,152,157," +
                            "158,159,172,178,182,183,184,187,188,195,198," +
                            "145,147,1700,1701,1702,162,1703,1705,1706,165," +
                            "1704,1707,1708,1709,171,167,1349,134,135,136," +
                            "137,138,139,147,150,151,152,157,158,159,1705," +
                            "178,182,183,184,187,188,198,130,131,132,145," +
                            "155,156,166,176,1709,171,185,186,133,153,180," +
                            "181,189,1700,177,173,199")
                            .replace("\n", "")
                            .split(","))
    )));

    /**
     * 满足mobilePhonePrefixes前缀规则后,用这个来验证手机号
     */
    private static final Pattern mobilePhonePattern = Pattern.compile("^1[0-9]{10}$");

    /**
     * ASCII表中可见字符从!开始,偏移位值为33(Decimal)
     */
    static final char DBC_CHAR_START = 33; // 半角!

    /**
     * ASCII表中可见字符到~结束,偏移位值为126(Decimal)
     */
    static final char DBC_CHAR_END = 126; // 半角~

    /**
     * 全角对应于ASCII表的可见字符从!开始,偏移值为65281
     */
    static final char SBC_CHAR_START = 65281; // 全角!

    /**
     * 全角对应于ASCII表的可见字符到~结束,偏移值为65374
     */
    static final char SBC_CHAR_END = 65374; // 全角~

    /**
     * ASCII表中除空格外的可见字符与对应的全角字符的相对偏移
     */
    static final int CONVERT_STEP = 65248; // 全角半角转换间隔

    /**
     * 全角空格的值,它没有遵从与ASCII的相对偏移,必须单独处理
     */
    static final char SBC_SPACE = 12288; // 全角空格 12288

    /**
     * 半角空格的值,在ASCII中为32(Decimal)
     */
    static final char DBC_SPACE = ' '; // 半角空格
    private static final Field stringValue;
    private static final Pattern CHECK_EMOJI = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]", Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
    private static final byte[] emptyByteArray = new byte[0];

    static {
        Field _stringValue = null;
        try {
            _stringValue = String.class.getDeclaredField("value");
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
        _stringValue.setAccessible(true);
        stringValue = _stringValue;
    }

    /**
     * 以流的形式读取字符串
     *
     * @param
     * @return java.io.InputStream
     * @author zhengyongpan
     * @since 2021/12/14 上午10:18
     */
    public static InputStream wrapToInputStream(CharSequence charSequence, Charset charset) {
        if (charSequence == null || charSequence.length() == 0) {
            return new ByteArrayInputStream(emptyByteArray);
        }
        return new InpuStreamFromString(charSequence, charset);
    }


    public static  String join(@Nonnull CharSequence separator, @Nonnull Iterable iterable) {
        return join(null, separator, null, iterable, i -> String.valueOf(i));
    }

    public static  String join(@Nonnull CharSequence separator, @Nonnull Iterable iterable, @Nonnull Function mapping) {
        return join(null, separator, null, iterable, mapping);
    }

    public static  String join(CharSequence prefix, @Nonnull CharSequence separator, CharSequence suffix, @Nonnull Iterable iterable) {
        return join(prefix, separator, suffix, iterable, i -> String.valueOf(i));
    }

    public static  String join(CharSequence prefix, @Nonnull CharSequence separator, CharSequence suffix, @Nonnull Iterable iterable, @Nonnull Function mapping) {
        StringBuilder sb = new StringBuilder();
        if (prefix != null) {
            sb.append(prefix);
        }
        Iterator it = iterable.iterator();
        if (it.hasNext()) {
            sb.append(mapping.apply(it.next()));
        }
        while (it.hasNext()) {
            sb.append(separator).append(mapping.apply(it.next()));
        }
        if (suffix != null) {
            sb.append(suffix);
        }
        return sb.toString();
    }

    /**
     * 反转字符串
     *
     * @return
     */
    public static String reverse(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        int length = str.length();
        char[] chars = new char[length];
        int maxLength = length - 1;
        for (int i = 0; i < length; i++) {
            chars[i] = str.charAt(maxLength - i);
        }
        return newStringZeroCopy(chars);
    }

    /**
     * 将str重复指定次数
     *
     * @param str
     * @param times
     * @return
     */
    public static String repeat(String str, int times) {
        if (times <= 0) {
            return "";
        }
        if (times == 1) {
            return str;
        }
        if (str == null || str.isEmpty()) {
            return str;
        }
        int length = str.length();
        int destLength = length * times;
        char[] chars = new char[destLength];
        char[] src = getStringValueUnsafe(str);
        for (int i = 0; i < destLength; i += length) {
            System.arraycopy(src, 0, chars, i, length);
        }
        return newStringZeroCopy(chars);
    }

    /**
     * 直接获取String对象里的字符数组
     * 警告: 禁止修改返回的char数组!
     *
     * @param str
     * @return
     */
    @Unsafe
    public static char[] getStringValueUnsafe(String str) {
        if (str == null) {
            return null;
        }
        try {
            return (char[]) stringValue.get(str);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断字符串是否包含emoji
     *
     * @param source
     * @return
     */
    public static boolean containsEmoj(String source) {
        int len = source.length();
        boolean isEmoji = false;
        for (int i = 0; i < len; i++) {
            char hs = source.charAt(i);
            if (0xd800 <= hs && hs <= 0xdbff) {
                if (source.length() > 1) {
                    char ls = source.charAt(i + 1);
                    int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
                    if (0x1d000 <= uc && uc <= 0x1f77f) {
                        return true;
                    }
                }
            } else {
                // non surrogate
                if (0x2100 <= hs && hs <= 0x27ff && hs != 0x263b) {
                    return true;
                } else if (0x2B05 <= hs && hs <= 0x2b07) {
                    return true;
                } else if (0x2934 <= hs && hs <= 0x2935) {
                    return true;
                } else if (0x3297 <= hs && hs <= 0x3299) {
                    return true;
                } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d
                        || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c
                        || hs == 0x2b1b || hs == 0x2b50 || hs == 0x231a) {
                    return true;
                }
                if (!isEmoji && source.length() > 1 && i < source.length() - 1) {
                    char ls = source.charAt(i + 1);
                    if (ls == 0x20e3) {
                        return true;
                    }
                }
            }
        }
        return isEmoji;
    }

    public static boolean isEmojiCharacter(char codePoint) {
        return !(
                (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA)
                        || (codePoint == 0xD)
                        || ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
                        || ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))
                        || ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))
        );
    }

    /**
     * 移除字符串中的emoji
     *
     * @param source
     * @return
     */
    public static String removeEmoji(@Nullable String source) {
        if (source == null || source.isEmpty()) {
            return source;
        }
        StringBuilder buf = null;
        int len = source.length();
        for (int i = 0; i < len; i++) {
            char codePoint = source.charAt(i);
            if (!isEmojiCharacter(codePoint)) {
                if (buf == null) {
                    buf = new StringBuilder(source.length());
                }
                buf.append(codePoint);
            }
        }
        if (buf == null) {
            return source;
        } else {
            if (buf.length() == len) {
                return source;
            } else {
                return buf.toString();
            }
        }
    }

    public static String trimToNull(@Nullable String src) {
        if (src == null || src.isEmpty()) {
            return null;
        }
        src = src.trim();
        if (src.isEmpty()) {
            return null;
        }
        return src;
    }

    public static String trimToEmpty(@Nullable String src) {
        if (src == null || src.isEmpty()) {
            return "";
        }
        src = src.trim();
        if (src.isEmpty()) {
            return "";
        }
        return src;
    }

    /**
     * 
     * 半角字符->全角字符转换
     * 只处理空格,!到˜之间的字符,忽略其他
     * 
*/ public static String hafl2Full(@Nullable String src) { if (src == null) { return src; } StringBuilder buf = new StringBuilder(src.length()); char[] ca = src.toCharArray(); for (int i = 0; i < ca.length; i++) { if (ca[i] == DBC_SPACE) { // 如果是半角空格,直接用全角空格替代 buf.append(SBC_SPACE); } else if ((ca[i] >= DBC_CHAR_START) && (ca[i] <= DBC_CHAR_END)) { // 字符是!到~之间的可见字符 buf.append((char) (ca[i] + CONVERT_STEP)); } else { // 不对空格以及ascii表中其他可见字符之外的字符做任何处理 buf.append(ca[i]); } } return buf.toString(); } /** *
     * 全角字符->半角字符转换
     * 只处理全角的空格,全角!到全角~之间的字符,忽略其他
     * 
*/ public static String full2Half(@Nullable String src) { if (src == null) { return src; } StringBuilder buf = new StringBuilder(src.length()); char[] ca = src.toCharArray(); for (int i = 0; i < src.length(); i++) { if (ca[i] >= SBC_CHAR_START && ca[i] <= SBC_CHAR_END) { // 如果位于全角!到全角~区间内 buf.append((char) (ca[i] - CONVERT_STEP)); } else if (ca[i] == SBC_SPACE) { // 如果是全角空格 buf.append(DBC_SPACE); } else { // 不处理全角空格,全角!到全角~区间外的字符 buf.append(ca[i]); } } return buf.toString(); } public static boolean isMobilePhone(@Nullable String str) { if (str == null || str.isEmpty()) { return false; } if (str.length() != 11) { return false; } for (String phonePrefix : mobilePhonePrefixes) { if (!str.startsWith(phonePrefix)) { return false; } } Matcher m = mobilePhonePattern.matcher(str); return m.matches(); } /** * 计算字符串的ansi字符个数(比如一个汉字占2个字符) * * @param str * @return */ public static int ansiCharCount(@Nullable CharSequence str) { int count = 0; if (str == null) { return count; } int length = str.length(); for (int i = 0; i < length; i++) { count += Character.charCount(Character.codePointAt(str, i)); } return count; } /** * 首字母大写 * * @param str * @return */ public static String upperCaseFirstCharacter(@Nullable String str) { return changeFirstLetter(str, 1); } /** * 首字母小写 * * @param str * @return */ public static String lowerCaseFirstCharacter(@Nullable String str) { return changeFirstLetter(str, -1); } /** * 字符串按字典排序 * * @param list */ public static void sortNature(@Nullable List list) { if (list == null || list.isEmpty()) { return; } sortNature(list, Collator.getInstance(Locale.CHINA)); } /** * 字符串按字典排序 * * @param list */ public static void sortNature(@Nullable List list, @Nonnull Collator comparetor) { if (list == null || list.isEmpty()) { return; } // 当需要多次比较时,转换成CollationKey会获得性能提升 CollationKey[] keys = new CollationKey[list.size()]; int index = 0; for (String str : list) { keys[index++] = comparetor.getCollationKey(str); } Arrays.sort(keys); ListIterator it = list.listIterator(); for (CollationKey key : keys) { it.next(); it.set(key.getSourceString()); } } /** * 字符串按字典排序 * * @param list */ public static void sortNature(@Nullable List list, @Nonnull Function keyMapper) { sortNature(list, Collator.getInstance(Locale.CHINA), keyMapper); } /** * 字符串按字典排序 * * @param list */ public static void sortNature(@Nullable List list, @Nonnull Collator collator, @Nonnull Function keyMapper) { if (list == null || list.isEmpty()) { return; } // 当需要多次比较时,转换成CollationKey会获得性能提升 CollationKeyContainer[] keys = new CollationKeyContainer[list.size()]; int index = 0; for (T t : list) { keys[index++] = new CollationKeyContainer(collator.getCollationKey(keyMapper.apply(t)), t); } Arrays.sort(keys); ListIterator it = list.listIterator(); for (CollationKeyContainer key : keys) { it.next(); it.set(key.getExtend()); } } public static boolean isEmpty(@Nullable CharSequence str) { return str == null || str.length() == 0; } public static boolean isNotEmpty(@Nullable CharSequence str) { return !isEmpty(str); } /** * Check whether the given {@code CharSequence} contains actual text. *

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. *

     * StringUtils.hasText(null) = false
     * StringUtils.hasText("") = false
     * StringUtils.hasText(" ") = false
     * StringUtils.hasText("12345") = true
     * StringUtils.hasText(" 12345 ") = true
     * 
* * @param str 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(@Nullable CharSequence str) { return str == null || str.length() == 0 || !containsText(str); } public static boolean isNotBlank(@Nullable CharSequence str) { return !isBlank(str); } /** * 在字符串收尾填充字符,使其达到指定长度 * 当可填充字符数为奇数时,优先填充起始位置 * * @param content * @param character * @param length * @return */ public static String center(String content, char character, int length) { return center(content, character, character, length); } /** * 在字符串收尾填充字符,使其达到指定长度 * 当可填充字符数为奇数时,优先填充prefix * * @param content * @param prefix * @param suffix * @param length * @return */ public static String center(String content, char prefix, char suffix, int length) { if (content == null) { return content; } if (content.length() >= length) { return content; } char[] chars = new char[length]; int center = content.length(); int right = (length - center) / 2; int left = length - center - right; for (int i = 0; i < left; i++) { chars[i] = prefix; } for (int i = 0; i < right; i++) { chars[chars.length - right + i] = suffix; } for (int i = 0; i < center; i++) { chars[left + i] = content.charAt(i); } return newStringZeroCopy(chars); } /** * 用char数组零拷贝创建字符串 * 警告: 创建字符串后,禁止修改源src * * @param bytes * @return */ @Unsafe public static String newStringZeroCopy(char[] bytes) { return SharedSecrets.getJavaLangAccess().newStringUnsafe(bytes); } public static String toString(InputStream stream) throws IOException { return toString(stream, Charsets.UTF_8); } public static String toString(InputStream stream, Charset charset) throws IOException { StringBuilder sb = new StringBuilder(); char[] chars = new char[128]; BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset)); int n = -1; while ((n = reader.read(chars)) != -1) { sb.append(chars, 0, n); } return sb.toString(); } private static boolean containsText(CharSequence str) { int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(str.charAt(i))) { return true; } } return false; } private static String changeFirstLetter(String str, int flag) { if (str == null || str.isEmpty()) { return str; } char[] chars = str.toCharArray(); if (flag == 1) { chars[0] = Character.toUpperCase(chars[0]); } else if (flag == -1) { chars[0] = Character.toLowerCase(chars[0]); } String r = newStringZeroCopy(chars); return r; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy