按字节长度截取字符串,要求不能出现中文乱码,即尾部不能拼成完整多字节字符的字节会进行舍弃。
以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);
}
}