【问题标题】:How to printf accented characters in ANSI C (like á é í ó ú)如何在 ANSI C 中打印重音字符(如 á é í ó ú)
【发布时间】:2019-09-26 05:58:52
【问题描述】:

我尝试printf 使用一些重音字符,例如á é í ó ú

printf("my name is Seán\n");

DEVC++ IDE 中的文本编辑器可以很好地显示它们 - 即源代码看起来很好。 我想我需要stdio.h 以外的一些库,也许还需要一些普通printf 的变体。

我正在使用在 Windows XP 上运行的 IDE Bloodshed DEVC。

【问题讨论】:

    标签: c printf ansi non-ascii-characters


    【解决方案1】:

    Windows 控制台模式使用的Windows-1252(也称为“ANSI”)字符集与 GUI 应用程序使用的字符集不同。因此,IDE 表示不同于运行时表示。

    您的示例的快速而肮脏的解决方案是:

    printf("my name is Se\xe9n\n");
    

    这个问题的大多数解决方案都存在某种缺陷,对于需要大量多语言本地化的 Windows 应用程序,最简单的解决方案是使用 Unicode 将它们编写为 GUI 应用程序。

    【讨论】:

    • 感谢所有给我答案的人 --- Rodrego、Alexey 和 Clifford。我真的很感激这个建议。我需要一些时间来尝试这些建议,并围绕这个主题。然后我会回到论坛中的这个问题,告诉我我用过的东西——或者在必要时寻求更多帮助。肖恩
    【解决方案2】:

    也许最好是使用 Unicode。

    方法如下...

    首先,手动将您的控制台字体设置为“Consolas”或“Lucida Console”或您可以选择的任何 True-Type Unicode 字体(“Raster fonts”可能不起作用,那些不是 Unicode 字体,尽管它们可能包含字符你感兴趣)。

    接下来,使用SetConsoleOutputCP(CP_UTF8) 将控制台代码页设置为 65001 (UTF-8)。

    然后使用 WideCharToMultiByte(CP_UTF8, ...) 将您的文本转换为 UTF-8(如果还不是 UTF-8)。

    最后调用WriteConsoleA()输出UTF-8文本。

    这是一个为您完成所有这些事情的小函数,它是wprintf() 的“改进”变体:

    int _wprintf(const wchar_t* format, ...)
    {
      int r;
      static int utf8ModeSet = 0;
      static wchar_t* bufWchar = NULL;
      static size_t bufWcharCount = 256;
      static char* bufMchar = NULL;
      static size_t bufMcharCount = 256;
      va_list vl;
      int mcharCount = 0;
    
      if (utf8ModeSet == 0)
      {
        if (!SetConsoleOutputCP(CP_UTF8))
        {
          DWORD err = GetLastError();
          fprintf(stderr, "SetConsoleOutputCP(CP_UTF8) failed with error 0x%X\n", err);
          utf8ModeSet = -1;
        }
        else
        {
          utf8ModeSet = 1;
        }
      }
    
      if (utf8ModeSet != 1)
      {
        va_start(vl, format);
        r = vwprintf(format, vl);
        va_end(vl);
        return r;
      }
    
      if (bufWchar == NULL)
      {
        if ((bufWchar = malloc(bufWcharCount * sizeof(wchar_t))) == NULL)
        {
          return -1;
        }
      }
    
      for (;;)
      {
        va_start(vl, format);
        r = vswprintf(bufWchar, bufWcharCount, format, vl);
        va_end(vl);
    
        if (r < 0)
        {
          break;
        }
    
        if (r + 2 <= bufWcharCount)
        {
          break;
        }
    
        free(bufWchar);
        if ((bufWchar = malloc(bufWcharCount * sizeof(wchar_t) * 2)) == NULL)
        {
          return -1;
        }
        bufWcharCount *= 2;
      }
    
      if (r > 0)
      {
        if (bufMchar == NULL)
        {
          if ((bufMchar = malloc(bufMcharCount)) == NULL)
          {
            return -1;
          }
        }
    
        for (;;)
        {
          mcharCount = WideCharToMultiByte(CP_UTF8,
                                           0,
                                           bufWchar,
                                           -1,
                                           bufMchar,
                                           bufMcharCount,
                                           NULL,
                                           NULL);
          if (mcharCount > 0)
          {
            break;
          }
    
          if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
          {
            return -1;
          }
    
          free(bufMchar);
          if ((bufMchar = malloc(bufMcharCount * 2)) == NULL)
          {
            return -1;
          }
          bufMcharCount *= 2;
        }
      }
    
      if (mcharCount > 1)
      {
        DWORD numberOfCharsWritten, consoleMode;
    
        if (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &consoleMode))
        {
          fflush(stdout);
          if (!WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),
                             bufMchar,
                             mcharCount - 1,
                             &numberOfCharsWritten,
                             NULL))
          {
            return -1;
          }
        }
        else
        {
          if (fputs(bufMchar, stdout) == EOF)
          {
            return -1;
          }
        }
      }
    
      return r;
    }
    

    以下测试此功能:

    _wprintf(L"\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7"
             L"\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
             L"\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7"
             L"\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"
             L"\n"
             L"\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7"
             L"\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"
             L"\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7"
             L"\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"
             L"\n"
             L"\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7"
             L"\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF"
             L"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7"
             L"\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"
             L"\n");
    
    _wprintf(L"\x391\x392\x393\x394\x395\x396\x397"
             L"\x398\x399\x39A\x39B\x39C\x39D\x39E\x39F"
             L"\x3A0\x3A1\x3A2\x3A3\x3A4\x3A5\x3A6\x3A7"
             L"\x3A8\x3A9\x3AA\x3AB\x3AC\x3AD\x3AE\x3AF\x3B0"
             L"\n"
             L"\x3B1\x3B2\x3B3\x3B4\x3B5\x3B6\x3B7"
             L"\x3B8\x3B9\x3BA\x3BB\x3BC\x3BD\x3BE\x3BF"
             L"\x3C0\x3C1\x3C2\x3C3\x3C4\x3C5\x3C6\x3C7"
             L"\x3C8\x3C9\x3CA\x3CB\x3CC\x3CD\x3CE"
             L"\n");
    
    _wprintf(L"\x410\x411\x412\x413\x414\x415\x401\x416\x417"
             L"\x418\x419\x41A\x41B\x41C\x41D\x41E\x41F"
             L"\x420\x421\x422\x423\x424\x425\x426\x427"
             L"\x428\x429\x42A\x42B\x42C\x42D\x42E\x42F"
             L"\n"
             L"\x430\x431\x432\x433\x434\x435\x451\x436\x437"
             L"\x438\x439\x43A\x43B\x43C\x43D\x43E\x43F"
             L"\x440\x441\x442\x443\x444\x445\x446\x447"
             L"\x448\x449\x44A\x44B\x44C\x44D\x44E\x44F"
             L"\n");
    

    并且应该在控制台中产生以下文本:

     ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿
    ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß
    àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
    ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήίΰ
    αβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ
    АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ
    абвгдеёжзийклмнопрстуфхцчшщъыьэюя
    

    我不知道您的 IDE 在 .c/.cpp 文件中存储非 ASCII 字符的编码,也不知道您的编译器在遇到非 ASCII 字符时会做什么。这部分你应该自己弄清楚。

    只要您向 _wprintf() 提供正确编码的 UTF-16 文本或使用正确编码的 UTF-8 文本调用 WriteConsoleA(),一切都会正常。

    附:一些关于控制台字体的血腥细节可以在here找到。

    【讨论】:

    • 这个解决方案的问题是“手动将您的控制台字体设置为[...]您可以选择的任何True-Type Unicode字体”。向最终用户解释这一点会使解决方案难以部署。
    • @Clifford 我还以为是 Windows 问题。
    • 200 行打印 'é' ?我想可以做得更简单。
    • @King'sjester 随时与我们分享您的发现。
    • 引用 cplusplus.com 上的某人的话:“有太多事情需要正确进行才能正确完成,而且并非所有事情都可以通过您的程序进行控制。它非常不可靠,几乎不值得努力。相信我,我是根据经验说话的。忘掉它吧。”
    【解决方案3】:

    Windows 控制台在字符编码方面通常被认为严重损坏。例如,您可以在here 阅读有关此问题的信息。

    问题是 Windows 一般使用 ANSI 代码页(假设您在西欧或美国 Windows-1252),但控制台使用 OEM 代码页(CP850 在相同假设下)。

    您有多种选择:

    • 在写入之前将文本转换为 CP850(参见CharToOem())。缺点是,如果用户将输出重定向到文件 (&gt; file.txt) 并使用例如记事本,他会看错的。
    • 更改控制台的代码页:您需要选择一个TTF控制台字体(例如Lucida Console)并使用命令chcp 1252
    • 使用 UNICODE 文本和 wprintf():无论如何您都需要 TTF 控制台字体。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-15
      • 2012-07-14
      相关资源
      最近更新 更多