wostream не может вывести wstring

Я использую Visual Studio C++ 2008 (Express). Когда я запускаю приведенный ниже код, Wostream (оба std::wcout, а также std::wfstream) прекращает вывод на первый встреченный не-ASCII символ (в данном случае китайский). Обычные символы ASCII печатаются нормально. Однако в отладчике я вижу, что wstringна самом деле правильно заполнены китайскими иероглифами, и output << ... на самом деле исполняется.

Настройки проекта в решении Visual Studio установлены на "Использовать набор символов Unicode". Почему std::wostream не удается вывести символы Юникода за пределы диапазона ASCII?

void PrintTable(const std::vector<std::vector<std::wstring>> &table, std::wostream& output) {
    for (unsigned int i=0; i < table.size(); ++i) {
        for (unsigned int j=0; j < table[i].size(); ++j) {
            output << table[i][j] << L"\t";
        }
        //output << std::endl;
    }
}


void TestUnicodeSingleTableChinesePronouns() {
    FileProcessor p("SingleTableChinesePronouns.docx");
    FileProcessor::iterator fileIterator;
    std::wofstream myFile("data.bin", std::ios::out | std::ios::binary);
    for(fileIterator = p.begin(); fileIterator != p.end(); ++fileIterator) {
        PrintTable(*fileIterator, myFile);
        PrintTable(*fileIterator, std::wcout);
        std::cout<<std::endl<<"---------------------------------------"<<std::endl;
    }
    myFile.flush();
    myFile.close();
}

2 ответа

Решение

По умолчанию языковой стандарт, используемый std::wcout и std::wofstream для определенных операций, является языковым стандартом "C", который не требуется для поддержки не-ascii символов (или любых символов вне базового набора символов C++). Измените локаль на ту, которая поддерживает символы, которые вы хотите использовать.

К сожалению, в Windows проще всего использовать устаревшие кодовые страницы, однако вам действительно следует этого избегать. Устаревшие кодовые страницы - плохие новости. Вместо этого вы должны использовать Unicode, будь то UTF-8, UTF-16 или что-то еще. Также вам придется обойти неудачную модель консоли Windows, которая сильно отличает запись в консоль от записи в другие виды выходных потоков. Вам может понадобиться найти или написать собственный выходной буфер, который специально обрабатывает консоль (или, возможно, отправить сообщение об ошибке с просьбой Microsoft исправить это).

Вот пример вывода на консоль:

#include <Windows.h>

#include <streambuf>
#include <iostream>

class Console_streambuf
    : public std::basic_streambuf<wchar_t>
{
    HANDLE m_out;
public:
    Console_streambuf(HANDLE out) : m_out(out) {}

    virtual int_type overflow(int_type c = traits_type::eof())
    {
        wchar_t wc = c;
        DWORD numberOfCharsWritten;
        BOOL res = WriteConsoleW(m_out, &wc, 1, &numberOfCharsWritten, NULL);
        (void)res;
        return 1;
    }
};

int main() {
    Console_streambuf out(GetStdHandle(STD_OUTPUT_HANDLE));
    auto old_buf = std::wcout.rdbuf(&out);
    std::wcout << L"привет, 猫咪!\n";
    std::wcout.rdbuf(old_buf); // replace old buffer so that destruction can happen correctly. FIXME: use RAII to do this in an exception safe manner.
}

Вы можете сделать вывод UTF-8 в файл, подобный этому (хотя я не уверен, что VS2008 поддерживает codecvt_utf8_utf16):

#include <codecvt>
#include <fstream>

int main() {
    std::wofstream myFile("data.bin", std::ios::out | std::ios::binary);
    myFile.imbue(std::locale(myFile.getloc(),new std::codecvt_utf8_utf16<wchar_t>));

    myFile << L"привет, 猫咪!";
}

Включить следующий заголовочный файл

#include <locale>

в начале main добавьте следующую строку.

std::locale::global(std::locale("chinese"));

Это помогает установить правильную локаль.

Другие вопросы по тегам