代码之家  ›  专栏  ›  技术社区  ›  JuMoGar

读/写特殊字符(如颚化符,…)在控制台应用程序C中

  •  2
  • JuMoGar  · 技术社区  · 7 年前

    我正在尝试让C控制台应用程序可以读取(使用键盘)特殊的西班牙语字符,例如重音符号、“±”等 scanf gets 然后,用 printf

    我已经实现了正确显示这些字符(存储在变量中,或者直接从 打印F )多亏了这个包裹 locale.h 。我举一个例子:

    #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:

    在测试中,我观察到:

    • setlocale(LC_ALL, "spanish"); 它可以正确显示西班牙语的字符,但不会从键盘收集这些字符。
    • setlocale(LC_ALL, "es_ES"); 它可以从键盘上正确地拾取西班牙语字符,但无法很好地显示它们。


    编辑2:

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

    1 回复  |  直到 7 年前
        1
  •  2
  •   Eryk Sun    7 年前

    Microsoft的C运行时库(CRT)不支持UTF-8作为区域设置编码。它只支持Windows代码页。此外,“es\u es”不是有效的CRT区域设置字符串,因此 setlocale 将失败,使您处于默认的C语言环境中。较新版本的Microsoft CRT支持Windows区域设置名称,如“es es”(连字符,而不是下划线)。否则,CRT使用全名或旧的三个字母缩写,例如“spanish\u Spania”、“esp\u esp”或“esp\u esp.1252”。

    但这还不是故事的结局。当使用传统文本编码(而不是Unicode)从控制台读写时,控制台本身有另一层翻译。为了避免mojibake,您必须设置控制台输入和输出代码页(即。 SetConsoleCP SetConsoleOutputCP )以匹配区域设置代码页。如果您仅限于西班牙语或拉丁语-1,那么可以将语言环境设置为“西班牙语”,并通过以下方式设置控制台代码页 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);
            }
        }
    }
    

    也就是说,在使用控制台时,如果设置 stdin stdout 使用 _O_U16TEXT 模式并使用宽字符函数,例如 fgetws wprintf 。最终,如果C运行时库支持,则应使用宽字符控制台I/O函数 ReadConsoleW WriteConsoleW 。使用UTF-16宽字符模式的缺点是需要对代码进行完全重写才能使用 wchar_t 字符串和宽字符函数,还需要为使用多字节编码字符串(最好是UTF-8)的库实现适配器。