我有一台新的工作电脑(它有更高版本的 Windows 10,与 O365 的最高版本相同),配置与我之前的电脑几乎相同(在区域和语言方面完全一样),并且我遇到了 Excel VBA 特殊字符显示的一个奇怪问题。我为我们的法国用户准备了多个宏,它们使用该Chr()
函数对法语特殊字符进行编码。但是在新电脑上,这些字符在所有用户窗体(包括消息框)中显示为�。当我debug.print
让函数返回时,它会产生一些其他无意义的结果,例如“Mo”、“Tr”或再次�。即使我告诉宏打开 Word 文档并在其中输入结果,它通常也是�。
这个问题只限于我的新电脑和功能Chr()
- 如果我使用该ChrW()
功能,结果显示正确。我可以用于ChrW()
未来,但我不太愿意重做所有现有的宏...
例子:
Sub TEST()
MsgBox "Sélectionnez" & vbCrLf & "S" & Chr(233) & "lectionnez" & vbCrLf & "S" & ChrW(233) & "lectionnez" & vbCrLf
End Sub
编辑:
根据建议,我通过函数运行了字符串中的可疑位置,AscW()
结果返回了-3
。我不太熟悉十六进制表示法,但在解释这个结果时,我明白负数可能由与某些正数相同的编码来编码,因此-3
对应于FFFD
,反过来也可以翻译为65533
。进一步的研究表明U+65533
是一个“替换字符”或 �,Google 返回给我的问题的答案通常是“编码问题”。
我还运行了一个快速循环,将 1 到 65533 映射到工作表中的结果。ChrW()
中断发生在 128 处,从那里和返回 � 直到 255 ASCII 限制。但值得注意的是,从 128 到 160(到)不返回任何字符,我发现这很奇怪。也是一个大胆的猜测:128 刚好大于 255 的一半,但这可能只是巧合,因为返回,而不是 �,而且我不明白这会如何影响结果。此外,我运行了这三个函数的结果,有趣的是,从 160 开始,它开始返回 0 (对于其他函数,可以预见,它从它们的 � 返回开始,所以已经在 128 处)。Chr()
Char()
Chr()
Char()
ChrW()
€
nonbreakable space
Char(256)
#VALUE!
ChrW()
Code()
ChrW()
答案1
答案2
我认为 Ron 的回答关于实际需要的 Windows 配置更改是正确的,但并没有真正谈论为什么它有这种效果。
Chr()
根据系统当前选择的“ANSI 代码页”转换字节值,通常是根据地区而定的 Windows-125x 代码页之一(例如,西方为 cp1252,波罗的海为 cp1257)。
ChrW()
而是采用 Unicode BMP 代码点,它们是 0..65535 范围内的双字节值,并且无论在哪个地区都有相同的映射。
这两个函数恰好只在 0..127 范围内匹配,因为 Windows-125x 和 Unicode 都基于相同的 7 位 ASCII,并以完全相同的方式定义此范围。但超出该范围,它们确实是两个不同的代码页。
Chr() 使用的 Windows-125x 代码页是单字节代码页,涵盖值 0..255(减去“控制”范围 0..31)。这就是Chr(256)
返回 #VALUE! 的原因——它对于单字节代码页来说毫无意义,而ChrW(256)
在 Unicode 中只是普通的 U+0100 Ā
。
那么旧的 Chr() 停止工作的真正原因是什么?
如上所述,Chr() 使用特定于区域的代码页,Windows 将其称为“ANSI 代码页”。ANSI 代码页被视为一项遗留功能,保留下来是为了与最初为 Windows 95/98(不是基于 Unicode 的)编写的程序兼容。
(许多 Windows API 实际上有两组几乎相同的函数,一组用于“ANSI”,一组用于“Unicode”,例如 MessageBoxA() 接受旧式 ANSI 代码页中的文本,而 MessageBoxW() 接受 Unicode 中的文本。同样,CreateFileA() 与 CreateFileW() 也是如此,等等。)
Windows 10 中最大的变化(即Ron Rosenfeld 在另一个答案中表示)是“ANSI”功能不再使用 Windows-125x – 他们使用 UTF-8。UTF-8 代码页是使用 Unicode 代码点的另一种方式,它不是单字节的,而是可变宽度的,一个字符的长度从 1 个字节到 4 个字节不等。
UTF-8 的前 128 个字节(0..127)仍刻意与 ASCII 匹配,因此您仍会获得与 Windows-125x 相同的结果。但是,另一半(字节 128..255)具有完全不同的含义 - 它们用于多字节序列,单独使用时没有任何意义。
例如,é
在 UTF-8 中是两个字节,195 后跟 169。如果你使用Chr(195) + Chr(169)
,你应该会得到一个"é"
反义词。但是当它们单独使用时,那就是一个不完整序列;UTF-8 解码器会给你一个,"�"
因为单个字节本身没有意义。
微软之所以增加这个新功能,是因为“ANSI”功能对于旧软件的兼容性越来越差,而UTF-8可以非常对于从其他操作系统(例如 Linux)移植的现代软件很有用,因为这些程序倾向于使用 UTF-8 作为唯一的代码页。
然而,值得注意的是,ChrW() 不会返回 128 到 160 之间的任何字符(€ 到不可分割的空格),我觉得这很奇怪
这是正常的——如上所述,ChrW() 使用 Unicode 映射,而 Unicode 在此范围内没有字符。
Windows-1252(西方)基于 ISO 8859-1 代码页,但有一个区别:原始 ISO 8859 将值 128..159(0x80..0x9F)保留一秒钟控制字符范围,其中没有可见字符。但是,Microsoft 确实会在此范围内放置额外的图形字符,这就是您在调用Chr(128)
等操作时所得到的。
前两个 Unicode 块总共覆盖 0..255(或者说 U+0000..U+00FF),还基于 ISO 8859-1 – 因此代码点 128..159 又名 U+0080..U+009F (属于拉丁语-1 补充块) 包含相同的不可见控制字符。
该问题仅限于我的新 PC 和 Chr() 函数 - 如果我使用 ChrW() 函数,结果显示正确。我以后可以使用 ChrW(),但我不太愿意重做所有现有的宏...
你应该真的使用 ChrW()。
由于它采用 Unicode 代码点,因此无论您的 Windows 区域设置如何,它都会始终返回相同的结果。这意味着您的宏将继续适用于那些出于某种原因需要将其操作系统设置为西里尔语、波罗的语、希腊语或土耳其语的人。
(旧程序中嵌入了关于区域设置的假设,导致我和我的同事很多头疼,因此我对此有点直言不讳。我坚信这种胡言乱语不应该持续到 2020 年代——毕竟,Windows 本身已经基于 Unicode 超过二十年了。
由于代码页不匹配,官方文档中有人的名字被弄乱了。我们不得不告诉人们将他们的区域设置更改为完全不符合他们语言的设置,只是为了让遗留程序正常工作。我们有一个程序需要 YYYY/MM/DD 日期,还有一个程序需要 YYYY-MM-DD 日期,我们需要找到一种方法来运行两个都在同一台计算机上。与不愿意改进的程序员打交道一点也不好玩。