В двух словах, если вы хотите передать/получить текст Unicode в/из консоли в Windows 10 (фактически, любую версию Windows), вам нужно использовать широкие строки IE, std :: wstring. Сама Windows не поддерживает кодировку UTF-8. Это фундаментальное ограничение ОС.
Весь API Win32, на котором основаны такие функции, как доступ к консоли и файловой системе, работает только с символами Unicode под кодировкой UTF-16, а время выполнения C/C++, предоставляемое в Visual Studio, не предлагает никаких слоя перевода, чтобы сделать этот API UTF-8 совместимым. Это не означает, что вы не можете использовать внутреннюю кодировку UTF-8, это просто означает, что, когда вы нажмете на Win32 API или функцию времени исполнения C/C++, которая его использует, вам нужно будет конвертировать между UTF-8 и UTF -16. Это отстой, но это именно то, где мы сейчас.
Некоторые люди могут направить вас к серии трюков, которые пропорциональны работе консоли с UTF-8. Не ходите по этому маршруту, у вас будет много проблем. Для доступа к консоли Unicode поддерживаются только широкосимвольные строки.
Edit: Поскольку UTF-8/UTF-16 строка преобразования нетривиален, и там тоже не большая помощь предоставляется для этого в C++, вот некоторые функции преобразования I, полученные ранее:
///////////////////////////////////////////////////////////////////////////////////////////////////
std::wstring UTF8ToUTF16(const std::string& stringUTF8)
{
// Convert the encoding of the supplied string
std::wstring stringUTF16;
size_t sourceStringPos = 0;
size_t sourceStringSize = stringUTF8.size();
stringUTF16.reserve(sourceStringSize);
while (sourceStringPos < sourceStringSize)
{
// Determine the number of code units required for the next character
static const unsigned int codeUnitCountLookup[] = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4 };
unsigned int codeUnitCount = codeUnitCountLookup[(unsigned char)stringUTF8[sourceStringPos] >> 4];
// Ensure that the requested number of code units are left in the source string
if ((sourceStringPos + codeUnitCount) > sourceStringSize)
{
break;
}
// Convert the encoding of this character
switch (codeUnitCount)
{
case 1:
{
stringUTF16.push_back((wchar_t)stringUTF8[sourceStringPos]);
break;
}
case 2:
{
unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x1F) << 6) |
((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F);
stringUTF16.push_back((wchar_t)unicodeCodePoint);
break;
}
case 3:
{
unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x0F) << 12) |
(((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F) << 6) |
((unsigned int)stringUTF8[sourceStringPos + 2] & 0x3F);
stringUTF16.push_back((wchar_t)unicodeCodePoint);
break;
}
case 4:
{
unsigned int unicodeCodePoint = (((unsigned int)stringUTF8[sourceStringPos] & 0x07) << 18) |
(((unsigned int)stringUTF8[sourceStringPos + 1] & 0x3F) << 12) |
(((unsigned int)stringUTF8[sourceStringPos + 2] & 0x3F) << 6) |
((unsigned int)stringUTF8[sourceStringPos + 3] & 0x3F);
wchar_t convertedCodeUnit1 = 0xD800 | (((unicodeCodePoint - 0x10000) >> 10) & 0x03FF);
wchar_t convertedCodeUnit2 = 0xDC00 | ((unicodeCodePoint - 0x10000) & 0x03FF);
stringUTF16.push_back(convertedCodeUnit1);
stringUTF16.push_back(convertedCodeUnit2);
break;
}
}
// Advance past the converted code units
sourceStringPos += codeUnitCount;
}
// Return the converted string to the caller
return stringUTF16;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
std::string UTF16ToUTF8(const std::wstring& stringUTF16)
{
// Convert the encoding of the supplied string
std::string stringUTF8;
size_t sourceStringPos = 0;
size_t sourceStringSize = stringUTF16.size();
stringUTF8.reserve(sourceStringSize * 2);
while (sourceStringPos < sourceStringSize)
{
// Check if a surrogate pair is used for this character
bool usesSurrogatePair = (((unsigned int)stringUTF16[sourceStringPos] & 0xF800) == 0xD800);
// Ensure that the requested number of code units are left in the source string
if (usesSurrogatePair && ((sourceStringPos + 2) > sourceStringSize))
{
break;
}
// Decode the character from UTF-16 encoding
unsigned int unicodeCodePoint;
if (usesSurrogatePair)
{
unicodeCodePoint = 0x10000 + ((((unsigned int)stringUTF16[sourceStringPos] & 0x03FF) << 10) | ((unsigned int)stringUTF16[sourceStringPos + 1] & 0x03FF));
}
else
{
unicodeCodePoint = (unsigned int)stringUTF16[sourceStringPos];
}
// Encode the character into UTF-8 encoding
if (unicodeCodePoint <= 0x7F)
{
stringUTF8.push_back((char)unicodeCodePoint);
}
else if (unicodeCodePoint <= 0x07FF)
{
char convertedCodeUnit1 = (char)(0xC0 | (unicodeCodePoint >> 6));
char convertedCodeUnit2 = (char)(0x80 | (unicodeCodePoint & 0x3F));
stringUTF8.push_back(convertedCodeUnit1);
stringUTF8.push_back(convertedCodeUnit2);
}
else if (unicodeCodePoint <= 0xFFFF)
{
char convertedCodeUnit1 = (char)(0xE0 | (unicodeCodePoint >> 12));
char convertedCodeUnit2 = (char)(0x80 | ((unicodeCodePoint >> 6) & 0x3F));
char convertedCodeUnit3 = (char)(0x80 | (unicodeCodePoint & 0x3F));
stringUTF8.push_back(convertedCodeUnit1);
stringUTF8.push_back(convertedCodeUnit2);
stringUTF8.push_back(convertedCodeUnit3);
}
else
{
char convertedCodeUnit1 = (char)(0xF0 | (unicodeCodePoint >> 18));
char convertedCodeUnit2 = (char)(0x80 | ((unicodeCodePoint >> 12) & 0x3F));
char convertedCodeUnit3 = (char)(0x80 | ((unicodeCodePoint >> 6) & 0x3F));
char convertedCodeUnit4 = (char)(0x80 | (unicodeCodePoint & 0x3F));
stringUTF8.push_back(convertedCodeUnit1);
stringUTF8.push_back(convertedCodeUnit2);
stringUTF8.push_back(convertedCodeUnit3);
stringUTF8.push_back(convertedCodeUnit4);
}
// Advance past the converted code units
sourceStringPos += (usesSurrogatePair) ? 2 : 1;
}
// Return the converted string to the caller
return stringUTF8;
}
Я был ответственным за незавидную задачу преобразования 6-миллионного унаследованного приложения Windows для поддержки Unicode, когда он был написан только для поддержки ASCII (на самом деле его предварительный формат Unicode), где мы использовали std :: string и char [] для хранения строк. Поскольку изменение всех внутренних буферов хранения строк было просто невозможно, нам нужно было внедрить UTF-8 внутренне и преобразовать между UTF-8 и UTF-16 при попадании в Win32 API. Это функции преобразования, которые мы использовали.
Я настоятельно рекомендую придерживаться того, что поддерживается для новой разработки Windows, что означает широкие строки. Тем не менее, нет причин, по которым вы не можете основывать ядро своей программы на строках UTF-8, но при взаимодействии с Windows и различных аспектах времени работы C/C++ это будет более сложно.
Редактировать 2: Я только что перечитал исходный вопрос, и я вижу, что я не очень хорошо ответил на него. Позвольте мне дать дополнительную информацию, которая специально ответит на ваш вопрос.
Что происходит? При разработке с C++ в Windows, когда вы используете std :: string с std :: cin/std :: cout, консоль ввода-вывода выполняется с использованием кодирования MBCS. Это устаревший режим, при котором символы кодируются с использованием выбранного на данный момент code page. Значения, закодированные под этими кодовыми страницами, не являются unicode и не могут использоваться совместно с другими системами, для которых выбрана другая кодовая страница, или даже с той же системой, если кодовая страница изменена. Он отлично работает в вашем тесте, потому что вы записываете ввод под текущей кодовой страницей и отображаете его на той же кодовой странице.Если вы попытаетесь захватить этот ввод и сохранить его в файл, проверка покажет, что он не является юникодом. Загрузите его с другой кодовой страницей, выбранной в нашей ОС, и текст будет поврежден. Вы можете интерпретировать текст только в том случае, если вы знаете, на какой кодовой странице он был закодирован. Поскольку эти страницы устаревшего кода являются региональными, и ни один из них не может представлять все текстовые символы, это делает невозможным совместное использование общего текста на разных компьютерах и компьютерах. MBCS предваряет развитие юникода, и именно из-за таких проблем, которые был изобретен unicode. Unicode - это, в основном, «одна кодовая страница, чтобы управлять всеми ими». Возможно, вам интересно, почему UTF-8 не является выбираемой «старой» кодовой страницей в Windows. Многим из нас интересно то же самое. Достаточно сказать, что это не так. Таким образом, вы не должны полагаться на кодировку MBCS, потому что вы не можете получить поддержку Unicode при ее использовании. Единственный вариант поддержки Unicode в Windows - использование std :: wstring и вызов UTF-16 Win32 API.
Что касается вашего примера о жестко закодированной струне, то прежде всего понять, что кодирование текста, отличного от ASCII, в исходный файл помещает вас в область поведения, специфичного для компилятора. В Visual Studio вы можете указать кодировку исходного файла (в разделе «Файл-> Расширенные параметры сохранения»). В вашем случае текст отличается от того, что вы ожидаете, потому что он кодируется (скорее всего) в UTF-8, но, как уже упоминалось, вывод консоли выполняется с использованием кодирования MBCS на выбранной вами кодовой странице, которая не UTF-8. Исторически вам было бы рекомендовано избегать любых символов, отличных от ASCII, в исходных файлах и избегать использования с помощью обозначения \ x. Сегодня существуют C++ 11 string prefixes and suffixes, которые гарантируют различные формы кодирования. Вы можете попробовать использовать их, если вам нужна эта способность. У меня нет практического опыта их использования, поэтому я не могу сообщить, есть ли какие-либо проблемы с этим подходом.
В какой кодировке вы сохраняете исходный файл? –
Вы можете использовать строковые литералы utf8 в C++ 11 и более поздних версиях: http://en.cppreference.com/w/cpp/language/string_literal – BitTickler
С u8 он выводит «caf├®» ... И я понятия не имею о кодировании ... –