Bootstrap

Emoji字符串的真实长度、脱敏和截取

普通的substring不能正确截取带Emoji的字符串会导致乱码,这个工具类可以截取。

基本可用,个别情况比如这类“1️⃣”组合Emoji还是会错,据说这个emoji是由:'1'、一个变体选择器和'⃣'组成的。

也可以用str.substring(str.offsetByCodePoints(0, start), str.offsetByCodePoints(0, end))

但是实测发现现在的实现速度更快一些。

package util;


/**
 * Emoji字符串工具类
 *
 * @author [email protected]
 * @version 1.0.0 2023-12-21
 */
public class EmojiStringUtil {
    private EmojiStringUtil() {
    }

    /**
     * emoji字符串脱敏 - 仅前后各保留一个原文,中间替换为“*”
     * @param emojiStr 原始字符串
     * @param keepLength 是否保持原长度,保持原长度时中间隐去几个字符就加几个“*”,否则只加一个“*”
     *  Examples:
     *  <blockquote><pre>
     *  masking("hamburger", true) returns "h*******r"
     *  masking("hamburger", false) returns "h*r"
     *  </pre></blockquote>
     * @return
     */
    public static String masking(String emojiStr, boolean keepLength) {
        if (emojiStr==null || emojiStr.isEmpty()) {
            return "**";
        }

        int realStringLength = length(emojiStr);
        if (realStringLength == 1) {
            return emojiStr + "*";
        }
        String first = substring(emojiStr, 0, 1);
        if (realStringLength == 2) {
            return first + "*";
        }
        String last = substring(emojiStr, realStringLength - 1, realStringLength);
        // 保持原长度
        if (keepLength) {
            StringBuilder result = new StringBuilder();
            result.append(first);
            int fillCount = realStringLength-2;
            for (int i=0; i<fillCount; i++) {
                result.append("*");
            }
            result.append(last);
            return result.toString();
        }
        // 不保持原长度
        return first + "*" + last;
    }

    /**
     * 能处理emoji表情的substring方法
     * @param str 原有的str
     * @param beginIndex 起始位置(含)
     * @param endIndex 结束位置(不含)
     *  Examples:
     *  <blockquote><pre>
     *  substring("hamburger", 4, 4) returns ""
     *  substring("hamburger", 4, 8) returns "urge"
     *  substring("hamburger", 4, 20) returns "urger"
     *  substring("hamburger", 10, 20) returns ""
     *  </pre></blockquote>
     */
    public static String substring(String str, int beginIndex, int endIndex) {
        if (str==null || str.length()==0) {
            return str;
        }
        if (endIndex-beginIndex<=0 || beginIndex>=str.length()) {
            return "";
        }
        int realPosition = 0;
        int charStart = -1;
        int charEnd = -1;

        for (int i = 0; i < str.length(); i++) {
            if(charStart==-1 && realPosition==beginIndex) {
                charStart = i;
            }
            if(realPosition==endIndex) {
                charEnd = i;
                break;
            }
            int codePoint = str.codePointAt(i);
            if (Character.isSupplementaryCodePoint(codePoint)) {
                i++;
            }
            realPosition++;
        }
        // 未匹配到开始位置时返回空字符
        if (charStart==-1){
            return "";
        }
        // 未匹配到结尾时
        if (charEnd==-1) {
            return str.substring(charStart);
        }

        return str.substring(charStart, charEnd);
    }

    /**
     * 能处理emoji表情的substring方法
     * @param str 原有的str
     * @param beginIndex 起始位置(含)
     *  Examples:
     *  <blockquote><pre>
     *  substring("hamburger", 4) returns "urger"
     *  substring("hamburger", 10) returns ""
     *  </pre></blockquote>
     */
    public static String substring(String str, int beginIndex) {
        if (str==null || str.length()==0) {
            return str;
        }
        int realPosition = 0;
        int charStart = -1;

        for (int i = 0; i < str.length(); i++) {
            if(realPosition==beginIndex) {
                charStart = i;
                break;
            }
            int codePoint = str.codePointAt(i);
            if (Character.isSupplementaryCodePoint(codePoint)) {
                i++;
            }
            realPosition++;
        }
        // 未匹配到开始位置时返回空字符
        if (charStart==-1){
            return "";
        }

        return str.substring(charStart);
    }

    /**
     * 包含emoji表情的字符串的实际长度
     * @param str 原有str
     * @return str实际长度
     */
    public static int length(String str) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            int codePoint = str.codePointAt(i);
            if (Character.isSupplementaryCodePoint(codePoint)) {
                i++;
            }
            count++;
        }
        return count;
    }
}

;