在控制台应用程序 C 中读取/写入特殊字符(如波浪号、ñ、...)

Read / Write special characters (like tilde, ñ,...) in a console application C

我正在尝试让 C 控制台应用程序可以读取(使用键盘)特殊的西班牙字符,例如 scanfgets 中的重音符、'ñ' 等,然后打印printf.

感谢软件包 locale.h,我已经实现了正确显示这些字符(存储在变量中或直接来自 printf)。我举个例子:

#include <stdio.h>
// Add languaje package
#include <locale.h>

int main(void)
{
    char string[254];

    // Set languaje to Spanish
    setlocale(LC_ALL, "spanish");

    // Show correctly spanish special chars 
    printf("¡Success!. It is shown special chars like 'ñ' or 'á'.\n\n\n");

    // Gets special chars by keyboard
    printf("Input spanish special chars (such 'ñ'): ");
    gets(string);

    printf("Your string is: %s", string);

    return 0;   
}

但我还没有实现用上面提到的功能正确地拾取它们。

有人知道怎么做吗?

谢谢。


编辑 1:

在测试中,我观察到:


编辑 2:

我也试过 setlocale(LC_ALL, "");setlocale(LC_ALL, "es_ES.UTF-8");setlocale(LC_ALL, "es_ES.ISO_8859-15");,结果与 EDIT 1 相同(或者从键盘上抓取字符或在控制台中很好地显示它们,但绝不能同时显示它们)。

Microsoft 的 C 运行时库 (CRT) 不支持 UTF-8 作为语言环境编码。它仅支持 Windows 代码页。此外,"es_ES" 不是有效的 CRT 语言环境字符串,因此 setlocale 会失败,使您处于默认的 C 语言环境。 Microsoft 的 CRT 的较新版本支持 Windows 语言环境名称,例如 "es-ES"(连字符,而不是下划线)。否则 CRT 使用全名或旧的 3 字母缩写,例如"spanish_spain"、"esp_esp" 或 "esp_esp.1252"。

但这还没有结束。当使用传统文本编码而不是 Unicode 读取和写入控制台时,控制台本身还有另一层翻译。为避免 mojibake,您必须设置控制台输入和输出代码页(即 SetConsoleCPSetConsoleOutputCP)以匹配语言环境代码页。如果您仅限于西班牙语或 Latin-1,那么将区域设置设置为 "spanish" 并通过 SetConsoleCP(1252)SetConsoleOutputCP(1252) 设置控制台代码页应该可行。更一般地说,您可以查找给定语言环境名称的 ANSI 代码页,设置控制台代码页,并保存它们以便在退出时重置控制台。例如:

wchar_t *locale_name = L"es-ES";
if (_wsetlocale(LC_ALL, locale_name)) {
    int codepage;
    gPrevConsoleCP = GetConsoleCP();
    if (gPrevConsoleCP) { // The process is attached to a console.
        gPrevConsoleOutputCP = GetConsoleOutputCP();
        if (GetLocaleInfoEx(locale_name, 
                            LOCALE_IDEFAULTANSICODEPAGE | 
                            LOCALE_RETURN_NUMBER, 
                            (LPWSTR)&codepage, 
                            sizeof(codepage) / sizeof(wchar_t))) {
            if (!codepage) { // The locale doesn't have an ANSI codepage.
                codepage = GetACP();
            }
            SetConsoleCP(codepage);
            SetConsoleOutputCP(codepage);
            atexit(reset_console);
        }
    }
}

也就是说,在使用控制台工作时,如果您将 stdinstdout 设置为使用 _O_U16TEXT 模式并使用 wide-character 函数,通常情况会更好例如 fgetwswprintf。最终,如果 C 运行时库支持,这应该使用 wide-character 控制台 I/O 函数 ReadConsoleWWriteConsoleW。使用 UTF-16 wide-character 模式的缺点是它需要完全重写代码以使用 wchar_t 字符串和 wide-character 函数,并且还需要为使​​用的库实现适配器多字节编码字符串(最好是 UTF-8)。