std::fstream & UTF-8 посимвольное чтение

Необходимо посимвольно читать из файла в кодировке UTF-8

std::fstream fileStream;
fileStream.open(std::filesystem::path(fileName), std::ios_base::in);
std::string line;
while (!fileStream.eof())
{
    line += m_fileStream.get();
....
}

текст в фале кириллицей. На первой итерации в line мусор, на 2-й появляется реальный символ из файла, и так каждую четную итерацию.

при этом std::getline(fileStream, line); работает корректно, но мне нужно считывать до определенной последовательности, поэтому она не подходит.

Каким образом посимвольно считать в строку из файла? PS: желательно иметь одинаковый алгоритм при разных кодировках файла.


Ответы (3 шт):

Автор решения: KoVadim

Символы кириллицы занимают 2 байта. Поэтому, нужно читать сразу по два. В некоторых случаях символы могут занимать 3 или даже 4 байта (для некоторых японских эмоджи). Что делать? читать UTF-8 правильно! Для начала смотрим в wiki uft-8. А потом понимаем, что для кириллицы (да и для 99% случаев, код будет такой. прочитать байт. Если байт меньше 128 - ок, это английский символ, все ок. Если нет, то посмотреть в первые биты. Если там '110xxxxx' - прочитать ещё один байт.

при этом std::getline(fileStream, line); работает корректно, но мне нужно считывать до определенной последовательности, поэтому она не подходит.

Правильно, потому что он читает до перевода строк, а при выводе консоль все делает за Вас. Если измерите строку в байтах (.length()), то обнаружите, что оно немного больше, чем реально выведено символов.

А ещё лучше прочитать строку, а потом делить ее на символы

→ Ссылка
Автор решения: Stanislav Volodarskiy

Можно читать символы стандартными средствами. wifstream умеет читать многобайтовые кодировки если верно установлена локаль:

// g++ -std=c++11 -pedantic -Wall -Wextra -Werror utf8.cpp

#include <fstream>
#include <iostream>
#include <locale>

int main(int argc, char **argv) {
    if (argc != 2) {
        return 1;
    }

    std::ios_base::sync_with_stdio(false);
    std::wcout.imbue(std::locale("C.UTF-8"));

    std::wifstream f(argv[1]);
    f.imbue(std::locale("C.UTF-8"));

    wchar_t c;
    while (f.get(c)) {
        std::wcout << "'" << c << "', ";
    }
}
$ g++ -std=c++11 -pedantic -Wall -Wextra -Werror utf8.cpp
$ cat input 
Hello world!
Привет, мир!
你好, 世界!
$ ./a.out input
'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '
', 'П', 'р', 'и', 'в', 'е', 'т', ',', ' ', 'м', 'и', 'р', '!', '
', '你', '好', ',', ' ', '世', '界', '!', '
', 
→ Ссылка
Автор решения: user286634

VIsual Studio 2015.

  1. Все файлы проекта должны быть в кодировке utf8.
  2. В свойствах проекта устанавливаете UNICODE.
  3. В файле stdafx.h пишете #pragma execution_character_set("utf-8") // строки string кодируются в UTF8
    bool read_file_utf8(vector<string> & vs, bool bAddEnd=false )
    {
        ATLASSERT(!m_pathW.empty());// путь берется из диалога открытия файла
        bool ret = false;
        streamoff szFile = get_size_file();
        if (szFile == 0 || szFile == -1)
            return ret;

        vs.clear();
        vs.reserve((size_t)szFile);
        ifstream fl(m_pathW.c_str());
        locale loc;
        if (!fl)
            return ret;

        fl.imbue(locale("rus"));
        string s;
        s.reserve(1024);
        CHAR buf[1024] = { 0 };
        
        // skip bom
        if (fl.get() != 0xef && fl.get() != 0xbb && fl.get() != 0xbf)
            fl.seekg(0, ios::beg);
        else fl.seekg(3, ios::beg);

        while (fl.good())
        {
            fl.getline(buf, 1024);
            s.assign(buf);
            if (bAddEnd) s += "\r\n";
            vs.push_back(s);
        }

        if (fl.eof())
            ret = true;

        return ret;
    }

Путь к файлу всегда LPCTSTR или wstring.c_str(), при необходимости можно перекодировать.

    string utf16_to_utf8(wstring ws)
    {
        string str;
        str.reserve(ws.size());

        for (auto it : ws)
        {
            if (it < 128)
                str.push_back((BYTE)it);
            else
            {
                int first_bits = 6;
                const int other_bits = 6;
                int first_val = 0xC0;
                int t = 0;
                if (it >= (1 << first_bits))
                {
                    t = 128 | (it & ((1 << other_bits) - 1));
                    it >>= other_bits;
                    first_val |= 1 << (first_bits);
                    --first_bits;
                }
                str.push_back(first_val | it);
                str.push_back(t);
            }
        }

        return str;
    }

→ Ссылка