Здесь есть несколько очень хороших ответов, но я думаю, что есть несколько вещей, которые я могу добавить в отношении Windows / Visual Studio. Это основано на моем опыте с VS2015. В Linux, в основном, ответ - везде использовать кодировку UTF-8 std::string
. На Windows / VS это становится более сложным. Вот почему. Windows ожидает, что строки, хранящиеся с использованием char
s, будут закодированы с использованием кодовой страницы локали. Это почти всегда набор символов ASCII, за которым следуют 128 других специальных символов в зависимости от вашего местоположения. Позвольте мне просто заявить, что это не только при использовании Windows API, есть три других основных места, где эти строки взаимодействуют со стандартным C ++. Это строковые литералы, выводимые в std::cout
с использованием <<
и передачей имени файла в std::fstream
.
Я буду прямо здесь, потому что я программист, а не специалист по языку. Я ценю, что USC2 и UTF-16 не совпадают, но для моих целей они достаточно близки, чтобы быть взаимозаменяемыми, и я использую их здесь как таковые. Я на самом деле не уверен, какая Windows использует, но мне, как правило, тоже не нужно знать. Я указал UCS2 в этом ответе, поэтому извините заранее, если я кого-то расстроил своим незнанием этого вопроса, и я рад изменить его, если у меня что-то не так.
Строковые литералы
Если вы вводите строковые литералы, которые содержат только символы, которые могут быть представлены вашей кодовой страницей, VS сохраняет их в вашем файле по 1 байту на кодировку символов на основе вашей кодовой страницы. Обратите внимание, что если вы измените свою кодовую страницу или передадите свой источник другому разработчику, используя другую кодовую страницу, то я думаю (но не проверял), что символ в конечном итоге будет другим. Если вы запустите свой код на компьютере, используя другую кодовую страницу, тогда я не уверен, что этот символ тоже изменится.
Если вы введете какие-либо строковые литералы, которые не могут быть представлены вашей кодовой страницей, VS попросит вас сохранить файл как Unicode. Файл будет закодирован как UTF-8. Это означает, что все символы не ASCII (включая те, которые находятся на вашей кодовой странице) будут представлены 2 или более байтами. Это означает, что если вы передадите свой источник кому-то другому, источник будет выглядеть так же. Однако перед передачей исходного кода компилятору VS преобразует кодированный в кодировке UTF-8 текст в кодированный код, и любые символы, отсутствующие в кодовой странице, заменяются на ?
.
Единственный способ гарантировать правильное представление строкового литерала Unicode в VS - это предшествовать строковому литералу L
, что делает его широким строковым литералом. В этом случае VS преобразует кодированный в UTF-8 текст из файла в UCS2. Затем вам нужно передать этот строковый литерал в конструктор std::wstring
, или вам нужно преобразовать его в utf-8 и поместить в std::string
. Или, если вы хотите, вы можете использовать функции Windows API для кодирования, используя кодовую страницу, чтобы поместить его в std::string
, но тогда вы, возможно, также не использовали широкий строковый литерал.
станд :: соиЬ
При выводе на консоль с использованием <<
вы можете использовать только std::string
, а не std::wstring
, и текст должен быть закодирован с использованием вашей кодовой страницы локали. Если у вас есть std::wstring
, то вы должны конвертировать его, используя одну из функций API Windows, и любые символы, отсутствующие на вашей кодовой странице, заменяются на ?
(возможно, вы можете изменить символ, я не помню).
std :: fstream filenames
ОС Windows использует имена файлов UCS2 / UTF-16, поэтому независимо от вашей кодовой страницы вы можете иметь файлы с любым символом Unicode. Но это означает, что для доступа или создания файлов с символами, которые не находятся на вашей кодовой странице, вы должны использовать std::wstring
. Другого пути нет. Это специфичное для Microsoft расширение std::fstream
, поэтому, вероятно, не будет компилироваться в других системах. Если вы используете std :: string, то вы можете использовать только те имена файлов, которые содержат только символы на вашей кодовой странице.
Ваши варианты
Если вы просто работаете над Linux, вы, вероятно, не зашли так далеко. Просто используйте UTF-8 std::string
везде.
Если вы просто работаете в Windows, просто используйте UCS2 std::wstring
везде. Некоторые пуристы могут сказать, что используют UTF8, а затем конвертируют, когда это необходимо, но зачем беспокоиться?
Если вы кроссплатформенный, то это откровенный беспорядок. Если вы пытаетесь использовать UTF-8 повсюду в Windows, вам нужно быть очень осторожным с строковыми литералами и выводом на консоль. Вы можете легко повредить свои строки там. Если вы используете std::wstring
повсюду в Linux, то у вас может не быть доступа к широкой версии std::fstream
, поэтому вам придется выполнять конвертацию, но нет риска повреждения. Так что лично я думаю, что это лучший вариант. Многие могут не согласиться, но я не одинок - это путь, выбранный, например, wxWidgets.
Другой вариант может заключаться в том, чтобы ввестиdef unicodestring
как std::string
в Linux и std::wstring
в Windows, и иметь макрос UNI (), который префикс L в Windows и ничего в Linux, а затем код
#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>
#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
std::string result;
//Call WideCharToMultiByte to do the conversion
return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
return str;
}
#endif
int main()
{
unicodestring fileName(UNI("fileName"));
std::ofstream fout;
fout.open(fileName);
std::cout << formatForConsole(fileName) << std::endl;
return 0;
}
было бы хорошо на любой платформе, я думаю.
Ответы
Итак, чтобы ответить на ваши вопросы
1) Если вы программируете для Windows, то все время, если кроссплатформенное, то, возможно, все время, если вы не хотите иметь дело с возможными проблемами повреждения в Windows или пишете код с платформой #ifdefs
для обхода различия, если просто использовать Linux, то никогда.
2) Да. В дополнение к Linux вы можете использовать его для всех Unicode тоже. В Windows вы можете использовать его только для всех Unicode, если вы решите вручную кодировать с использованием UTF-8. Но Windows API и стандартные классы C ++ будут ожидать, что код std::string
будет закодирован с использованием кодовой страницы локали. Сюда входят все ASCII плюс еще 128 символов, которые меняются в зависимости от кодовой страницы, которую ваш компьютер настроил для использования.
3) Я верю в это, но если нет, то это просто определение типа std :: basic_string с использованием wchar_t
вместо char
4) Широкий символ - это тип символа, который больше, чем 1-байтовый стандартный тип char
. В Windows это 2 байта, в Linux это 4 байта.