不能直接用 string.length,因其返回utf-16码元个数而非utf-8字节数:如'€'长度为1但utf-8占3字节,'?'长度为2(代理对)但utf-8占4字节。

JavaScript中字符串按字节长度截取,关键在于正确处理Unicode字符(尤其是UTF-16代理对和扩展Unicode字符),因为JavaScript内部用UTF-16编码,而“字节长度”通常指UTF-8编码下的字节数。直接用.length会返回字符数而非字节数,容易出错。
为什么不能直接用 string.length?
JS字符串的.length返回的是UTF-16码元(code unit)个数,不是字符数,更不是UTF-8字节数。例如:
-
'a'.length === 1→ UTF-8占1字节 ✔ -
'€'.length === 1→ 实际UTF-8占3字节 ✖(但.length仍是1) -
'?'.length === 2→ 是一个代理对(surrogate pair),UTF-8占4字节,但.length为2
核心思路:先编码为UTF-8,再按字节截断
最可靠的方式是模拟UTF-8编码逻辑,逐字符计算其UTF-8字节长度,累加直到接近目标字节数,然后安全截断——避免把一个多字节字符切在中间。
- ASCII字符(U+0000–U+007F)→ 1字节
- U+0080–U+07FF → 2字节
- U+0800–U+FFFF → 3字节
- U+10000–U+10FFFF → 4字节(需识别代理对)
判断代理对的方法:charCode >= 0xD800 && charCode ,且高位在0xD800–0xDBFF、低位在0xDC00–0xDFFF时构成一个字符。
实用截取函数实现(不依赖外部库)
以下函数接收字符串和最大UTF-8字节数,返回截断后的字符串(保证UTF-8完整性):
function truncateByBytes(str, maxBytes) {
if (maxBytes = 0xD800 && code = 0xDC00 && nextCode maxBytes) break;
bytes += 4;
result += str[i] + str[i + 1];
i++; // 跳过低位代理
continue;
}
}
// 单个字符的UTF-8字节长度
let charBytes;
if (code 0xFFFF时已由代理对覆盖,此处兜底
}
if (bytes + charBytes > maxBytes) break;
bytes += charBytes;
result += str[i];
}
return result;
}
注意事项与边界情况
该方案已覆盖常见场景,但仍需注意:
- 不支持BOM、控制字符等特殊编码细节(一般业务无需考虑)
- 若原始字符串含无效代理对(如单独0xD800),按单个码元处理(UTF-8编码中视为替换字符,但此处保持原样)
- 性能敏感场景可预计算字节偏移表,避免重复遍历
- 服务端统一用Node.js的
Buffer.byteLength(str, 'utf8')更简单,但浏览器环境不可用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











