按字节长度截取字符串

2023-12-20 14:18:35

按字节长度截取字符串,要求不能出现中文乱码,即尾部不能拼成完整多字节字符的字节会进行舍弃。

以UTF-8编码为例,相关代码如下:

/**
     * 将字符串按照字节长度限制来进行截取,截取后的字节要能形成完整的字符。(尾部不能拼成完整多字节字符的字节会进行舍弃)
     * UTF-8说明:
     * UTF-8编码是一种变长编码方式,用于对Unicode字符集中的字符进行编码。在UTF-8编码中,每个字符可以由1到4个字节表示,具体取决于字符的Unicode码点
     * 根据UTF-8的编码规则,以下是不同长度字符的开始字节的模式:
     * 单字节字符(ASCII字符):开始字节以0开头。
     * 双字节字符:开始字节以110开头, 第二个字节以10开头。
     * 三字节字符:开始字节以1110开头, 第二个和第三个字节都以10开头。
     * 四字节字符:开始字节以11110开头, 其余三个字节都以10开头。
     * @param str 存在中文字符,一个中文字符占用2,3字节,需要避免截取时出现乱码
     * @param maxByteLength 最多返回的字节数
     */
    public static String subStringByByte(String str, int maxByteLength) {
        if (Strings.isNullOrEmpty(str)) {
            return str;
        }
        byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
        if (bytes.length <= maxByteLength) {
            return str;
        }
        // 字节字符占用的字节数
        int charLength = 1;
        int num = 0;
        // 根据截取字节长度,从数组截取处向前循环直到遇到正数后break,计算负数的个数,此负数个数的作用是,用来判断截取处,最后一个多字节字符是否完全被截取到
        for (int i = maxByteLength - 1; i >= 0; i--) {
            if (bytes[i] < 0) {
                num++;
                if ((bytes[i] & 0xE0) == 0xC0) { // 110xxxxx,双字节字符
                    charLength = 2;
                    break;
                } else if ((bytes[i] & 0xF0) == 0xE0) { // 1110xxxx,三字节字符
                   charLength = 3;
                   break;
                } else if ((bytes[i] & 0xF8) == 0xF0) { // 11110xxx,四字节字符
                  charLength = 4;
                  break;
                }
            } else {
                break;
            }
        }
        // 如果完全截取到,则截取byte[]的长度为(0, maxByteLength)
        // 如果截取了一部分,则应该截取byte[]的长度为(0, maxByteLength - (负数的个数 % 多字节字符占用字节))
        int sub = num % charLength;
        return new String(bytes, 0, maxByteLength - sub, StandardCharsets.UTF_8);
    }

test用例

    @Test
    public void testSubStringByByte() {
        String oneList = "!#$%&'()*+,-./:;<=>?@[\\]^_`{|}~1234567hvcxmwerDSFADADSJFAS";
        String twoList = "?あé";
        String threeList = "你好我好大家好才是真的";
        String fourList = "\uD83D\uDE04\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04\uD83D\uDE04\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04\uD83D\uDE04\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04";

        StringBuilder sb = new StringBuilder();
        int times = 50;
        Random rand = new Random();

        for (int i = 0; i < times; i++) {
            int randomNumber = rand.nextInt(oneList.length());
            sb.append(oneList.charAt(randomNumber));
            randomNumber = rand.nextInt(twoList.length());
            sb.append(twoList.charAt(randomNumber));
            randomNumber = rand.nextInt(threeList.length());
            sb.append(threeList.charAt(randomNumber));
            randomNumber = rand.nextInt(fourList.length() - 1);
            sb.append(fourList.charAt(randomNumber)).append(fourList.charAt(randomNumber + 1));
        }

        String data = sb.toString();
        System.out.println("原始数据  " + data);
        System.out.println("字节长度" + data.getBytes().length);

        for (int i = 20; i > 0; i--) {
            String result = RequestParamUtils.subStringByByte(data, i);
            System.out.println("结果数据   " + result + ",限制长度:" + i + ",字节长度" + result.getBytes().length);
        }

    }

文章来源:https://blog.csdn.net/xiao__jia__jia/article/details/135076733
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。