You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中Encoding.UTF8解码不同字节得同串及字节数获取问题

问题复现代码
byte[] b1 = new byte[] { 60, 239, 191, 189, 14, 239, 191, 189, 2, 14, 62, 32, 23, 37, 239, 191, 189, 239, 191, 189, 127, 58, 50, 52, 56, 32, 95, 112, 117, 98, 95, 110, 117, 98, 95, 99, 108, 105, 101, 110, 116, 46, 99, 112, 112, 32, 58, 111, 110, 82, 101, 99, 101, 105, 118, 101, 84, 97, 112, 78, 111, 116, 105, 102, 121, 68, 97, 116, 97, 13, 10 };
byte[] b2 = new byte[] { 60, 215, 14, 235, 164, 2, 14, 62, 32, 23, 37, 207, 255, 127, 58, 50, 52, 56, 32, 95, 112, 117, 98, 95, 110, 117, 98, 95, 99, 108, 105, 101, 110, 116, 46, 99, 112, 112, 32, 58, 111, 110, 82, 101, 99, 101, 105, 118, 101, 84, 97, 112, 78, 111, 116, 105, 102, 121, 68, 97, 116, 97, 13, 10 };

var s1 = Encoding.UTF8.GetString(b1);
var s2 = Encoding.UTF8.GetString(b2);

var sc1 = Encoding.UTF8.GetByteCount(s1);
var sc2 = Encoding.UTF8.GetByteCount(s2);
异常现象
  • 内容完全不同的字节数组b1、b2,经UTF8解码后s1 == s2返回true,二者字符串内容完全一致,均为<�\u000e�\u0002\u000e> \u0017%��\u007f:248 _pub_nub_client.cpp :onReceiveTapNotifyData\r\n
  • Encoding.UTF8.GetByteCount返回的sc1、sc2均为71,但原始字节数组b2实际长度仅为64,无法通过解码后的字符串反推原始字节的实际占用长度。
根本原因

.NET默认的UTF8编码实例开启了替换回退(Replacement Fallback)机制:

  • 解码过程中遇到不符合UTF8编码规则的非法字节序列时,不会抛出异常,会将整段非法字节统一替换为Unicode替换字符U+FFFD(即字符串中看到的�)。
  • b2中存在多段非法UTF8字节:包括单独的高位字节215、14、2、连续不完整的多字节序列235+164、207+255,这些片段解码时全部被替换为U+FFFD;而b1中本身存储的就是U+FFFD对应的合法UTF8三字节编码239,191,189,因此两个内容完全不同的字节数组最终解码得到了完全相同的字符串。
  • 对解码后的字符串调用GetByteCount时,所有U+FFFD字符都会被编码为标准的3字节UTF8序列,和原始非法字节的长度(1字节、2字节不等)没有对应关系,因此返回值和原始字节数组长度必然不匹配。
解决方案
  • 优先方案:如果业务需要完整保留原始字节信息、准确获取字节长度,禁止直接将非纯UTF8的二进制字节数组通过Encoding.UTF8.GetString转为字符串存储。二进制数据应使用Convert.ToBase64String转为Base64格式字符串,需要读取原始字节时通过Convert.FromBase64String完整还原,全程无信息丢失,长度完全匹配。
  • 次选方案:如果必须直接对原始字节做UTF8解码,同时需要统计原始字节长度,需要自定义解码逻辑:
    1. 自定义DecoderFallback实现,在解码过程中记录每一处非法字节序列的起始位置、原始长度;
    2. 累计合法UTF8字符对应的字节长度、所有非法字节段的原始长度,总和即为原始字节数组的实际长度;
    3. 禁止对解码后的字符串直接调用GetByteCount反推原始长度,该方法只能返回字符串重新编码为标准合法UTF8的字节长度,和含非法序列的原始字节长度无映射关系。
  • 规避方案:如果处理的内容混合了二进制片段和文本,优先使用byte[]作为原始存储载体,不要强行转为string类型处理,从根源上避免非法字节被替换导致的信息丢失、长度失配问题。

内容的提问来源于stack exchange,提问作者Jim.Jiang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:51:07