記錄一下維護程式遇到的一個 bug
資料庫欄位是 Oracle VARCHAR2(byte)
但程式並沒有校驗傳入參數的長度
而是要在寫入 DB 前才截斷超過的部分
方法如下
public static String substringByBytes(String string, int length) {
if (string == null)
return null;
return new String(string.getBytes(), 0, length);
}
但結果卻不如預期
String cuttedString = substringByBytes("壹貳參肆", 11);
System.out.println(cuttedString); //壹貳參�
System.out.println(cuttedString.getBytes().length); //12
字串「壹貳參肆」在 UTF8 編碼下共 12 bytes
若將其擷取為 11 bytes
最後一個字元「肆」會從 3 bytes 被切成 2 bytes
結果出現的奇怪字元本身仍是佔 3 bytes
因此長度並未減少
String cuttedString = substringByBytes("壹貳參abc", 11);
System.out.println(cuttedString); //壹貳參ab
System.out.println(cuttedString.getBytes().length); //11
可以對比
若非 UTF8 就是正常的
因此必須逐個字元計算 bytes 長度
修改後如下
public static String substringByBytesWithCompleteChars(String string, int length, Charset charset) {
if (string == null)
return null;
if (string.getBytes(charset).length <= length)
return string;
StringBuilder result = new StringBuilder();
int count = 0;
for (String str : string.split("")) {
count += str.getBytes(charset).length;
if (count > length)
break;
result.append(str);
}
return result.toString();
}