【问题标题】:Is it possible to use a Unicode "argv"?是否可以使用 Unicode“argv”?
【发布时间】:2010-12-12 11:27:15
【问题描述】:

我正在为一个使用文件作为参数的应用程序编写一个小包装器。

包装器需要使用 Unicode,所以我使用 wchar_t 来处理我拥有的字符和字符串。现在我发现自己遇到了一个问题,我需要将程序的参数放在 wchar_t 数组和 wchar_t 字符串中。

有可能吗?我将main 函数定义为

int main(int argc, char *argv[])

我应该为argv 使用 wchar_t 吗?

非常感谢,我似乎没有找到有关如何在 C 中正确使用 Unicode 的有用信息。

【问题讨论】:

    标签: c unicode command-line-arguments


    【解决方案1】:

    一般来说,不会。这将取决于操作系统,但 C 标准规定 'main()' 的参数必须是 'main(int argc, char **argv)' 或等价物,所以除非 char 和 wchar_t 是相同的基本类型,你做不到。

    话虽如此,您可以将 UTF-8 参数字符串放入程序中,将它们转换为 UTF-16 或 UTF-32,然后继续生活。

    在 Mac(10.5.8,Leopard)上,我得到:

    Osiris JL: echo "ï€" | odx
    0x0000: C3 AF E2 82 AC 0A                                 ......
    0x0006:
    Osiris JL: 
    

    这都是 UTF-8 编码的。 (odx 是一个十六进制转储程序)。

    另请参阅:Why is it that UTF-8 encoding is used when interacting with a UNIX/Linux environment

    【讨论】:

      【解决方案2】:

      可移植代码不支持它。 Windows(例如)支持使用wmain 而不是main,在这种情况下 argv 作为宽字符传递。

      【讨论】:

        【解决方案3】:

        在 Windows 上,您可以使用 GetCommandLineW()CommandLineToArgvW() 生成 argv 样式的 wchar_t[] 数组,即使应用程序未针对 Unicode 进行编译。

        【讨论】:

          【解决方案4】:

          无论如何,在 Windows 上,您都可以使用 wmain() 进行 UNICODE 构建。虽然不便携。我不知道 GCC 或 Unix/Linux 平台是否提供类似的东西。

          【讨论】:

            【解决方案5】:

            假设您的 Linux 环境使用 UTF-8 编码,那么以下代码将使您的程序准备好在 C++ 中轻松进行 Unicode 处理:

                int main(int argc, char * argv[]) {
                  std::setlocale(LC_CTYPE, "");
                  // ...
                }
            

            接下来,wchar_t 类型在 Linux 中是 32 位的,这意味着它可以保存单独的 Unicode 代码点,您可以安全地使用 wstring 类型在 C++ 中进行经典字符串处理(逐个字符)。使用上面的 setlocale 调用,插入到 wcout 将自动将您的输出转换为 UTF-8,从 wcin 提取将自动将 UTF-8 输入转换为 UTF-32(1 个字符 = 1 个代码点)。剩下的唯一问题是 argv[i] 字符串仍然是 UTF-8 编码的。

            您可以使用以下函数将 UTF-8 解码为 UTF-32。如果输入字符串损坏,它将返回正确转换的字符,直到 UTF-8 规则被破坏的地方。如果您需要更多错误报告,您可以改进它。但是对于 argv 数据,可以安全地假设它是正确的 UTF-8:

            #define ARR_LEN(x) (sizeof(x)/sizeof(x[0]))
            
                wstring Convert(const char * s) {
                    typedef unsigned char byte;
                    struct Level { 
                        byte Head, Data, Null; 
                        Level(byte h, byte d) {
                            Head = h; // the head shifted to the right
                            Data = d; // number of data bits
                            Null = h << d; // encoded byte with zero data bits
                        }
                        bool encoded(byte b) { return b>>Data == Head; }
                    }; // struct Level
                    Level lev[] = { 
                        Level(2, 6),
                        Level(6, 5), 
                        Level(14, 4), 
                        Level(30, 3), 
                        Level(62, 2), 
                        Level(126, 1)
                    };
            
                    wchar_t wc = 0;
                    const char * p = s;
                    wstring result;
                    while (*p != 0) {
                        byte b = *p++;
                        if (b>>7 == 0) { // deal with ASCII
                            wc = b;
                            result.push_back(wc);
                            continue;
                        } // ASCII
                        bool found = false;
                        for (int i = 1; i < ARR_LEN(lev); ++i) {
                            if (lev[i].encoded(b)) {
                                wc = b ^ lev[i].Null; // remove the head
                                wc <<= lev[0].Data * i;
                                for (int j = i; j > 0; --j) { // trailing bytes
                                    if (*p == 0) return result; // unexpected
                                    b = *p++;   
                                    if (!lev[0].encoded(b)) // encoding corrupted
                                        return result;
                                    wchar_t tmp = b ^ lev[0].Null;
                                    wc |= tmp << lev[0].Data*(j-1);
                                } // trailing bytes
                                result.push_back(wc);
                                found = true;
                                break;
                            } // lev[i]
                        }   // for lev
                        if (!found) return result; // encoding incorrect
                    }   // while
                    return result;
                }   // wstring Convert
            

            【讨论】:

              【解决方案6】:

              在 Windows 上,您可以使用 tchar.h 和 _tmain,如果在编译时定义了 _UNICODE 符号,它们将转换为 wmain,否则将转换为 main。如果定义了 unicode,则 TCHAR *argv[] 将类似地扩展为 WCHAR * argv[],否则将扩展为 char * argv[]。

              如果你想让你的 main 方法跨平台工作,你可以定义自己的宏来达到同样的效果。

              TCHAR.h 包含许多方便的宏,用于在 wchar 和 char 之间进行转换。

              【讨论】:

              • 注意:Unix 不支持wmain,只支持main(int argc, char *argv[]),因此对于使用宽字符的程序,您需要转换参数
              • 是的,在 2009 年,我的大部分 Unicode 体验是在 Windows 上而不是 Unix 上,但在任何最近的 Unix 系统上,您可能只需要查看用户是否处于 UTF-8 语言环境(环境变量 LC_CTYPE=UTF-8 可能足以作为提示,或者只是假设 utf-8 并让那些 Shift-Jis 人感到困惑)并在遵守规则的 C 风格 main() 中进行编码假设。 Unix 中可能不需要 wchar_t。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-04-26
              • 2012-07-12
              • 2013-02-25
              • 2014-03-20
              • 2011-05-05
              • 1970-01-01
              相关资源
              最近更新 更多