我不清楚在C的何处使用什么编码argv。我特别对以下情况感兴趣:
argv
N
P在命令行上看到什么字节序列?
我观察到在Linux上,在UTF-8语言环境中创建文件名,然后在(例如)zw_TW.big5语言环境中用Tab键完成该文件名似乎会使我的程序P喂UTF-8而不是UTF-8 Big5。但是,在OS X上,相同的一系列操作导致我的程序P获得Big5编码的文件名。
zw_TW.big5
Big5
这是到目前为止我一直在想的事情(很长,我可能错了,需要纠正):
文件名以某种Unicode格式存储在磁盘上。因此Windows以这个名称命名N,从L1(当前代码页)转换为N我们将称为的Unicode版本N1,并存储N1在磁盘上。
N1
然后,我 假设 发生的事情是,稍后稍后使用制表符补全时,该名称N1将转换为语言环境L2(新的当前代码页)以进行显示。运气好的话,这将产生原始名称N-但是,如果其中N包含L2中无法表示的字符,则该名称将不成立。我们叫新名字N2。
N2
当用户实际按下Enter键以使用该参数运行P时,该名称N2将转换回Unicode,N1再次产生。这N1是通过在UCS2格式现在可供程序GetCommandLineW/ wmain/ tmain,但用户GetCommandLine/ main会看到的名称N2在当前区域设置(代码页)。
GetCommandLineW
wmain
tmain
GetCommandLine
main
据我所知,磁盘存储的故事是相同的。OS X将文件名存储为Unicode。
对于Unicode终端,我 认为 发生的事情是该终端在Unicode缓冲区中构建了命令行。因此,完成制表符后,它会将文件名作为Unicode文件名复制到该缓冲区。
当您运行命令时,该Unicode缓冲区将转换为当前语言环境L2,并通过馈入程序argv,并且程序可以将具有当前语言环境的argv解码为Unicode以显示。
在Linux上,一切都不同,我对正在发生的事情感到困惑。Linux将文件名存储为 字节字符串 ,而不是Unicode。因此,如果您N在区域设置L1中创建名称为文件的文件,则该文件N作为字节字符串存储在磁盘上。
当我稍后运行终端并尝试用Tab键完成名称时,我不确定会发生什么。在我看来,命令行被构造为字节缓冲区,而文件名 作为字节字符串 被串联到该缓冲区上。我假设当您键入标准字符时,它会即时编码为附加到该缓冲区的字节。
当您运行程序时,我认为缓冲区直接发送到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用来更改您的语言环境。
locale -a
export LC_ALL=my_encoding
感谢大家的回应。我已经了解了很多有关此问题的知识,并发现了以下解决了我问题的方法:
如前所述,在Windows上,argv使用当前代码页进行编码。但是,您可以使用GetCommandLineW将命令行检索为UTF-16。不建议在支持unicode的现代Windows应用程序中使用argv,因为不建议使用代码页。
在Unixes上,argv没有固定的编码:
a)通过制表符补全/通配符插入的文件名将在argv 逐字 出现,恰好是它们在磁盘上被命名的字节序列。即使这些字节序列在当前语言环境中没有意义,也是这样。
b)用户使用其IME直接输入的输入将以区域设置编码在argv中出现。(Ubuntu似乎使用LOCALE来决定如何对IME输入进行编码,而OS X使用Terminal.app编码首选项。)
对于想要将命令行参数视为字符串的语言,例如Python,Haskell或Java,这很烦人。他们需要决定如何解码argv为内部使用的任何编码String(对于这些语言,编码为UTF-16)。但是,如果他们只是使用语言环境编码来执行此解码,则输入中的有效文件名可能无法解码,从而导致异常。
String
Python 3采用的解决此问题的方法是代理字节编码方案(http://www.python.org/dev/peps/pep-0383/),该方案将argv中任何不可解码的字节表示为特殊的Unicode代码点。当该代码点被解码回字节流时,它再次变成原始字节。这允许通过本地Python字符串类型将来自argv的在当前编码中无效的数据(即,以当前语言环境以外的名称命名的文件名)通过本机Python字符串类型往返,并返回到字节,而不会丢失信息。
如您所见,情况非常混乱:-)