如何在 ANSI C 中 printf 带重音的字符(例如á é í ó ú)

12

我尝试使用一些重音字符(如á é í ó ú)进行printf

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

DEVC++ IDE中的文本编辑器显示它们很好 - 即源代码看起来很好。我猜我需要使用stdio.h之外的某个库,也许是普通printf的某个变体。

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

3个回答

6
也许最好的方法是使用Unicode。
以下是具体步骤:
首先,将控制台字体手动设置为“Consolas”或“Lucida Console”或任何您可以选择的True-Type Unicode字体(“Raster fonts”可能不起作用,因为它们不是Unicode字体,尽管它们可能包含您感兴趣的字符)。
接下来,使用SetConsoleOutputCP(CP_UTF8)将控制台代码页设置为65001(UTF-8)。
然后,如果文本还没有转换为UTF-8,请使用WideCharToMultiByte(CP_UTF8, ...)进行转换。
最后,调用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(),就应该可以正常工作。
附:有关控制台字体的一些细节可在此处找到。

这个解决方案的问题在于“手动设置控制台字体为[...]任何你可以选择的True-Type Unicode字体”。向最终用户解释这一点使得部署变得困难。 - Clifford
@Clifford 我还以为这是Windows的问题。 - Alexey Frunze
打印200行“é”?我猜可能有更简单的方法。 - user6547518
@King'sjester 欢迎与我们分享你的发现。 - Alexey Frunze
在cplusplus.com上引用某人的话:“太多事情需要正确地进行,而且并不是所有这些事情都可以从您的程序中控制。它是如此不可靠,以至于几乎不值得花费精力。相信我,我是从经验中说话的。就算了吧。” - user6547518
@King'sjester 另一方面,如果我们没有针对移动目标进行定位,如果我们处理的是完美的Windows(因为它们不再开发),那么可以使用稳定的黑客技术。我不知道Windows 10是否仍然存在这个问题,需要先实现完美。 :) - Alexey Frunze

4

通常认为,Windows控制台在字符编码方面存在严重问题。在这里可以了解到这个问题。

问题在于,Windows通常使用ANSI代码页(假设您在西欧或美国,即Windows-1252),但控制台使用OEM代码页(在同样的假设下是CP850)。

有几种选择:

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

0

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

您的示例的快速且简单的解决方案是:

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

这个问题的大多数解决方案都有一定的缺陷,对于需要进行广泛的多语言本地化的Windows应用程序来说,最简单的解决方案是使用Unicode编写GUI应用程序。


感谢Rodrego、Alexey和Clifford给我答案。我非常感激你们的建议。我需要一点时间来尝试这些建议,并理解这个主题。然后我会回到论坛上这个问题,告诉大家我用了什么 - 或者如果需要更多帮助,再次寻求帮助。Seán - sean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接