【问题标题】:Clarification on Winapi Paths and Filename (W functions and A functions)关于 Winapi 路径和文件名的说明(W 函数和 A 函数)
【发布时间】:2018-08-28 06:23:28
【问题描述】:

我尝试检查使用 W winapi vs A 的重要性和原因,(W 表示宽字符,A 表示 ascii 对吗?)

我做了一个简单的例子,我收到了当前用户的临时路径,如下所示:

CHAR pszUserTempPathA[MAX_PATH] = { 0 };
WCHAR pwszUserTempPathW[MAX_PATH] = { 0 };

GetTempPathA(MAX_PATH - 1, pszUserTempPathA);

GetTempPathW(MAX_PATH - 1, pwszUserTempPathW);

printf("pathA=%s\r\npathW=%ws\r\n",pszUserTempPathA,pwszUserTempPathW);

我当前的用户有一个俄语名字,所以它是用西里尔字母写的,printf 输出如下:

pathA=C:\users\Пыщь\Local\Temp
pathW=C:\users\Пыщь\Local\Temp

所以两条路径都很好,我想我会收到一些错误,或者因为当前用户是 unicode,所以会收到一些带有 GetTempPathA 的符号,但我发现,西里尔字符实际上包含在扩展的 ascii 字符中放。所以我有一个问题,如果我要使用我的软件,它会在当前用户的临时文件夹中提取数据,他是中国人(假设他的用户名中有中文符号),我会使用GetTempPathA 版本?对于直接使用 winapi 的生产软件,我是否应该始终使用 W 前缀函数?

【问题讨论】:

    标签: c windows winapi unicode ascii


    【解决方案1】:

    首先,-A 后缀代表 ANSI,而不是 ASCII。 ASCII 是一个 7 位字符集。 ANSI,正如 Microsoft 使用的术语,用于使用 8 位代码单元 (chars) 和代码页进行编码。

    有些人使用术语“扩展 ASCII”或“高级 ASCII”,但这实际上不是标准,在某些情况下,与 ANSI 并不完全相同。扩展 ASCII 是 ASCII 字符集加上(最多)128 个附加字符。对于许多 ANSI 代码页,这与扩展 ASCII 相同,但某些代码页可容纳可变长度字符(Microsoft 将其称为多字节)。有些人认为“扩展 ASCII”仅表示 ISO-Latin-1(与 Windows-1252 几乎相同)。


    无论如何,使用 ANSI 函数,您的字符串可以包含当前代码页中的任何字符。 如果您需要的字符不是当前代码页的一部分,那么您就不走运了。您必须使用宽 -W 版本。

    在现代版本的 Windows 中,您通常可以将 -A 函数视为 -W 函数的包装器,这些函数使用 MultiByteToWideChar 和/或 WideCharToMultiByte 来转换通过 API 传递的任何字符串。但是后一种转换可能是有损的,因为宽字符串可能包含您的多字节字符串无法表示的字符。


    可移植的跨平台代码通常存储 all text in UTF-8,它使用 8 位代码单元 (chars),但可以表示任何 Unicode 代码点,并且任何时候文本需要通过 Windows API,您都可以显式转换为宽字符/从宽字符转换,然后调用 API 的 -W 版本。

    UTF-8 与 Microsoft 所称的多字节 ANSI 代码页几乎相似,只是 Windows 不完全支持 UTF-8 代码页。有CP_UTF8,但它仅适用于某些 API(如 WideCharToMultiByte 和 MultiByteToWideChar)。您不能将代码页设置为 CP_UTF8 并期望通用 -A API 做正确的事情。


    当您尝试测试时,请注意很难(有时甚至不可能)让 CMD 控制台窗口显示当前代码页之外的字符。如果您想显示多脚本字符串,您可能应该编写一个 GUI 应用程序和/或使用调试器来检查字符串的实际内容。

    【讨论】:

    • 很好,除了关于控制台的最后一部分。 CMD 是一个恰好使用控制台的 shell,它是一个完全独立的东西,就像 xterm 和 bash 不一样。 CMD 使用控制台的宽字符 API 以及所有宽字符 WinAPI 函数(例如 ReadConsoleWWriteConsoleWCreateProcessW)。它仅将控制台的输出代码页用作 POSIX LANG 之类的东西,用于解码批处理文件的内容,从 for /f 循环中使用的管道读取,并将其内部命令(例如 dir)重定向到文件或管道(除非使用cmd /u /c)。
    • 控制台本身将屏幕缓冲区存储为宽字符,但每个“单元格”只有一个字符,因此它无法正确显示基本多语言平面之外的 UTF-16 代理对。但是,此类文本仍然可以从控制台复制并粘贴到其他地方。也就是说,即使对于 BMP 文本,在控制台中正确显示非 OEM 文本也需要您选择 TrueType 字体,例如 Consolas。在 Windows 8 之前,它默认为 OEM 光栅字体。它也不支持复杂的脚本和备用字体(例如启用 DirectWrite 的窗口),所以它还有很多不足之处。
    • 我们通常会遇到使用 C 运行时默认 ANSI C 语言环境的遗留应用程序的问题。这最终调用了ReadFileWriteFile,最终确实使用了遗留控制台代码页(即由GetConsoleCPGetConsoleOutputCP 返回)。有些人认为切换到代码页 65001 (UTF-8) 可以解决问题,但这只是因为他们没有对受支持的 Windows (7+) 版本和 C 运行时进行广泛的测试,以了解它有多么错误。唯一好的解决方案是使用控制台的宽字符 API,必要时显式转码为 UTF-8。
    • 我认为这与我所写的内容没有任何矛盾。 OP 使用 printf 归结为发送到控制台的 ANSI 样式命令,因此控制台本身可以处理宽字符在很大程度上无关紧要。我所知道的 Windows 上没有可用于控制台的固定间距 TrueType 字体同时涵盖西里尔文和 CJK,这对于处理 OP 中提出的情况是必要的。我只是指出,当您试图弄清楚 Windows API 如何处理 Unicode 时,使用基于控制台的实验确实会混淆问题。
    • 首先我想澄清一下,您所说的与CMD无关,CMD本身使用控制台的宽字符API。控制台应用程序继承或分配控制台(而 GUI 应用程序必须手动分配或附加到控制台)。 CMD 本身使用 Unicode API。程序可以在 UTF-16 文本文件中写出它的结果,然后在 CMD 中正确地type 它。即使使用了字体的默认字符(例如空框),文本也至少正确地位于控制台屏幕缓冲区中。您可以将其复制并粘贴到另一个可以正确显示的窗口中。
    【解决方案2】:

    当然,您需要宽版本。 ASCII 版本在技术上甚至不能处理超过 256 个不同的字符。西里尔字母包含在扩展的 ASCII 集中(如果这是您的本地化),而中文则没有,也不能,因为需要更大的字符集来表示它。此外,您也可能会弄乱西里尔字母 - 只有在执行机器具有匹配的本地化时它才能正常工作。因此,在具有非西里尔本地化的机器上,文本将根据本地化设置定义的任何内容显示。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      • 2016-04-15
      • 1970-01-01
      相关资源
      最近更新 更多