为什么同一个角色在 Windows 个应用程序中呈现不同的方式?

Why does the same character render different ways in Windows applications?

我在 Excel 工作簿中遇到了一个 ascii 值为 63 的字符。它呈现为包含问号的实心菱形(例如,带有白色问号 - � 的黑色菱形)。

如果我将其粘贴到它自己的单元格中并使用 Excel CODE 函数,则返回的 ascii 值是 63。如果我在另一个单元格中键入一个问号,使用键盘,并使用 CODE 函数,该函数也 returns ascii 值 63 - 但问号呈现 "normally" - ?。

如果我检查这两个单元格上的字体,它们使用的是相同的字体 (Calibri)。

如果我 copy/paste 将两个字符(菱形问号;普通问号)放入 SQL Server Management Studio 查询 window,它们将继续以不同方式呈现,但 ASCII T-SQL 函数显示它们都有值 63.

如果我 copy/paste 这两个字符进入 EditPlus 文本编辑器版本 2,菱形问号现在呈现为白色space(除了 space 什么都看不见)。

问题

这是怎么回事?如果不是他们的字符代码,这两个字符实际上有什么不同?

是每个字符使用的字符集吗?如何确定每个字符使用的字符集?当我尝试阅读 SQL 服务器中的字符集时,它们似乎适用于 db/schema/table - 那么 SSMS 正在做什么以单独处理这两个字符?每个字符必须有一些固有的东西来区分它们 - 而不是 table 级设置。 (无论如何,SSMS 在查询 window 中默认使用什么字符编码?即使 SSMS 为其查询设置了默认字符 window,显然这两个字符呈现不同)。同样,当我读到 Excel 字符编码时,它似乎是根据 document/file 定义的 - 那么 Excel 如何以不同方式呈现相同的 ascii 字符?

这类似于 Stack Overflow 上的 a question,有人想让 SSMS 找到并替换这个神秘字符,所以我从那个问题中知道,Stack Overflow 也以不同方式呈现这两个字符 - 即 1。还有另一个应用程序在字符级别以不同方式呈现它们(Web 浏览器,在我的例子中是 Firefox),以及 2. 您应该能够在下面的 SQL 代码中看到字符:

declare @mysteryChar char(1) = '�', @knownChar char(1) = '?';
select 'mystery character', @mysteryChar union all
select 'known character', @knownChar union all
select 'mystery ascii value', cast(ascii(@mysteryChar) as varchar(16)) union all
select 'known ascii value', cast(ascii(@knownChar) as varchar(16)) union all
select 'matches knownchar', case when @mysteryChar = @knownChar then 'true' else 'false' end

如果你可以 运行 T-SQL 那么太好了,代码将演示 ascii 值是相同的,最后一行显示 SQL 服务器认为它们是等价的字符,但即使你不能,第一行也应该向你展示它们在你的网络浏览器中的不同呈现方式。

PS。我找不到任何方法来输出这个菱形字符,例如,使用 T-SQL 中的 CHAR 函数。如前所述,我在 Excel 工作簿中找到了它,并且只能将其 copy/paste 到其他应用程序中以尝试找出发生了什么。

字符“�”和“?”是具有不同代码点的不同字符。要使用 T-SQL 查询查看字形和代码点:

SELECT 
      N'�' AS DiamondQuestionMark
    , UNICODE(N'�') AS DiamondQuestionMarkUnicodeCodePoint
    , N'?' QuestionMark
    , UNICODE(N'?') AS QuestionMarkUnicodeCodePoint;

+---------------------+-------------------------------------+--------------+------------------------------+
| DiamondQuestionMark | DiamondQuestionMarkUnicodeCodePoint | QuestionMark | QuestionMarkUnicodeCodePoint |
+---------------------+-------------------------------------+--------------+------------------------------+
| �                   |                               65533 | ?            |                           63 |
+---------------------+-------------------------------------+--------------+------------------------------+

ASCII 仅定义 0-127 代码点范围内的字符。使用非 ASCII 字符的非 Unicode 文字规范,如 '�'、SQL 服务器根据默认数据库整理代码页将字符映射到 128-255 点范围内的代码点。当代码页中不存在该字符的映射时,该字符要么被分配一个回退字符(例如,当从代码页 1252 转换为 850 时,“Ÿ”变为 "Y")或“?”当在这种情况下不存在回退字符时。

因此,因为“�”不是 ASCII 字符并且不存在于默认数据库代码页中,所以单字节文字 '�' 变为 '?'。使用 Unicode 文字 N'�' 和 Unicode 数据类型 ncharnvarchar 可以避免这种代码页的混乱。