Unicode调试相关问题

本文用于指导在一些特殊情况下该如何做。也就是,您在某处(通常是数据库)有一些字符数据,这些数据需要经过几个步骤,最终显示给用户(通常在网页上)。不幸的是,有些字符会显示异常。由于涉及多个步骤,问题可能出现在多个地方。本文旨在帮助您简单、可靠地找出问题所在。

第一步:了解Unicode的基础知识

如果您对Unicode、字符编码等能熟练掌握,那么可以跳过这一步。一般来说,在进一步深入之前,您需要了解一些关于字符是什么,以及字符可以进行哪些转换的知识。更多信息请参见我关于此主题的文章(原文 译文)(以及它引用的文章)。

第二步:尝试识别可能涉及的转换

如果您能弄清楚问题可能出在哪里,就更容易分辨出具体是哪里出了问题。同时不仅要考虑到您如何检索数据的,还要考虑数据最初是如何存入的。(我见过的一些问题是由于旧应用程序以错误的方式向数据库写入和读取数据,导致错误相互抵消了。只有这个有问题的应用程序访问数据库时不会出现问题,但任何其他东西访问数据库时,就都会出错。)涉及到的步骤可能包括从数据库获取数据、从文件中读取数据、通过网络连接发送数据或在屏幕上显示数据。

第三步:验证每一步的数据

首先不要信任任何试图将字符数据作为一系列的字形(glyphs)进行记录并输出的东西。相反,您应该将字符数据记录为一系列的Unicode值(整数)。例如,如果我有一个包含单词 “hello” 的字符串,我会将其显示为 “0068 0065 006c 006c 006f”。(使用十六进制可以让您在稍后检查Unicode代码图表时更容易进行比对。)要做到这一点,请逐个检查字符串中的每个字符,并以显示整数的方式显示该字符。例如,这是一个将字符串中所有字符输出到控制台的方法:

1
2
3
4
5
6
7
8
static void DumpString (string value)
{
foreach (char c in value)
{
Console.Write("{0:x4} ", (int)c);
}
Console.WriteLine();
}

根据您的具体环境,输出方法会有所不同,但使用类似上述的方法应该能为您提供所需的信息。我的关于字符串的文章提供了更详细的调试形式。

这样做的目的是为了排除字体、其他编码问题等方面的原因。如果您连普通的ASCII十六进制数字都无法正确输出,那您就遇到大麻烦了——您可能无法以可靠的方式输出Unicode,而且您已经知道了问题出在了Unicode,所以必须采取一些安全措施。

现在您需要确保有一个测试案例可以使用。找到您的应用程序出错的某个(最好是个小型的)示例,确保您知道正确的结果应该是什么,然后在您可能遇到问题的每个点都输出实际结果。(有些可能不在您的控制范围内,但通常如果您在接收到某些数据后立即输出,并在发送某些数据之前输出,您就能找到问题所在了。)

输出了有问题的字符串后,您应该验证它是否是正确的形式。这就是Unicode代码图表的用处所在了。您可以选择您认为正确字符所在的部分,或者可以按字母顺序搜索您的字符。检查字符串中的每个字符的Unicode值是否正确。一旦您在应用程序流程中发现了字符数据被损坏的点,您应该检查该代码区域,找出字符被损坏的原因并修复它。当您在整个应用程序流程中都检查无误后,应用程序应该就能正常工作了。

结论

像软件工程中的许多问题一样,解决文本问题通常会使用“分而治之”的方法。当您对每个步骤都有信心时,您应该就会对整体充满信心。如果您在解决问题时遇到特别棘手的情况,我强烈建议您编写包含这些情况的单元测试——既可以作为可能发生的事情的文档,也可以作为防止未来回归的预防措施。

本文是对Debugging Unicode Problems这篇文章的翻译,作者是著有C# in Depth的大神Jon Skeet。非文章原文或本人对某段文字理解,会以斜体 个人理解:xxx 进行标注。本人翻译能力有限,强烈建议大家去看原版!