Как переносить std:: wstring в файл?
У меня есть wstring
, объявленный как таковой:
// random wstring
std::wstring str = L"abcàdëefŸg€hhhhhhhµa";
Литерал будет кодироваться в кодировке UTF-8, потому что мой исходный файл.
[EDIT: Согласно Mark Ransom, это не обязательно так, что компилятор решит, какую кодировку использовать. Предположим, что я прочитал эту строку из файла, закодированного, например. UTF-8]
Я бы очень хотел, чтобы это было в файле чтения (когда текстовый редактор установлен на правильную кодировку)
abcàdëefŸg€hhhhhhhµa
но ofstream
не очень кооперативен (отказывается принимать параметры wstring
), а wofstream
предположительно должен знать настройки языка и кодировки. Я просто хочу вывести этот набор байтов. Как обычно это делается?
EDIT: он должен быть кросс-платформой, а не должен полагаться на кодировку UTF-8. У меня просто есть набор байтов, хранящихся в wstring
, и хочу их вывести. Это вполне может быть UTF-16 или простой ASCII.
Ответы
Ответ 1
Почему бы не записать файл как двоичный файл. Просто используйте streamstream с двоичной установкой std:: ios::. Редактор должен уметь его интерпретировать. Не забывайте флаг Unicode 0xFEFF в начале.
Возможно, вам лучше написать библиотеку, попробуйте один из них:
http://www.codeproject.com/KB/files/EZUTF.aspx
http://www.gnu.org/software/libiconv/
http://utfcpp.sourceforge.net/
Ответ 2
Для std::wstring
вам нужно std::wofstream
std::wofstream f(L"C:\\some file.txt");
f << str;
f.close();
Ответ 3
std::wstring
- это что-то вроде UTF-16 или UTF-32, а не UTF-8. Для UTF-8 вы, вероятно, просто хотите использовать std::string
и выписать через std::cout
. Просто FWIW, С++ 0x будет иметь литералы из Юникода, что должно помочь прояснить ситуации вроде этого.
Ответ 4
С++ имеет средства для выполнения преобразования от широкого символа к локализованному на выходе или записи файла. Используйте для этого facecvt facet.
Вы можете использовать стандартные std:: codecvt_byname или нестандартную реализацию codecvt_facet .
#include <locale>
using namespace std;
typedef codecvt_facet<wchar_t, char, mbstate_t> Cvt;
locale utf8locale(locale(), new codecvt_byname<wchar_t, char, mbstate_t> ("en_US.UTF-8"));
wcout.imbue(utf8locale);
wcout << L"Hello, wide to multybyte world!" << endl;
Остерегайтесь того, что на некоторых платформах codecvt_byname может выпустить преобразование только для локалей, установленных в системе. Поэтому я рекомендую искать stackoverflow для "utf8 codecvt" и сделайте выбор из многих реферансов, реализованных в пользовательских реализациях codecvt.
EDIT:
Поскольку OP утверждает, что строка уже закодирована, все, что он должен сделать, это удалить префиксы L и "w" из каждого символа его кода.
Ответ 5
Существует (для Windows) решение, которое должно работать для вас здесь. В основном, конвертируйте wstring
в кодовую страницу UTF-8, а затем используйте ofstream
.
#include < windows.h >
std::string to_utf8(const wchar_t* buffer, int len)
{
int nChars = ::WideCharToMultiByte(
CP_UTF8,
0,
buffer,
len,
NULL,
0,
NULL,
NULL);
if (nChars == 0) return "";
string newbuffer;
newbuffer.resize(nChars) ;
::WideCharToMultiByte(
CP_UTF8,
0,
buffer,
len,
const_cast< char* >(newbuffer.c_str()),
nChars,
NULL,
NULL);
return newbuffer;
}
std::string to_utf8(const std::wstring& str)
{
return to_utf8(str.c_str(), (int)str.size());
}
int main()
{
std::ofstream testFile;
testFile.open("demo.xml", std::ios::out | std::ios::binary);
std::wstring text =
L"< ?xml version=\"1.0\" encoding=\"UTF-8\"? >\n"
L"< root description=\"this is a naïve example\" >\n< /root >";
std::string outtext = to_utf8(text);
testFile << outtext;
testFile.close();
return 0;
}
Ответ 6
Обратите внимание, что в широких потоках выводятся только переменные char *, поэтому, возможно, вам стоит попробовать использовать функцию-член c_str()
для преобразования std::wstring
, а затем вывести его в файл. Тогда это должно сработать?
Ответ 7
У меня была такая же проблема некоторое время назад, и я написал решение, которое я нашел в своем блоге. Возможно, вы захотите проверить его, если это поможет, особенно функция wstring_to_utf8.
http://pileborg.org/b2e/blog5.php/2010/06/13/unicode-utf-8-and-wchar_t
Ответ 8
Не следует использовать исходный файл с кодировкой UTF-8, если вы хотите написать переносимый код. К сожалению.
std::wstring str = L"abcàdëefŸg€hhhhhhhµa";
(Я не уверен, что это действительно повредит стандарт, но я думаю, что это так. Но даже если, чтобы быть в безопасности, вы не должны.)
Да, чистое использование std::ostream
не будет работать. Существует много способов преобразования wstring
в UTF-8. Мой любимый использует Международные компоненты для Юникода. Это большая библиотека, но это здорово. Вы получаете много дополнительных услуг и вещей, которые вам могут понадобиться в будущем.
Ответ 9
Из моего опыта работы с разными кодировками символов я бы рекомендовал вам работать с UTF-8 при загрузке и сэкономить время. Если вы пытаетесь сохранить внутреннее представление в UTF-8, вы можете испытывать боль, поскольку один символ может быть от 1 байт до 4. Такие простые операции, как strlen, требуют рассмотрения каждого байта, чтобы решить len, а не (хотя вы можете оптимизировать, посмотрев на первый байт в последовательности char, например 00..7f - это один байт char, c2..df указывает 2 байта char и т.д.).
Люди довольно часто ссылаются на "строки Unicode", когда они означают UTF-16, а в Windows wchar_t - фиксированные 2 байта. В Windows я думаю, что wchar_t просто:
typedef SHORT wchar_t;
Полное представление байтов UTF-32 4 байтов редко требуется и очень расточительно, вот что должен сказать об этом Unicode Standard (5.0):
"В среднем более 99% всех UTF-16 выражается с использованием единых кодовых блоков... UTF-16 обеспечивает правильное сочетание компактных размеров с возможностью обработки случайного символа вне BMP"
Короче говоря, используйте whcar_t как свое внутреннее представление и делайте конверсии при загрузке и сохранении (и не беспокойтесь о полном Unicode, если не знаете, что вам это нужно).
Что касается выполнения фактического преобразования, посмотрите проект ICU:
http://site.icu-project.org/