首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 精準截取混合中英字符串:按字節長度截斷並補全尾部

     編輯:Java 相關知識

業務痛點

前端列表展示、消息預覽、搜索高亮等場景,常需「顯示前 N 個字符」。但 Java String.substring 按 char (UTF-16 代碼單元) 計數,導致:

  • 中文字符佔 2-3 字節,英文佔 1 字節,視覺寬度不一
  • 直接按字符數截取,中英混排時顯示寬度差異大
  • 若按字節截取,易將多字節字符「切半」產生亂碼

解決方案:按字節長度截取 + 邊界修正

import java.util.Arrays;

public class ByteSubstringUtil {
    /**
     * @param str 輸入字符串
     * @param charLen 期望顯示字符數(按「中文佔2、英文佔1」的視覺寬度估算)
     * @return 截取後字符串,超長時尾部追加 ".."
     */
    public static String subStringByVisualLength(String str, int charLen) {
        if (str == null) return "";
        int byteLen = charLen * 2; // 視覺長度換算為字節預算
        byte[] bytes = str.getBytes(StandardCharsets.UTF_8); // 明確編碼

        if (bytes.length <= byteLen) return str; // 無需截取

        // 1. 先按字節硬截取
        byte[] truncated = Arrays.copyOf(bytes, byteLen);

        // 2. 計算截取區間內的「負字節數」(UTF-8 中文高位為 1)
        int negativeCount = 0;
        for (byte b : truncated) {
            if (b < 0) negativeCount++;
        }

        // 3. 若負字節數為奇數,說明切斷了雙字節字符,需向後擴展 1 字節
        if (negativeCount % 2 != 0) {
            if (byteLen < bytes.length) {
                truncated = Arrays.copyOf(bytes, byteLen + 1);
            }
        }

        return new String(truncated, StandardCharsets.UTF_8) + "..";
    }

    // 測試
    public static void main(String[] args) {
        String s = "2024年中秋節快樂 Hello";
        System.out.println(subStringByVisualLength(s, 10)); // 2024年中秋..
        System.out.println(subStringByVisualLength(s, 20)); // 2024年中秋節快樂 He..
    }
}

原理深度解析

字符類型UTF-8 字節首字節最高位負字節判定
ASCII (英文/數字)1 字節0 (0xxxxxxx)b >= 0
中文 (BMP)3 字節1 (1110xxxx)b < 0
Emoji (補充平面)4 字節1 (11110xxx)b < 0

UTF-8 多字節字符的 所有字節最高位均為 1(即 Java byte 為負數)。統計截取區間負字節總數:

  • 偶數:邊界落在字符邊界,安全
  • 奇數:切中字符內部,需向後擴展 1 字節完成該字符

進階優化版(支援自定義編碼、無後綴、精確字符數)

public static String subStringExactChars(String str, int maxChars, String suffix) {
    if (str == null) return "";
    int count = 0;
    int byteEnd = 0;
    byte[] bytes = str.getBytes(StandardCharsets.UTF_8);

    for (int i = 0; i < bytes.length; ) {
        int charLen = 1;
        if (bytes[i] < 0) { // 多字節字符
            // 簡易判斷:連續負字節屬於同一字符
            while (i + charLen < bytes.length && bytes[i + charLen] < 0) charLen++;
        }
        if (count + 1 > maxChars) break;
        count++;
        i += charLen;
        byteEnd = i;
    }
    return new String(bytes, 0, byteEnd, StandardCharsets.UTF_8) 
           + (count < maxChars ? "" : (suffix != null ? suffix : ".."));
}

總結

核心邏輯:UTF-8 編碼 → 按字節預算截取 → 統計負字節奇偶性修正邊界。該方案無需引入第三方庫,僅依賴 JDK 基礎 API,適用於對性能敏感、中英混排的截斷場景。若需嚴格按「字符數」而非視覺寬度截取,請使用進階版逐字符解碼邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved