我不清楚C's argv
中使用了什么编码.特别是,我对以下场景感兴趣:
- 用户使用区域设置L1创建一个文件,其名称
N
包含非ASCII字符 - 稍后,用户使用locale L2在命令行上完成该文件的名称,该文件作为命令行参数输入到程序P中
P在命令行上看到的字节序列是什么?
我观察到,在Linux上,在UTF-8语言环境中创建一个文件名,然后在(例如)zw_TW.big5
语言环境中完成它,似乎会导致我的程序P被输入UTF-8而不是Big5
.然而,在OSX上,同样的一系列操作会导致我的程序P获得Big5
编码的文件名.
以下是我目前的 idea (很长,我可能错了,需要纠正):
Windows
文件名以某种Unicode格式存储在磁盘上.所以Windows使用名称N
,将L1(当前代码页)转换为Unicode版本的N
,我们称之为N1
,并将N1
存储在磁盘上.
然后,当tab稍后完成时,名称N1
被转换为locale L2(新的当前代码页)以供显示.幸运的是,这将产生原来的名字N
——但如果N
包含在L2中不可表示的字符,这将不是真的.我们把这个新名字叫做N2
.
当用户实际按enter键运行带有该参数的P时,名称N2
将转换回Unicode,再次生成N1
.这个N1
现在可以通过GetCommandLineW
/wmain
/tmain
以UCS2格式提供给程序,但GetCommandLine
/main
的用户将在当前语言环境中看到名称N2
(代码页).
OS X
据我所知,磁盘存储的故事是一样的.OSX将文件名存储为Unicode.
对于Unicode终端,I think发生的事情是,终端在Unicode缓冲区中构建命令行.因此,当您使用tab complete时,它会将文件名作为Unicode文件名复制到该缓冲区.
运行该命令时,Unicode缓冲区将转换为当前语言环境L2,并通过argv
馈送到程序,程序可以将当前语言环境下的argv解码为Unicode进行显示.
Linux
在Linux上,一切都不一样,我对正在发生的事情感到非常困惑.Linux将文件名存储为byte strings,而不是Unicode.所以,如果在语言环境L1中创建一个名为N
的文件,那么N
作为字节字符串存储在磁盘上.
当我稍后运行终端并try 完成名称时,我不确定会发生什么.在我看来,命令行被构造为字节缓冲区,而文件as a byte string的名称只是连接到该缓冲区上.我假设,当您键入一个标准字符时,它会被动态编码为附加到该缓冲区的字节.
当你运行一个程序时,我认为缓冲区会直接发送到argv
.现在,argv
有什么编码?看起来,在语言环境L2中,您在命令行中键入的任何字符都将使用L2编码,但the file name will be in the L1 encoding.所以argv
包含两种编码的混合!
Question
如果有人能告诉我这里发生了什么,我真的很高兴.目前我所能做的只是猜测和猜测,而这两者并不完全吻合.我真正希望的是,在当前代码页(Windows)或当前语言环境(Linux/OS X)中对argv
进行编码,但情况似乎并非如此...
Extras
下面是一个简单的候选程序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
更改区域设置.