业务场景:
Oracle数据字符集UTF-8,字段类型为
Oracle数据字符集UTF-8,字段类型为
VARCHAR2
。当插入字符串的字节长度超过4000时,就会报错。使用subString(0,2000)
也会报错,因为数据库采用的字符编码不同每个汉字占用的字节数量也不一样。在数据库运行SQL语句:select * from v$nls_parameters t where t.PARAMETER='NLS_CHARACTERSET'
,返回当前数据库的字符集,如果value是GB2312,那么一个汉字占用2个字节,如果value是AL32UTF8,则一个汉字占用3个字节。所以又不能直接只截取1000长度,这就需要一个根据字节长度来截取字符串的方法。package com.demo.util;
import lombok.SneakyThrows;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @Author zf
* @ClassName SubStringUtils.java
* @ProjectName Demo
*/
public class SubStringUtils {
/**
* 按字节长度截取字符串
*
* @param orgin 需要截取的字符串
* @param blength 需要保留的字节长度
* @param charsetName 编码,对于字符集为UTF-8的数据库,请指定编码为UTF-8;字符集为GBK的数据库,请指定编码GBK
* @return 截取后的字符串
* @throws UnsupportedEncodingException 不支持的字符编码
*/
@SneakyThrows
public static String sub(String orgin, int blength, String charsetName){
return subList(orgin, blength, charsetName).get(0);
}
/**
* 按字节长度分割字符串
*
* @param orgin 需要截取的字符串
* @param blength 需要保留的字节长度
* @param charsetName 编码,对于字符集为UTF-8的数据库,请指定编码为UTF-8;字符集为GBK的数据库,请指定编码GBK
* @return 分割后的字符串
* @throws UnsupportedEncodingException 不支持的字符编码
*/
public static List<String> subList(String orgin, int blength, String charsetName) throws UnsupportedEncodingException {
List<String> result = new ArrayList<>();
int length;
byte[] bs = orgin.getBytes(charsetName);
while (bs.length > 0) {
length = blength;
if (length >= bs.length) {
result.add(new String(bs, 0, bs.length, charsetName));
break;
}
if ("UTF8".equals(charsetName.toUpperCase()) || "UTF-8".equals(charsetName.toUpperCase())) {
// utf8 encoding
// 0000 0000 - 0000 007F 0xxxxxxx
// 0000 0080 - 0000 07FF 110xxxxx 10xxxxxx
// 0000 0800 - 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
// 0001 0000 - 0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
while (length > 0) {
if ((bs[length] | 0x7F) == 0x7F) {
break;
}
if ((bs[length] & 0xC0) == 0xC0) {
break;
}
/*
if ((bs[length] & 0xE0) == 0xE0) {
break;
}
if ((bs[length] & 0xF0) == 0xF0) {
break;
}
*/
length--;
}
} else if ("GBK".equals(charsetName.toUpperCase())) {
boolean removLast = length % 2 == 1;
for (int i = 0; i < length; i++) {
if ((bs[i] | 0x7F) == 0x7F) {
removLast = !removLast;
}
}
if (removLast) {
length--;
}
} else if ("UNICODE".equals(charsetName.toUpperCase())) {
if (length % 2 == 1) {
length--;
}
} else if ("UTF-16".equals(charsetName.toUpperCase()) || "UTF16".equals(charsetName.toUpperCase())) {
if (length % 2 == 1) {
length--;
}
} else if ("UTF-16BE".equals(charsetName.toUpperCase())) {
if (length % 2 == 1) {
length--;
}
} else if ("UTF-16LE".equals(charsetName.toUpperCase())) {
if (length % 2 == 1) {
length--;
}
}
result.add(new String(bs, 0, length, charsetName));
bs = Arrays.copyOfRange(bs, length, bs.length);
}
if (result.size() == 0) {
result.add("");
}
return result;
}
}