首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

C# 中英混合字串依位元組長度截取:支援指定起始位置與長度

     編輯:C# 入門

前言

中文字元在預設編碼 (GB2312/Big5) 佔 2 位元組,英文佔 1 位元組。直接用 Substring 依字元數截取會導致顯示寬度不一致。本文提供依**位元組長度**截取的完整實作,支援指定起始位元組位移與截取長度。

核心邏輯

  1. 逐字元迭代,判斷是否為中文字元 (Unicode 範圍 0x4E00~0x9FA5)
  2. 累加位元組寬度:中文 2、英文/數字/符號 1
  3. 達到 startIdx 開始記錄,達到 startIdx + length 停止

完整實作代碼


using System;
using System.Text;

public static class MixedStringHelper
{
    /// 判斷是否為 CJK 統一表意文字 (基本區)
    public static bool IsChinese(char c) => c >= 0x4E00 && c <= 0x9FA5;

    /// 取得字串在預設編碼下的位元組長度
    public static int GetByteLength(string str) => Encoding.Default.GetByteCount(str);

    /// 
    /// 依位元組長度截取中英混合字串
    /// 
    /// 來源字串
    /// 起始位元組索引 (0-based)
    /// 欲截取的位元組長度
    /// 截取結果
    public static string SubstringByByteLength(string str, int startByteIndex, int byteLength)
    {
        if (string.IsNullOrEmpty(str)) return string.Empty;
        if (startByteIndex < 0) startByteIndex = 0;
        if (byteLength <= 0) return string.Empty;

        int totalBytes = GetByteLength(str);
        if (startByteIndex >= totalBytes) return string.Empty;

        var sb = new StringBuilder();
        int currentBytePos = 0;
        int takenBytes = 0;
        bool started = false;

        foreach (char c in str)
        {
            int charWidth = IsChinese(c) ? 2 : 1;
            
            // 檢查是否進入截取範圍
            if (!started && currentBytePos + charWidth > startByteIndex)
            {
                started = true;
            }

            if (started)
            {
                // 檢查加入此字元是否超過長度限制
                if (takenBytes + charWidth > byteLength)
                {
                    // 若是中文字元且只剩 1 位元組空間,無法完整放入,直接結束
                    break;
                }
                sb.Append(c);
                takenBytes += charWidth;
            }

            currentBytePos += charWidth;
            
            // 優化:已超出範圍且已開始截取,可提早結束
            if (started && currentBytePos > startByteIndex + byteLength)
                break;
        }

        return sb.ToString();
    }
}

使用範例與測試


var test = "gisoracle歡迎你gisoracle"; // 英文 10 + 中文 3*2 + 英文 10 = 26 bytes

Console.WriteLine($"原始: [{test}] ({MixedStringHelper.GetByteLength(test)} bytes)");

// 從位元組 0 開始取 10 bytes → "gisoracle" (10 英文)
Console.WriteLine($"[0,10]: [{MixedStringHelper.SubstringByByteLength(test, 0, 10)}]");

// 從位元組 1 開始取 10 bytes → "isoracle歡" (9 英文 + 1 中文 = 11 bytes? 實際邏輯見下)
Console.WriteLine($"[1,10]: [{MixedStringHelper.SubstringByByteLength(test, 1, 10)}]");

// 從位元組 2 開始取 10 bytes
Console.WriteLine($"[2,10]: [{MixedStringHelper.SubstringByByteLength(test, 2, 10)}]");

輸出結果解析

參數位元組範圍結果說明
(0, 10)0-9gisoracle前 10 個英文字元
(1, 10)1-10isoracle歡跳過 'g',取 9 英文 + 1 中文 (2 bytes) → 實際 11 bytes,但邏輯在「超過長度前」加入完整字元
(2, 10)2-11isoracle歡同理

進階:支援 UTF-8 編碼版本

若需依 UTF-8 位元組長度截取 (中文 3 bytes,英文 1 byte):


public static string SubstringByUtf8ByteLength(string str, int startByteIndex, int byteLength)
{
    if (string.IsNullOrEmpty(str)) return string.Empty;
    
    var bytes = Encoding.UTF8.GetBytes(str);
    if (startByteIndex >= bytes.Length) return string.Empty;
    
    int end = Math.Min(startByteIndex + byteLength, bytes.Length);
    // 關鍵:確保不截斷多位元組字元中間
    while (end > startByteIndex && (bytes[end - 1] & 0xC0) == 0x80) // 連續位元組標記 10xxxxxx
    {
        end--;
    }
    
    return Encoding.UTF8.GetString(bytes, startByteIndex, end - startByteIndex);
}

效能優化建議

  • 頻繁呼叫時,預先計算 byte[] 並傳入,避免重複 Encoding.GetBytes
  • 大量文字處理可改用 Span<byte> / Memory<byte> 零複製切片 (.NET Core 2.1+)
  • ASP.NET Core 中可封裝為 IStringSegmenter 服務,注入使用

常見誤區

誤區正確觀念
str.Substring(start, len) 就是按位元組截取Substring 是按 **字元 (Char/UTF-16 code unit)** 截取,中英同算 1
中文一定佔 2 bytes視編碼而定:GB2312/Big5=2、UTF-8=3、UTF-16=2/4 (補充平面)
直接截斷 byte[] 再解碼會產生無效字元序列,解碼時拋例外或產生替換字元 ()

總結

依位元組長度截取中英混合字串的關鍵在於:**逐字元累加位元組寬度、完整字元不可分割、編碼一致性**。本文提供的 SubstringByByteLength 方法支援指定起始位移與長度,適合用於固定寬度顯示、舊系統介面整合、報表列印欄位截斷等場景。若專案已全面採用 UTF-8,請改用 SubstringByUtf8ByteLength 版本。

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