【问题标题】:Why does the same character render different ways in Windows applications?为什么相同的字符在 Windows 应用程序中呈现不同的方式?
【发布时间】:2019-08-04 23:12:46
【问题描述】:

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

如果我将其粘贴到自己的单元格中并使用 Excel CODE 函数,则 ascii 值将返回为 63。如果我使用键盘在另一个单元格中键入问号并使用 CODE 函数,这也返回 63 的 ascii 值-但问号呈现“正常”-?。

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

如果我将这两个字符(菱形问号;普通问号)复制/粘贴到 SQL Server Management Studio 查询窗口中,它们会继续以不同的方式呈现,但 ASCII T-SQL 函数显示它们都具有值63.

如果我将这两个字符复制/粘贴到 EditPlus 文本编辑器版本 2 中,菱形问号现在会呈现为空格(除了空格之外什么都不可见)。

问题

这里发生了什么?如果不是它们的字符代码,这两个字符实际上有什么不同?

是每个字符使用的字符集吗?如何确定每个字符使用的字符集?当我尝试阅读 SQL Server 中的字符集时,它们似乎适用于每个 db/schema/table - 那么 SSMS 为了单独处理这两个字符做了什么?各个字符中必须有一些固有的东西来区分它们——而不是表级别的设置。 (无论如何,SSMS 在查询窗口中默认使用什么字符编码?即使 SSMS 为其查询窗口设置了默认字符集,显然这两个字符的呈现方式也不同)。同样,当我阅读有关 Excel 字符编码的信息时,它似乎是按文档/文件定义的 - 那么 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 Server 认为它们是等效字符,但即使你不能,第一行也应该显示给你它们在您的网络浏览器中的呈现方式有何不同。

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

【问题讨论】:

  • 在 Excel 中,字符集是 Unicode。使用不尊重这一点的功能只会造成混乱和数据丢失。使用 UNICODE 而不是代码。在文本编辑器中,您告诉它使用哪种字符编码。
  • � 向我们的用户表明由于错误应用字符编码而丢失了他们的一些数据。你应该去上游解决这个问题。
  • 谢谢。有趣的是,在 StackExchange 应用程序中,它呈现为六边形。
  • 在 SSMS 中工作,我有一个很长的脚本,其中有一个菱形问号 - 这个结果帮助我找到它,所以我可以删除并跳过询问我是否要保存为的对话框由于该字符不在代码页中,因此文件格式不同 - 希望我保留该错误消息,以便可以在这篇文章中找到它。

标签: sql-server excel character-encoding character special-characters


【解决方案1】:

字符“�”和“?”是具有不同代码点的不同字符。要使用 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 Server 根据默认数据库排序代码页将该字符映射到 128-255 点范围内的代码点。当代码页中不存在该字符的映射时,将为该字符分配一个备用字符(例如,从代码页 1252 转换为 850 时,“Ÿ”变为“Y”)或“?”在这种情况下不存在后备字符时。

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

【讨论】:

  • 好的 - 我现在明白了。缺少的步骤是粘贴的字符不在 ASCII 范围内,当我将其分配给非 unicode 数据类型时,SQL Server 让它回退到 ASCII 值。当正确键入强制转换并使用 UNICODE 函数时,可以看到字符之间的差异。谢谢。
猜你喜欢
  • 2011-04-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 2013-03-08
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 1970-01-01
相关资源
最近更新 更多