Как переносимо записать std:: wstring в файл?

у меня есть wstring объявлен такой:

// random wstring
std::wstring str = L"abcàdëefŸg€hhhhhhhµa";

литерал будет UTF-8 закодирован, потому что мой исходный файл.

[EDIT: согласно Марку Рэнсому, это не обязательно так, компилятор решит, какую кодировку использовать - вместо этого предположим, что я читаю эту строку из файла, закодированного, например, в UTF-8]

Я очень хотел бы получить это в чтение файла (когда текстовый редактор установлен на правильный кодировка)

abcàdëefŸg€hhhhhhhµa

но ofstream не очень сотрудничает (отказывается принимать wstring параметры), и wofstream якобы должен знать, настройки локали и кодировки. Я просто хочу вывести этот набор байтов. Как обычно это делается?

EDIT: он должен быть кросс-платформенным и не следует полагаться на кодировку UTF-8. У меня просто есть набор байтов, хранящихся в wstring, и хотите вывести их. Это вполне может быть UTF-16, или обычный ФОРМАТ ASCII.

9 ответов


Почему бы не записать файл в двоичном формате. Просто используйте ofstream с настройкой std::ios::binary. Тогда редактор сможет его интерпретировать. Не забудьте флаг Unicode 0xFEFF в начале. Возможно, вам лучше писать с библиотекой, попробуйте один из них:

http://www.codeproject.com/KB/files/EZUTF.aspx

http://www.gnu.org/software/libiconv/

http://utfcpp.sourceforge.net/


на std::wstring вам нужно std::wofstream

std::wofstream f(L"C:\some file.txt");
f << str;
f.close();

std::wstring для чего-то вроде UTF-16 или UTF-32, не UTF-8. Для UTF-8 вы, вероятно, просто хотите использовать std::string, и напишите через std::cout. Просто FWIW, C++0x будет иметь литералы Unicode, которые должны помочь прояснить такие ситуации.


C++ имеет средства для выполнения преобразования из широкого символа в локализованные на выходе или записи файла. использовать фасет codecvt для этой цели.

вы можете использовать standard 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 "и сделать выбор из многих referenes перечисленных пользовательские реализации codecvt.

изменить: Поскольку OP утверждает, что строка уже закодирована, все, что он должен сделать, это удалить префиксы L и "w" Из каждого маркера его кода.


существует (специфичное для 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;
}

обратите внимание, что широкие потоки выводят только переменные char *, поэтому, возможно, вам следует попробовать использовать c_str() функция-член для преобразования std::wstring, а затем выведите его в файл. Тогда надо, наверное, работать?


вы должны не используйте исходный файл в кодировке UTF-8, Если вы хотите написать портативный код. Извиняюсь.

  std::wstring str = L"abcàdëefŸg€hhhhhhhµa";

(Я не уверен, что это действительно вредит стандарту, но я думаю, что это так. Но даже если, для безопасности, вы не должны.)

Да, чисто с помощью std::ostream не будет работать. Существует много способов преобразования wstring в UTF-8. Мой любимый использует на международные компоненты для Unicode. Это большая свобода, но это здорово. Вы получите много дополнительных услуг и вещей, которые вам могут понадобиться в будущем.


из моего опыта работы с различными кодировками символов я бы рекомендовал вам иметь дело только с UTF-8 При загрузке и экономии времени. Вы находитесь в мире боли, Если вы пытаетесь сохранить внутреннее представление в UTF-8, так как один символ может быть от 1 байта до 4. Поэтому простые операции, такие как strlen, требуют просмотра каждого байта, чтобы решить len, а не выделенный буфер (хотя вы можете оптимизировать, глядя на первый байт в последовательности символов, например, 00..7Ф является один байт типа char, С2..df указывает на 2-байтовый символ и т. д.).

люди довольно часто ссылаются на "строки Юникода", когда они означают UTF-16, а в Windows wchar_t-фиксированные 2 байта. В Windows я думаю, что wchar_t просто:

typedef SHORT wchar_t;

полное представление UTF-32 4 байта редко требуется и очень расточительно, вот что должен сказать на нем стандарт Unicode (5.0):

" в среднем более 99% всех UTF-16 выражается с использованием единичных кодовых единиц... UTF-16 обеспечивает правое смешивание компактного размера с способностью отрегулировать случайный характер вне BMP"

короче говоря, используйте whcar_t в качестве внутреннего представления и выполняйте преобразования при загрузке и сохранении (и не беспокойтесь о полном Unicode, если вы не знаете, что вам это нужно).

что касается выполнения фактического преобразования, посмотрите на проект ICU:

http://site.icu-project.org/


некоторое время назад у меня была такая же проблема, и я записал решение, которое нашел в своем блоге. Возможно, вы захотите проверить его, чтобы узнать, может ли это помочь, особенно функция wstring_to_utf8.

http://pileborg.org/b2e/blog5.php/2010/06/13/unicode-utf-8-and-wchar_t