Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C# Unicode 编码代理项错误处理

C# Unicode 编码代理项错误处理

作者头像
jgrass
发布于 2024-12-25 09:55:16
发布于 2024-12-25 09:55:16
14300
代码可运行
举报
文章被收录于专栏:蔻丁杂记蔻丁杂记
运行总次数:0
代码可运行

问题描述

在一个 XML 序列化与文件保存的业务中,出现了一个异常:“代理项对无效,缺少低代理项字符。”

直接原因:业务提供的字符串中,有非法编码的字符。具体而言,就是如果出现了需要用代理项表示的字符,则必须成对出现(代理项对),前一个为高代理项,后一个为低代理项。

关于 Unicode 的代理项,可以参看:Unicode | 代理项(Surrogate) - 云+社区 - 腾讯云

本质原因:业务层提供的字符串有问题,需要调查为什么会出现非法的代理项编码。

业务方可能的问题原因:

对于需要使用代理项字符来说,就自然含义来看,它是一个字符,但编码上由高代理+低代理组合成一个代理对来表示。在 C# 的代码下,如 string surrogateContent = “\ud835\udc01”,此时的 surrogateContent.Length 为 2。

所以就可能出现,在某些场景下,将其分开成两个“字符”了,但这两个“字符”都是非法的,因为其编码是在代理项区域,必须成对出现,合起来表示一个字符。

解决:

1 修复业务,不再产生非法字符。

2 存储层对于非法字符过滤掉,之后再进行 XML 序列化或者保存(先正常处理,出现异常,再检查是否有非法字符,处理之后再重试)。这种方式会造成数据的丢失,需要根据实际业务场景来决定是否可以这样处理。

场景复现

这里使用的 C# 代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static string Test2(){    // 示例字符:高代理项 \ud835 低代理项 \udc01    char invalidChar = '\ud835';    string invalidStr = "\ud835";
    string surrogateContent = "\ud835"; // 代理项对无效,缺少低代理项字符。    string surrogateContent = "\udc01"; // 无效的高代理项字符(0x{0})。高代理项字符必须具有范围(0xD800 - 0xDBFF)内的值。    string surrogateContent = "\ud835\ud835"; // 代理项对 (0x{0}, 0x{1}) 无效。高代理项字符(0xD800 - 0xDBFF) 必须始终与低代理项字符(0xDC00 - 0xDFFF)成对。
    Console.WriteLine(Char.IsSurrogate(invalidChar));    Console.WriteLine(Char.IsSurrogatePair(invalidStr, 0));    Console.WriteLine(Char.IsHighSurrogate(invalidChar));    Console.WriteLine(Char.IsLowSurrogate(invalidChar));
    XElement element = new XElement("node")    {        Value = surrogateContent    };
    var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), element);
    using (var stringWriter = new StringWriter(CultureInfo.InvariantCulture))    {        var settings = new XmlWriterSettings        {            OmitXmlDeclaration = true,            Indent = true,            NewLineHandling = NewLineHandling.Entitize,        };        using (var writer = System.Xml.XmlWriter.Create(stringWriter, settings))        {            document.WriteTo(writer);        }        return stringWriter.ToString();    }}

因为代理项需要成对出现,前高后低,所以这里的错误一共有三种。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 Xml_InvalidSurrogatePairWithArgs  The surrogate pair (0x{0}, 0x{1}) is invalid. A high surrogate character (0xD800 - 0xDBFF) must always be paired with a low surrogate character (0xDC00 - 0xDFFF).  代理项对 (0x{0}, 0x{1}) 无效。高代理项字符(0xD800 - 0xDBFF) 必须始终与低代理项字符(0xDC00 - 0xDFFF)成对。
2 Xml_InvalidSurrogateMissingLowChar  The surrogate pair is invalid. Missing a low surrogate character.  代理项对无效,缺少低代理项字符。
3 Xml_InvalidSurrogateHighChar  Invalid high surrogate character (0x{0}). A high surrogate character must have a value from range (0xD800 - 0xDBFF).  无效的高代理项字符(0x{0})。高代理项字符必须具有范围(0xD800 - 0xDBFF)内的值。

相关的源码:

XmlEncodedRawTextWriter.cs

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static unsafe char* EncodeSurrogate( char* pSrc, char* pSrcEnd, char* pDst ) {    Debug.Assert( XmlCharType.IsSurrogate( *pSrc ) );
    int ch = *pSrc;    if ( ch <= XmlCharType.SurHighEnd ) {        if ( pSrc + 1 < pSrcEnd ) {            int lowChar = pSrc[1];            if ( lowChar >= XmlCharType.SurLowStart &&                (LocalAppContextSwitches.DontThrowOnInvalidSurrogatePairs || lowChar <= XmlCharType.SurLowEnd)) {
                pDst[0] = (char)ch;                pDst[1] = (char)lowChar;                pDst += 2;
                return pDst;            }            throw XmlConvert.CreateInvalidSurrogatePairException( (char)lowChar, (char)ch );        }        throw new ArgumentException( Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar ) );    }    throw XmlConvert.CreateInvalidHighSurrogateCharException( (char)ch );}

由于都是 ArgumentException,所以不是很好区分,不严谨的处理方式是,根据 Message 来判断。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>/// 是否字符代理项相关的异常/// </summary>/// <param name="ex"></param>/// <returns></returns>public static bool IsSurrogateException(ArgumentException ex){    if (ex.Message.Contains("代理项字符") ||        ex.Message.ToLower().Contains("surrogate"))    {        return true;    }    else    {        return false;    }}

相关内容

C# 的 char 类型是 UTF-16 编码的字符。

char type - C# reference | Microsoft Docs

C# Char 下有多个方法可以进行代理项相关的判断。

Char.IsSurrogate 方法 (System) | Microsoft Docs

.NET 中的字符编码

.NET 中的 character 编码简介 | Microsoft Docs

Unicode | 代理项(Surrogate)

Unicode | 代理项(Surrogate) - 云+社区 - 腾讯云

字符编码查询工具

汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode

原文链接: https://cloud.tencent.com/developer/article/2481545

本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年3月11日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
聊聊Java中codepoint和UTF-16相关的一些事
Unicode和UTF-8/UTF-16/UTF-32之间就是字符集和编码的关系。字符集的概念实际上包含两个方面,一个是字符的集合,一个是编码方案。字符集定义了它所包含的所有符号,狭义上的字符集并不包含编码方案,它仅仅是定义了属于这个字符集的所有符号。但通常来说,一个字符集并不仅仅定义字符集合,它还为每个符号定义一个二进制编码。当我们提到GB2312或者ASCII的时候,它隐式地指明了编码方案是GB2312或者ASCII,在这些情况下可以认为字符集与编码方案互等。
哲洛不闹
2018/09/14
1.3K0
聊聊Java中codepoint和UTF-16相关的一些事
刨根究底字符编码之十四——UTF-16究竟是怎么编码的
首先要注意的是,代理Surrogate是专属于UTF-16编码方式的一种机制,UTF-8和UTF-32是不用代理的。
笨笨阿林
2019/01/18
1K1
(28) 剖析包装类 (下) / 计算机程序的思维逻辑
本节探讨Character类,它的基本用法我们在包装类第一节已经介绍了,本节不再赘述。Character类除了封装了一个char外,还有什么可介绍的呢?它有很多静态方法,封装了Unicode字符级别的各种操作,是Java文本处理的基础,注意不是char级别,Unicode字符并不等同于char,本节详细介绍这些方法以及相关的Unicode知识。 在介绍这些方法之前,我们需要回顾一下字符在Java中的表示方法,我们在第六节、第七节、第八节介绍过编码、Unicode、char等知识,我们先简要回顾一下。 Uni
swiftma
2018/01/31
6820
一文解开java中字符串编码的小秘密
在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系,同时你还会了解变种UTF-8,并且探讨一下UTF-8和变种UTF-8在java中的应用。
程序那些事
2020/09/22
6410
漫谈计算机编码
我们知道,在计算机内部,所有的信息都是以二进制形式进行存储。无论是字符,或是视频音频文件,最终都会对应到一串由 0 和 1 构成的数字串。所以从我们能看懂的人类信息转变为机器级别的二进制语言的过程就可以理解为一种编码的过程,自然,相反的过程就是所谓的解码的过程。
Single
2018/03/14
1.1K0
漫谈计算机编码
快来看看 ECMAScript 2024 (ES15) 发布了什么新特性
2024 年 6 月 26 日,第 127 届 Ecma 大会批准了 ECMAScript 2024 语言规范,这意味着它现在正式成为标准。
前端蛋卷
2024/07/02
3850
快来看看 ECMAScript 2024 (ES15)  发布了什么新特性
[十]基础数据类型之Unicode编码简介
(American Standard Code for Information Interchange,美国信息交换标准代码)
noteless
2018/10/12
1K0
[十]基础数据类型之Unicode编码简介
今天一次把 Unicode 和 UTF-8 说清楚
在日常开发过程中,Unicode & UTF-8 并不是很受关注的知识,但在阅读源码或文章时,出现频率很高。如果你没有理解清楚 Unicode、UTF-8、UTF-16 和 UTF-32 之前的关系,会带来阅读障碍。在这篇文章里,我将带你理解 Unicode 字符集的原理,希望能帮上忙。
用户9995743
2022/09/26
1.2K0
今天一次把 Unicode 和 UTF-8 说清楚
Unicode | 代理项(Surrogate)
代理项(Surrogate),是一种仅在 UTF-16 中用来表示补充字符的方法。在 UTF-16 中,为补充字符分配两个 16 位的 Unicode 代码单元:
Blume
2020/06/10
1.5K0
Unicode | 代理项(Surrogate)
JavaScript 有个 Unicode 的天坑
最近笔者在项目中遇到了emoji表情的处理,期间发现js处理多字节字符时会有较多坑,记录一下与各位分享。
疯狂的技术宅
2019/03/28
1.1K0
【拓展】谈谈字符编码:Unicode编码与emoji表情编码
码位(码点),对应编码术语中英文中的code point,指的是一个编码标准中为某个字符设定的数值,具有唯一性与一一对应性。码位只规定了一个字符对应的数值,并没有规定这个数值如何存储,视编码方案不同有不同的存储方式。
pingan8787
2020/08/17
8.8K0
从JavaScript看字符编码的前世今生!
导语 | 每个程序员都应该了解一下字符编码,有了基础概念之后我们对编程语言、字符处理能有更深入的理解。本文我花了大量时间进行资料查阅和考证,希望能够给大家带来一些帮助,多多交流! 一、起因 最近在研究Babel的源码,在看到Acorn词法解析源码中有这样一段逻辑: pp.fullCharCodeAtPos = function() { let code = this.input.charCodeAt(this.pos) if (code <= 0xd7ff || code >= 0xdc00
腾讯云开发者
2022/05/18
8590
从JavaScript看字符编码的前世今生!
一个 Java 字符串到底有多少个字符?
依照Java的文档, Java中的字符内部是以UTF-16编码方式表示的,最小值是 \u0000 (0),最大值是\uffff(65535), 也就是一个字符以2个字节来表示,难道Java最多只能表示 65535 个字符?
Java技术栈
2019/09/08
1.3K0
JavaScript emoji utils
也就是说,Unicode支持的编码范围是U+0000到U+10FFFF,能对应100多万个符号(0x10FFFF === 1114111)。这些符号被分组归入16个平面(panel),所以每个平面放65536(16^4 === 65536)个
ayqy贾杰
2019/06/12
2.1K0
JavaScript emoji utils
【原创】经验分享:一个小小emoji尽然牵扯出来这么多东西?
商品评价列表页,显示每条用户的评价详情,为了保护用户隐私,要求显示用户昵称时只能显示第一位和最后一位,其他的用※代替。
一枝花算不算浪漫
2020/10/09
8810
【原创】经验分享:一个小小emoji尽然牵扯出来这么多东西?
MUTF-8(Modified UTF-8)
在Android应用程序的Dex文件中,所有的字符串都是使用一种叫做MUTF-8(Modified UTF-8)的编码格式进行编码的。
longzeqiu
2019/11/18
1.8K1
字符编码的那些事
之前看到ES6中对String扩展了不少新特性,字符串操作更加友好,比如"\u{1f914}",codePointAt(),String.fromCodePoint()。其中涉及到不少字符编码的知识,为了更好理解这些新特性,本文对字符编码相关知识做一个较全面的梳理和总结。
elson
2018/06/17
1.9K0
11.2 Java 字符串相关类使用
Java中 Character、String、StringBuilder 等类用于文本处理,它们的基础都是 char。
acc8226
2022/05/17
6860
Unicode 与 utf8 utf16 utf32的关系
Unicode是计算机领域的一项行业标准,它对世界上绝大部分的文字的进行整理和统一编码,Unicode的编码空间可以划分为17个平面(plane),每个平面包含2的16次方(65536)个码位。17个平面的码位可表示为从U+0000到U+10FFFF,共计1114112个码位,第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0)。其他平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFF
2018/05/28
2.2K0
【字符编码那些事】ASCII、GB2312、GBK、UTF-8编码以及Unicode字符集
我们在开发中是不是经常会遇到这样的问题,比如你在VS2019中创建了一个工程,里面有C语言程序和中文注释,有一天,根据工作需要,你要把其中的一部分C文件和H文件移植到Keil工程中,当你通过复制黏贴把相应文件移植到Keil工程中,并使用MDK打开时,却发现,你移植的文件C语言程序是正常显示的,但是中文却成了一堆乱码,并且一编译各种莫名其妙的报错。这其实就有可能是你的VS2019和Keil使用了不同的编码方式,因为大部分编码兼容ASCII编码,而C语言程序是英文字符,采用了ASCII编码,所以正常显示,而中文编码就不同了,比如内存中同样的0xB0A1,使用不同编码标准去对0xB0A1解码,得到的可能就是不同的汉字。
mindtechnist
2024/08/08
2.1K0
【字符编码那些事】ASCII、GB2312、GBK、UTF-8编码以及Unicode字符集
推荐阅读
相关推荐
聊聊Java中codepoint和UTF-16相关的一些事
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验