Прочитать файл Unicode UTF-8 в строку wstring

Как я могу прочитать файл Unicode (UTF-8) в wstring (s) на платформе Windows?


person Abdelwahed    schedule 23.01.2011    source источник
comment
Под Unicode вы имеете в виду UTF-8 или UTF-16? А какую платформу вы используете?   -  person dan04    schedule 23.01.2011
comment
Прочтите эту статью: Чтение UTF-8 с потоками C ++   -  person Nawaz    schedule 23.01.2011
comment
Еще одна хорошая статья: UTF-8 с C ++ переносимым способом   -  person Nawaz    schedule 23.01.2011
comment
В Windows вы должны использовать std :: string для UTF-8 и std :: wstring для UTF-16.   -  person anno    schedule 23.01.2011


Ответы (6)


С поддержкой C ++ 11 вы можете использовать фасет std :: codecvt_utf8 , который инкапсулирует преобразование между строка байтов в кодировке UTF-8 и строка символов UCS2 или UCS4 и , которые могут использоваться для чтения и записи файлов UTF-8, как текстовых, так и двоичных.

Чтобы использовать фасет, вы обычно создаете объект языкового стандарта , который инкапсулирует информацию, относящуюся к региональным параметрам, в виде набора аспектов, которые в совокупности определяют конкретную локализованную среду. Создав объект языкового стандарта, вы можете может наполнить им буфер потока:

#include <sstream>
#include <fstream>
#include <codecvt>

std::wstring readFile(const char* filename)
{
    std::wifstream wif(filename);
    wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
    std::wstringstream wss;
    wss << wif.rdbuf();
    return wss.str();
}

который можно использовать так:

std::wstring wstr = readFile("a.txt");

В качестве альтернативы вы можете установить глобальный языковой стандарт C ++, прежде чем работать со строковыми потоками, которые вызывают все будущие вызовы std::locale конструктора по умолчанию для возврата копии глобального языкового стандарта C ++ (в этом случае вам не нужно явно добавлять его в буферы потока):

std::locale::global(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
person LihO    schedule 15.05.2012
comment
Это new codecvt_utf8 требует соответствующего delete? - person Dmitri Nesteruk; 05.09.2016
comment
Нет необходимости явно удалять codecvt_utf8. Это делается в деструкторе std :: locale, когда счетчик ссылок codecvt_utf8 становится нулевым (см. en.cppreference.com/w/cpp/locale/locale/%7Elocale) - person MrTux; 14.10.2016
comment
Для тех, кто использует этот ответ, std :: locale :: empty () имеет проблему с clang: error: нет члена с именем 'empty' в 'std :: __ 1 :: locale'. - person Felipe Valdes; 22.03.2019
comment
К сожалению, все полезные части codecvt устарели в C ++ 20. - person Bob Kline; 19.11.2020

Согласно комментарию @Hans Passant, самый простой способ - использовать _wfopen_s. Откройте файл в режиме rt, ccs=UTF-8.

Вот еще одно чистое решение на C ++, которое работает как минимум с VC ++ 2010:

#include <locale>
#include <codecvt>
#include <string>
#include <fstream>
#include <cstdlib>

int main() {
    const std::locale empty_locale = std::locale::empty();
    typedef std::codecvt_utf8<wchar_t> converter_type;
    const converter_type* converter = new converter_type;
    const std::locale utf8_locale = std::locale(empty_locale, converter);
    std::wifstream stream(L"test.txt");
    stream.imbue(utf8_locale);
    std::wstring line;
    std::getline(stream, line);
    std::system("pause");
}

За исключением locale::empty() (здесь locale::global() также может работать) и wchar_t* перегрузки конструктора basic_ifstream, это даже должно быть достаточно совместимо со стандартом (где «стандарт», конечно же, означает C ++ 0x).

person Philipp    schedule 23.01.2011
comment
Почему ты не delete converter? - person Mikhail; 28.09.2013
comment
Перегрузка 7 обычно вызывается со вторым аргументом f, полученным непосредственно из выражения new: локаль отвечает за вызов соответствующего удаления из своего собственного деструктора. ссылка - person sven; 29.07.2015
comment
Это хорошо работает. Любопытно, так как я не могу найти много информации о нем, а мой отлично работает без него, что именно делает stream.imbue? Кажется, что он устанавливает какой-то тип по умолчанию, но нужно ли это? Кроме того, для примечания к первой строке поместите вашу строку getline в цикл while (getline (stream, line)), чтобы увидеть больше, чем первая строка. - person adprocas; 25.09.2016

Вот функция, зависящая от платформы, только для Windows:

size_t GetSizeOfFile(const std::wstring& path)
{
    struct _stat fileinfo;
    _wstat(path.c_str(), &fileinfo);
    return fileinfo.st_size;
}

std::wstring LoadUtf8FileToString(const std::wstring& filename)
{
    std::wstring buffer;            // stores file contents
    FILE* f = _wfopen(filename.c_str(), L"rtS, ccs=UTF-8");

    // Failed to open file
    if (f == NULL)
    {
        // ...handle some error...
        return buffer;
    }

    size_t filesize = GetSizeOfFile(filename);

    // Read entire file contents in to memory
    if (filesize > 0)
    {
        buffer.resize(filesize);
        size_t wchars_read = fread(&(buffer.front()), sizeof(wchar_t), filesize, f);
        buffer.resize(wchars_read);
        buffer.shrink_to_fit();
    }

    fclose(f);

    return buffer;
}

Используйте так:

std::wstring mytext = LoadUtf8FileToString(L"C:\\MyUtf8File.txt");

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

person AshleysBrain    schedule 23.01.2011
comment
Можно пройти весь путь: _wfopen (filename.c_str (), Lrt, ccs = UTF-8); Преобразование теперь автоматическое. - person Hans Passant; 23.01.2011
comment
На самом деле, после отката, документы по _wfopen говорят, что он автоматически преобразуется в широкие символы, и этот код не учитывает это. - person AshleysBrain; 23.01.2011
comment
Только имя файла. Цитата: Simply using _wfopen has no effect on the coded character set used in the file stream. - person Hans Passant; 23.01.2011
comment
Вы уверены? То, как я интерпретировал документы, указав t в режиме, а также ccs=UTF-8, символы будут преобразованы по мере их чтения в поток и из потока. - person AshleysBrain; 23.01.2011
comment
@Ashley: Да, цитата относится к использованию _wfopen без спецификатора режима ccs=. Вам нужны оба _wfopen (согласно руководству предпочтительнее _wfopen_s) и ccs=UTF-8. - person Philipp; 23.01.2011
comment
Позднее редактирование в августе: оказывается, способ @Hans Passant лучше - отредактировал ответ, чтобы использовать его вместо этого! - person AshleysBrain; 11.08.2011

Этот вопрос был рассмотрен в Не знаете, что такое std :: wstring, UTF-16, UTF-8 в C ++ и отображение строк в графическом интерфейсе Windows. В общем, wstring основан на стандарте UCS-2, который является предшественником UTF-16. Это строго двухбайтовый стандарт. Я считаю, что это относится к арабскому языку.

person ThomasMcLeod    schedule 23.01.2011
comment
Я думаю, вы можете использовать wstring с UTF-16 - person David Heffernan; 23.01.2011
comment
@Daivd: На самом деле вы ошибаетесь, и это распространенное заблуждение. UTF-16 охватывает 1,112,064 кодовых точки от 0 до 0x10FFFF. Схема требует хранения переменной длины одного или двух 16-битных слов, тогда как UCS-2 представляет собой строго одно 16-битное слово. Если вы проследите определение wchar_t, вы обнаружите, что он имеет в качестве корня примитивный тип из 16 бит (обычно короткий). - person ThomasMcLeod; 23.01.2011
comment
@David: Технически wstring - это просто массив 16-битных целых чисел в Windows. Вы можете хранить в нем данные UCS-2 или UTF-16 или что угодно. В настоящее время большинство API-интерфейсов Windows принимают строки UTF-16. - person Philipp; 23.01.2011
comment
@Philip Я думал, что все Windows API теперь UTF-16. Какие из них принимают UCS-2? - person David Heffernan; 23.01.2011
comment
@ Томас Боюсь, что это недопонимание из-за тебя. Я знаю о переменной длине UTF-16 и суррогатных пар. Но это полностью совместимо с wstring. Суррогатная пара принимает 2 элемента wchar_t. - person David Heffernan; 23.01.2011
comment
@Philipp: вы можете хранить подмножество символов UTF-16 в строке wstring. Например, вы не можете хранить символы балийского сценария в строке wstring, но для этих символов существуют допустимые кодировки UTF-16. en.wikipedia.org/wiki/Balinese_script - person ThomasMcLeod; 23.01.2011
comment
@ Томас, это неверно. UTF-16 использует 16-битные кодовые единицы, то есть wchar_t в Windows. - person David Heffernan; 23.01.2011
comment
@ Томас Я должен согласиться с Дэвидом. Вы можете сохранить любую кодовую точку Unicode в wstring, если рассматривать ее как строку UTF-16. Для кодовых точек, отличных от BMP, потребуется две кодовых единицы, но в этом нет ничего плохого. - person Philipp; 23.01.2011
comment
@ Филипп: убери мою предыдущую. Я хотел сослаться на сценарий брахманов, который еще более неясен. - person ThomasMcLeod; 23.01.2011
comment
@David: Я думаю (но я не уверен, что сейчас я не использую Windows), что консоль по-прежнему не обрабатывает символы, отличные от BMP. Спорный вопрос, связано ли это с самим API. - person Philipp; 23.01.2011
comment
@Thomas все, что имеет определенный код Unicode, может быть представлено в UTF-16 - person David Heffernan; 23.01.2011
comment
@Philipp консоль - это целый мир боли! Даже заставить его отображать кодовые точки, отличные от ANSI, - это проявление крайнего мазохизма! - person David Heffernan; 23.01.2011
comment
@David: Нет, это две строки, см. блоги. msdn.com/b/michkap/archive/2008/03/18/8306597.aspx - person Philipp; 23.01.2011
comment
@Philipp Очень интересно! Я привык к Python в Windows, который поддерживает консоль для мусора. - person David Heffernan; 23.01.2011
comment
@ Дэвид: Кажется, мы спорим о семантике. Вы сказали, что я думаю, что можно использовать wstring с UTF-16. Это означает больше, чем просто магазин. Это означает сохранить и правильно интерпретировать его, по крайней мере, с помощью stdio. Я просто пробовал использовать символы SMP с wcout и wstring в 64-разрядной версии Windows 7 pro и получил много тарабарщины. - person ThomasMcLeod; 23.01.2011
comment
@Thomas Это не значит, что проблема в wstring. - person David Heffernan; 23.01.2011
comment
@David Я думаю, что это проблема Python, а не Windows. Я знаю, что разработчики Python изо всех сил стараются обеспечить повсюду поддержку Unicode, но я думаю, что трудно привести фактическую семантику Windows к модели, которая предполагает, что потоки операционной системы всегда основаны на байтах и ​​не зависят от кодирования (это верно для файлов Unix и консольные потоки и для файловых потоков Windows, но не для консоли Windows). Я не изучал исходный код Python, но думаю, что, по крайней мере, когда-то в прошлом они предполагали, что эта модель работает. - person Philipp; 23.01.2011
comment
@Philipp Очень жаль, что консоль Windows немного заброшена. - person David Heffernan; 23.01.2011
comment
@Thomas: Я не думаю, что библиотека MSVC ++ iostreams поддерживает какой-либо Unicode, кроме разрешения имен файлов Unicode. Все решения для использования Unicode в C ++ по сути являются чистыми решениями C, либо с использованием Windows API напрямую, либо с использованием нестандартных расширений библиотеки C. - person Philipp; 23.01.2011
comment
@ Филипп, согласен. Вот почему я говорю, что wstring - это UCS-2, а не UTF-16. - person ThomasMcLeod; 23.01.2011
comment
@David: проблема не в хранилище wstring, а в типичном использовании wstring и UTF-16. Можно ли хранить UTF-16 в битовом наборе, если хотите, но используется ли он с UTF-16? Не совсем. - person ThomasMcLeod; 23.01.2011
comment
@thomas, что бы вы использовали вместо wstring? - person David Heffernan; 24.01.2011
comment
@Thomas: Стандартная библиотека MSVC ++ также не поддерживает UCS-2. В прошлый раз, когда я проверил, локали C ++ не поддерживали никакую локаль Unicode, что делало вывод Unicode практически невозможным. - person Philipp; 24.01.2011
comment
Исправление: библиотека MSVC ++ поддерживает UTF-16 и UTF-32. для типов char16_t и char32_t это по существу решило бы проблему файлового ввода-вывода. - person Philipp; 24.01.2011
comment
@ Дэвид: Нет хорошего ответа. Что использовать, я полагаю, зависит от фреймворка, платформы, конкретных требований ввода-вывода и т. Д. В общем, если нужно поддерживать не-BMP, char32_t и UTF-32 кажутся более безопасными. - person ThomasMcLeod; 24.01.2011
comment
@Thomas Нет, вопрос в том, что вы используете вместо wstring для UTF-16 - person David Heffernan; 24.01.2011
comment
@David, преобразуйте его в UTF-32, затем используйте строку ‹char32_t›. Или в .Net используйте system.text.UTF32Encoding - person ThomasMcLeod; 24.01.2011
comment
@ Дэвид, если, конечно, вы можете гарантировать BMP, тогда нет никаких проблем. - person ThomasMcLeod; 24.01.2011
comment
@thomas вы слышали о суррогатных парах? UTF-16 разработан для использования с 16 кодовыми единицами. Вне БМП все нормально. Знаете ли вы, что UTF-16 может кодировать все кодовые точки Unicode? - person David Heffernan; 24.01.2011
comment
@ Дэвид, да, я в курсе. Проблема в том, что многие API, использующие wstrings, не знают разницы. Они интерпретируют суррогатные пары как две 16-битные кодовые точки. Но поскольку суррогатные пары находятся в недопустимом диапазоне BMP, они игнорируются. - person ThomasMcLeod; 24.01.2011
comment
@thomas, это было бы критикой API, но ваша исходная точка зрения заключается в том, что wstring не подходит для хранения UTF-16. В любом случае, какие API вы имеете в виду. Мне любопытно узнать, какие из них не поддерживают Unicode. - person David Heffernan; 24.01.2011

Это немного сыро, но как насчет того, чтобы прочитать файл как простые старые байты, а затем преобразовать байтовый буфер в wchar_t *?

Что-то вроде:

#include <iostream>
#include <fstream>
std::wstring ReadFileIntoWstring(const std::wstring& filepath)
{
    std::wstring wstr;
    std::ifstream file (filepath.c_str(), std::ios::in|std::ios::binary|std::ios::ate);
    size_t size = (size_t)file.tellg();
    file.seekg (0, std::ios::beg);
    char* buffer = new char [size];
    file.read (buffer, size);
    wstr = (wchar_t*)buffer;
    file.close();
    delete[] buffer;
    return wstr;
}
person dlchambers    schedule 18.10.2012
comment
Я думаю, что это не сработает - файл содержит UTF-8, а не последовательность wchar_t. - person ChrisW; 12.05.2021

person    schedule
comment
Привет. Спасибо, что поделился. Оценил. Не могли бы вы добавить немного больше контекста? Почему это ответ на вопросы шестилетней давности. Спасибо. - person wp78de; 03.11.2017
comment
У меня недавно возник вопрос, но теперь я решил, я хочу поделиться своим решением, чтобы помочь другим. - person Shen Yu; 03.11.2017
comment
Это мило. Но чем ваш ответ отличается от ответа @LihO? Вы просто используете другой язык, верно? - person wp78de; 03.11.2017
comment
У меня не сработало. Завершено с использованием ‹codecvt› от @LihO - person Peter L; 15.11.2019