【发布时间】:2011-07-21 11:48:51
【问题描述】:
我不清楚 C 的 argv 中使用了哪些编码。特别是,我对以下场景感兴趣:
- 用户使用区域设置 L1 创建一个文件,其名称
N包含非 ASCII 字符 - 稍后,用户使用语言环境 L2 在命令行上使用制表符完成该文件的名称,该文件作为命令行参数输入到程序 P 中
P 在命令行中看到的字节序列是什么?
我观察到在 Linux 上,在 UTF-8 语言环境中创建一个文件名,然后在(例如)zw_TW.big5 语言环境中使用制表符完成它似乎导致我的程序 P 被输入 UTF-8 而不是 @987654325 @。然而,在 OS X 上,同样的一系列操作会导致我的程序 P 得到一个Big5 编码的文件名。
这是我认为到目前为止的情况(很长,我可能错了,需要纠正):
窗口
文件名以某种 Unicode 格式存储在磁盘上。因此 Windows 采用名称 N,从 L1(当前代码页)转换为 N 的 Unicode 版本,我们将调用 N1,并将 N1 存储在磁盘上。
然后我假设发生的情况是,稍后在制表符完成时,名称N1 将转换为区域设置 L2(新的当前代码页)以进行显示。幸运的是,这将产生原始名称N——但如果N 包含在L2 中无法表示的字符,这将不是真的。我们将新名称称为N2。
当用户实际按下回车键以使用该参数运行 P 时,名称 N2 被转换回 Unicode,再次产生 N1。这个N1 现在可以通过GetCommandLineW/wmain/tmain 以UCS2 格式供程序使用,但GetCommandLine/main 的用户将在当前语言环境(代码页)中看到名称N2 )。
操作系统
据我所知,磁盘存储的故事是一样的。 OS X 将文件名存储为 Unicode。
使用 Unicode 终端,我认为终端会在 Unicode 缓冲区中构建命令行。因此,当您完成选项卡时,它会将文件名作为 Unicode 文件名复制到该缓冲区。
当您运行该命令时,该 Unicode 缓冲区将转换为当前语言环境 L2,并通过 argv 提供给程序,程序可以将具有当前语言环境的 argv 解码为 Unicode 以进行显示。
Linux
在 Linux 上,一切都不同了,我对正在发生的事情感到非常困惑。 Linux 将文件名存储为 字节字符串,而不是 Unicode。因此,如果您在语言环境 L1 中创建一个名为 N 的文件,那么 N 作为字节字符串存储在磁盘上。
当我稍后运行终端并尝试使用制表符完成名称时,我不确定会发生什么。在我看来,命令行被构造为一个字节缓冲区,文件的名称作为字节字符串只是连接到该缓冲区。我假设当您键入标准字符时,它会即时编码为附加到该缓冲区的字节。
当你运行一个程序时,我认为缓冲区是直接发送到argv的。现在,argv 有什么编码?看起来您在语言环境 L2 中在命令行中键入的任何字符都将采用 L2 编码,但文件名将采用 L1 编码。所以argv 包含两种编码的混合!
问题
如果有人能告诉我这里发生了什么,我真的很高兴。我现在只有半信半疑和猜测,它们并不能真正融合在一起。我真正想要的是 argv 在当前代码页 (Windows) 或当前语言环境 (Linux / OS X) 中进行编码,但情况似乎并非如此......
附加功能
这是一个简单的候选程序 P,让您可以自己观察编码:
#include <stdio.h>
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Not enough arguments\n");
return 1;
}
int len = 0;
for (char *c = argv[1]; *c; c++, len++) {
printf("%d ", (int)(*c));
}
printf("\nLength: %d\n", len);
return 0;
}
您可以使用locale -a 查看可用的语言环境,并使用export LC_ALL=my_encoding 更改您的语言环境。
【问题讨论】:
-
在带有 Visual C 标准库的 Windows 中,您可以实现 wmain() 并获得 WCHAR * wz_argv[]。如果您选择在 Windows 上实现自己的 C 标准库,您将从
WinMain开始,它采用整个原始命令行的 Unicode 字符串。在 Windows 上,您肯定正在运行 cmd.exe,并且所有管道都是 Unicode。至于 Unix 衍生产品,相对于您的问题,最关键的决策代码是 shell。例如,如果您正在运行 bash,那么该版本 bash 的行为就是您要询问的内容。 -
您只能在 Windows 上的命令行应用程序中使用 wchar argv。在 gui 构建中,您必须单独使用 GetCommandLine 来获取宽字符
-
@Martin - 我没有意识到 WinMain 也是从 MSVC 库中调用的,也不是 ANSI。原来,可以选择使用 wWinMain 来获取 Unicode。
-
见stackoverflow.com/questions/4101864/…其中一些是Qt特有的,但答案中有很多有用的信息
-
感谢您迄今为止的回复。似乎很明显,使用 Windows 和 wmain(我在回答中提到过)你会得到 UCS-2 编码的 Unicode 命令行参数。我想使用带有 char** argv 的 Windows 主程序,您会在当前代码页中获得编码的参数,但这是我不清楚的事情之一。