中文字元在 DBCS 編碼 (Big5/CP950) 佔 2 位元組,英文佔 1 位元組。直接用 Substring(0, n) 截取字元數會導致視覺寬度不一。需依「位元組長度」截取並補上省略號。
using System;
using System.Text;
public static class StringExtensions
{
///
/// 依指定編碼的位元組長度截取字串,超長時附加省略號
///
public static string CutByByteLength(this string input, int maxBytes, Encoding encoding = null, string ellipsis = "…")
{
if (string.IsNullOrEmpty(input)) return input;
encoding ??= Encoding.Default; // 系統預設編碼 (中文環境為 CP950)
byte[] bytes = encoding.GetBytes(input);
if (bytes.Length <= maxBytes) return input;
// 截取位元組陣列並轉回字串 (可能截斷多位元組字元,需處理)
byte[] truncated = new byte[maxBytes];
Array.Copy(bytes, truncated, maxBytes);
// 嘗試解碼,遇到無效位元組序列會產生替換字元 '?'
string result = encoding.GetString(truncated);
// 移除結尾可能的替換字元 (半個中文字元)
if (result.EndsWith("?"))
result = result.TrimEnd('?');
return result + ellipsis;
}
///
/// 更精確版:逐字元檢查累積位元組長度,避免截斷多位元組字元
///
public static string CutByByteLengthPrecise(this string input, int maxBytes, Encoding encoding = null, string ellipsis = "…")
{
if (string.IsNullOrEmpty(input)) return input;
encoding ??= Encoding.Default;
int currentBytes = 0;
int charCount = 0;
foreach (char c in input)
{
int charBytes = encoding.GetByteCount(new char[] { c });
if (currentBytes + charBytes > maxBytes) break;
currentBytes += charBytes;
charCount++;
}
if (charCount >= input.Length) return input;
return input.Substring(0, charCount) + ellipsis;
}
}
string s = "Hello 世界 123";
Console.WriteLine(s.CutByByteLength(10)); // "Hello 世…" (預設編碼)
Console.WriteLine(s.CutByByteLengthPrecise(10)); // "Hello 世…" (精確不截斷)
string utf8 = "Hello 世界";
Console.WriteLine(utf8.CutByByteLength(10, Encoding.UTF8)); // "Hello 世…" (UTF-8 中文佔3bytes)
| 場景 | 建議編碼 | 中文位元組數 |
|---|---|---|
| WinForms/WebForms 顯示寬度 (搭配 Monospace 字體) | Encoding.Default (CP950/Big5) | 2 |
| 網頁前端 (UTF-8 頁面) | Encoding.UTF8 | 3 |
| 資料庫欄位長度驗證 (VARCHAR BYTE) | 對應資料庫編碼 | 視編碼而定 |
using System.Drawing;
public static int GetTextWidth(string text, Font font)
{
using (var bmp = new Bitmap(1, 1))
using (var g = Graphics.FromImage(bmp))
{
return (int)g.MeasureString(text, font).Width;
}
}
// 依像素寬度截取
public static string CutByPixelWidth(string text, Font font, int maxWidth, string ellipsis = "…")
{
if (GetTextWidth(text, font) <= maxWidth) return text;
int low = 0, high = text.Length;
while (low < high)
{
int mid = (low + high + 1) / 2;
if (GetTextWidth(text.Substring(0, mid), font) <= maxWidth) low = mid;
else high = mid - 1;
}
return text.Substring(0, low) + ellipsis;
}
CutByByteLength (位元組截取 + 解碼)CutByByteLengthPrecise (逐字元累積位元組數)Graphics.MeasureString