繁体   English   中英

从控制台读取UTF-8字符

[英]Reading UTF-8 characters from console

我正试图从我的c ++应用程序的控制台读取UTF-8编码的抛光字符。 我确信控制台使用此代码页(已检入属性)。 我已经尝试过的:

  • 使用cin - 而不是“zażółć”我读了“za \\ 0 \\ 0 \\ 0 \\ 0”
  • 使用wcin - 而不是“zażółć” - 与cin相同的结果
  • 使用scanf - 而不是'zażółć\\ 0'我读'za \\ 0 \\ 0 \\ 0 \\ 0 \\ 0'
  • 使用wscanf - 与scanf相同的结果
  • 使用getchar逐个读取字符 - 与scanf相同的结果

在主要功能的开头我有以下几行:

setlocale(LC_ALL, "PL_pl.UTF-8");
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);

我真的非常乐于助人。

这是我用于UTF-8支持的技巧。 结果是多字节字符串,然后可以在别处使用:

#include <cstdio>
#include <windows.h>
#define MAX_INPUT_LENGTH 255

int main()
{

    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);

    wchar_t wstr[MAX_INPUT_LENGTH];
    char mb_str[MAX_INPUT_LENGTH * 3 + 1];

    unsigned long read;
    void *con = GetStdHandle(STD_INPUT_HANDLE);

    ReadConsole(con, wstr, MAX_INPUT_LENGTH, &read, NULL);

    int size = WideCharToMultiByte(CP_UTF8, 0, wstr, read, mb_str, sizeof(mb_str), NULL, NULL);
    mb_str[size] = 0;

    std::printf("ENTERED: %s\n", mb_str);

    return 0;
}

应该是这样的:

在此输入图像描述

PS非常感谢Remy Lebeau指出一些瑕疵!

虽然您已经接受了答案,但这是一个更便携的版本,它更接近标准库。 不幸的是,这是我发现很多广泛使用的实现不支持标准中所谓的东西的一个领域。 例如,应该有一种打印多字节字符串的标准方法(理论上它可能像shift-JIS一样不同,但实际上在每个现代操作系统上都是UTF-8),但它实际上并不是可移植的。 微软的运行时库在这方面特别差,但我也发现了libc ++中的错误。

/* Boilerplate feature-test macros: */
#if _WIN32 || _WIN64
#  define _WIN32_WINNT  0x0A00 // _WIN32_WINNT_WIN10
#  define NTDDI_VERSION 0x0A000002 // NTDDI_WIN10_RS1
#  include <sdkddkver.h>
#else
#  define _XOPEN_SOURCE     700
#  define _POSIX_C_SOURCE   200809L
#endif

#include <iostream>
#include <locale>
#include <locale.h>
#include <stdlib.h>
#include <string>

#ifndef MS_STDLIB_BUGS // Allow overriding the autodetection.
/* The Microsoft C and C++ runtime libraries that ship with Visual Studio, as
 * of 2017, have a bug that neither stdio, iostreams or wide iostreams can
 * handle Unicode input or output.  Windows needs some non-standard magic to
 * work around that.  This includes programs compiled with MinGW and Clang
 * for the win32 and win64 targets.
 *
 * NOTE TO USERS OF TDM-GCC: This code is known to break on tdm-gcc 4.9.2. As
 * a workaround, "-D MS_STDLIB_BUGS=0" will at least get it to compile, but
 * Unicode output will still not work.
 */
#  if ( _MSC_VER || __MINGW32__ || __MSVCRT__ )
    /* This code is being compiled either on MS Visual C++, or MinGW, or
     * clang++ in compatibility mode for either, or is being linked to the
     * msvcrt (Microsoft Visual C RunTime) library.
     */
#    define MS_STDLIB_BUGS 1
#  else
#    define MS_STDLIB_BUGS 0
#  endif
#endif

#if MS_STDLIB_BUGS
#  include <io.h>
#  include <fcntl.h>
#endif

using std::endl;
using std::istream;
using std::wcin;
using std::wcout;

void init_locale(void)
// Does magic so that wcout can work.
{
#if MS_STDLIB_BUGS
  // Windows needs a little non-standard magic.
  constexpr char cp_utf16le[] = ".1200";
  setlocale( LC_ALL, cp_utf16le );
  _setmode( _fileno(stdout), _O_WTEXT );
  _setmode( _fileno(stdin), _O_WTEXT );
#else
  // The correct locale name may vary by OS, e.g., "en_US.utf8".
  constexpr char locale_name[] = "";
  setlocale( LC_ALL, locale_name );
  std::locale::global(std::locale(locale_name));
  wcout.imbue(std::locale());
  wcin.imbue(std::locale());
#endif
}

int main(void)
{
  init_locale();

  static constexpr size_t bufsize = 1024;
  std::wstring input;
  input.reserve(bufsize);

  while ( wcin >> input )
    wcout << input << endl;

  return EXIT_SUCCESS;
}

无论其初始语言环境或代码页如何,它都会从控制台读取宽字符输入。 如果你的意思是输入将是UTF-8编码中的字节(例如来自UTF-8编码的重定向文件),而不是控制台输入,那么实现这一目标的标准方法应该是转换方面。 <codecvt><locale> UTF-8到wchar_t ,但实际上Windows不支持Unicode语言环境,因此您必须读取字节,然后手动转换它们。 更为标准的方法是使用mbstowcs() 我有一些旧代码来为STL迭代器进行转换,但标准库中也有转换函数。 无论如何,您可能需要这样做,例如,您需要以UTF-8保存或传输。

有些人会建议您在内部以UTF-8存储所有字符串,即使在使用基于某种形式的UTF-16的Windows之类的API时,只有在进行API调用时才转换为其他编码。 我强烈建议你尽可能在外部使用UTF-8,但我不会那么远。 但请注意,将字符串存储为UTF-8可以节省大量内存,尤其是在wchar_t为UCS-32的系统上。 你会有更好的想法,这通常会为波兰语文本节省多少字节。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM