【问题标题】:How to display text in the locale of the command line?如何在命令行的语言环境中显示文本?
【发布时间】:2014-07-06 18:59:44
【问题描述】:

对于一个小项目,我需要在 Windows 的 CMD 中输出可能已本地化的文本字符串,并且从程序的参数中读取一些字符串。为了简化问题,我将使用一个简单的 echo 程序作为演示。

请考虑C语言中的sn-p:

#include <stdio.h>

int main(int argc, char **argv) {
    // Display the first argument through the standard output:
    if (argc > 1)
        puts(argv[1]);
    return 0;
}

这些是两次执行的输出:

$ test.exe Wilhelm
$ Wilhelm

$ test.exe Röntgen
$ R÷ntgen

您已经可以看到像ö 这样超出ASCII 的内容将无法正确显示。但它们在程序中被正确识别,例如,如果您执行以下操作:

if (argv[1][1] == 'ö')
    puts("It is.");

将显示句子,因此程序正确接收字符。

所以我,好吧,可能需要 wchar_t 的东西,所以进行适当的更改并定义 UNICODE_UNICODE 你会得到:

#include <stdio.h>

int wmain(int argc, wchar_t **argv) {
    // Display the first argument through the standard output:
    if (argc > 1)
        _putws(argv[1]);
    return 0;
}

这个测试程序的输出仍然是一样的。

环顾四周并阅读文档,我发现了一种解决方法,例如将语言环境设置为英语:文本将正确显示。修改第一个版本(没有wchar_ts)我最终得到了这个:

#include <stdio.h>
#include <locale.h>

int main(int argc, char **argv) {
    // Get the previous locale and change to English:
    char *old_locale = setlocale(LC_ALL, NULL);
    setlocale(LC_ALL, "English");
    // Display the first argument through the standard output:
    if (argc > 1)
        puts(argv[1]);
    // Restore locale:
    setlocale(LC_ALL, old_locale);
    return 0;
}

"en-US" 似乎在 MinGW-w64 中不起作用,而 "English" 与它和 Microsoft Visual C++ 一起使用)

现在程序可以打印了,这样字符就可以在命令行窗口中正确显示了。

问题在于,在西班牙语系统或日语系统中,将内容设置为英语并不是最好的做法。所以我考虑以某种方式从系统中获取语言环境。我找到了一个名为_get_current_locale 的函数,它返回一个_locale_t,但它似乎根本不是我想要的:

_locale_t_variable-&gt;locinfo-&gt;lc_category[LC_ALL].locale(这是一个char *)似乎是NULL

所以问题是,如何在命令行的语言环境中获取或显示文本?在 Windows 的 CMD(不一定是 Unicode)中处理本地化文本的正确方法是什么?

【问题讨论】:

  • 你的问题有道理。 echo 程序可以在我的 Win7 机器上正确回显 Röntgen;所以你想要做的显然是可能的。
  • 但是,echo 在 MS cmd shell 内部。它可以由外壳进行“特殊”处理......
  • 默认情况下,命令提示符使用 OEM 代码页。设置 C 语言环境无关紧要。但是,您可以更改此代码页。
  • SetConsoleCP() 和/或 SetConsoleOutputCP() 与 CP_UTF8 或 65001 不会使程序输出字符,因为它们在程序的参数中输入。我当前的代码页是 437(不是 Unicode)并且可以输入/输出这些字符,我将在问题中添加该信息。

标签: c windows unicode cmd locale


【解决方案1】:

“这是两个输出...”:如果您使用的是cmd.exe,为什么提示符类似于美元符号?你是这样设置的吗?如果你真的在使用cmd.exe,你可以检查“代码页”:

mode con cp /status

如果您发现它是 437,这可以解释您的意外观察。打开charmap.exe,你会发现你关心的字符叫做“U+00F6 Latin Small Letter O With Diaresis”。如果您使用代码页 437 将其粘贴到 CLI 中,则会发生一些有趣的事情...

将传递给 unicode 程序的代码将是:0xF60x00您的程序将收到此代码。

该字符被识别为存在于代码页 437 中,但代码为 0x94。我相信 CLI(包括 echo 命令)会执行一些所见即所得的操作,并且后一个代码 (0x94) 会显示给您并输出到 stdout

如果您从 CLI 将字符复制到剪贴板,它将获得与“OEM 文本”和 0x94 代码的额外关联。

现在让我们切换到代码页1252

mode con cp select=1252

在此代码页中,当您从 Character Map 粘贴到 CLI 中时,传递给 unicode 程序的代码与上一个场景中的相同。

但是现在您观察到的字符是 Terminal 字体(一种在视觉上类似于代码页 437 的字体)中的 0xF6,因此您有了除号。 echo 命令会将相同的代码发送到 stdout

如果您从 CLI 将字符复制到剪贴板,它将获得与“OEM 文本”和 0x94 代码的额外关联,与以前相同。

如果您将带有此字符的 echo 命令的输出重定向到一个文件并使用终端字体在 Notepad 中打开一个文件,您将看到除法-标志。如果您将字体更改为 Courier New,您将看到 Unicode 中的“带分叉的小 o”。

现在切换回代码页437

mode con cp select=437

如果你想让 Windows unicode 程序将未翻译的 Unicode 序列输出到 FILE *,我相信你必须使用 binary 模式。要修改您的原始代码,您可能需要:

#define _UNICODE

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

#include <tchar.h>
#include <fcntl.h>
#include <io.h>

int __cdecl _tmain(int argc, TCHAR ** argv, TCHAR ** envp) {
    wchar_t bom = 0xFEFF;

    _setmode(_fileno(stdout), _O_BINARY);

    _ftprintf(stdout, _T("%c"), bom);
    _putts(argv[1]);

    return EXIT_SUCCESS;
  }

在本例中,我们在写入 UTF-16 字符之前写入 UTF-16LE 字节顺序标记(“BOM”) stdout 的参数。这在 CLI 中看起来很难看,但如果您重定向到文件或直接使用文件(以二进制模式),结果可能更符合您最初感兴趣的内容:

#define _UNICODE

#ifdef _UNICODE
#define BOM { 0xFF, 0xFE, 0, 0 }
#else
#define BOM { 0 }
#endif

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

#include <tchar.h>
#include <fcntl.h>
#include <io.h>

int __cdecl _tmain(int argc, TCHAR ** argv, TCHAR ** envp) {
    /* Initialize the BOM string */
    static const union {
        unsigned char bytes[sizeof (TCHAR) * 2];
        TCHAR c[2];
      } bom = BOM;
    FILE * f;
    TCHAR filename[] = _T("testfile.txt");
    int r;
    int rc;

    /* Assume failure */
    rc = EXIT_FAILURE;

    if (argc != 2) {
        _ftprintf(stderr, _T("Usage: %s <word>\n"), argv[0]);
        goto err_usage;
      }

    f = _tfopen(filename, _T("wb"));
    if (!f) {
        _ftprintf(stderr, _T("Could not open file: %s\n"), filename);
        goto err_fopen;
      }

    r = _ftprintf(f, _T("%s"), bom.c);
    if (r != _tcsclen(bom.c)) {
        _ftprintf(stderr, _T("Could not write BOM to file\n"));
        goto err_bom;
      }

    r = _ftprintf(f, _T("%s"), argv[1]);
    if (r !=  _tcsclen(argv[1])) {
        _ftprintf(stderr, _T("Could not write argument to file\n"));
        goto err_arg;
      }

    rc = EXIT_SUCCESS;

    err_arg:

    err_bom:

    fclose(f);
    err_fopen:

    err_usage:

    return rc;
  }

这里有一些额外的资源可能会有所帮助:

_tfopenhttp://msdn.microsoft.com/en-us/library/yeby3zcb.aspx

_ftprintfhttp://msdn.microsoft.com/en-us/library/xkh07fe2.aspx

_setmodehttp://msdn.microsoft.com/en-us/library/tw4k6df8.aspx

关于带有文本和二进制流的 Unicode:http://msdn.microsoft.com/en-us/library/c4cy2b8e.aspx

SBCS、MBCS、Unicode 函数:http://msdn.microsoft.com/en-us/library/tsbaswba.aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-04
    • 2012-02-24
    • 2017-07-17
    • 2012-11-03
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    相关资源
    最近更新 更多