Как прочитать файл UCS-2?
Я пишу программу для получения информации в кодировке файла *.rc в UCS-2 Little Endian.
int _tmain(int argc, _TCHAR* argv[]) {
wstring csvLine(wstring sLine);
wifstream fin("en.rc");
wofstream fout("table.csv");
wofstream fout_rm("temp.txt");
wstring sLine;
fout << "en\n";
while(getline(fin,sLine)) {
if (sLine.find(L"IDS") == -1)
fout_rm << sLine << endl;
else
fout << csvLine(sLine);
}
fout << flush;
system("pause");
return 0;
}
Первая строка в "en.rc" #include <windows.h>
но sLine
показывает, как показано ниже:
[0] 255 L'ÿ'
[1] 254 L'þ'
[2] 35 L'#'
[3] 0
[4] 105 L'i'
[5] 0
[6] 110 L'n'
[7] 0
[8] 99 L'c'
. .
. .
. .
Эта программа может работать правильно для UTF-8. Как я могу сделать это с UCS-2?
1 ответ
Широкие потоки используют широкий буфер потока для доступа к файлу. Буфер широкого потока читает байты из файла и использует его фасет codecvt для преобразования этих байтов в широкие символы. Фасет codecvt по умолчанию std::codecvt<wchar_t, char ,std::mbstate_t>
который преобразует между собственными наборами символов для wchar_t
а также char
(то есть как mbstowcs(
) делает).
Вы не используете нативный набор символов char, так что вам нужен фасет codecvt, который читает UCS-2
в виде многобайтовой последовательности и преобразует ее в широкие символы.
#include <fstream>
#include <string>
#include <codecvt>
#include <iostream>
int main(int argc, char *argv[])
{
wifstream fin("en.rc", std::ios::binary); // You need to open the file in binary mode
// Imbue the file stream with a codecvt facet that uses UTF-16 as the external multibyte encoding
fin.imbue(std::locale(fin.getloc(),
new std::codecvt_utf16<wchar_t, 0xffff, consume_header>));
// ^ We set 0xFFFF as the maxcode because that's the largest that will fit in a single wchar_t
// We use consume_header to detect and use the UTF-16 'BOM'
// The following is not really the correct way to write Unicode output, but it's easy
std::wstring sLine;
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert;
while (getline(fin, sLine))
{
std::cout << convert.to_bytes(sLine) << '\n';
}
}
Обратите внимание, что есть проблема с UTF-16
Вот. Цель wchar_t
для одного wchar_t
представлять одну кодовую точку. Однако Windows использует UTF-16
который представляет некоторые кодовые точки как два wchar_t
s. Это означает, что стандартный API не очень хорошо работает с Windows.
Следствием этого является то, что когда файл содержит суррогатную пару, codecvt_utf16
прочтет эту пару, преобразует ее в одно значение кодовой точки, превышающее 16 бит, и должно усечь значение до 16 бит, чтобы вставить его в wchar_t
, Это означает, что этот код действительно ограничен UCS-2
, Я установил для параметра шаблона maxcode значение 0xFFFF
чтобы отразить это.
Есть ряд других проблем с wchar_t
и вы можете просто избежать этого полностью: что "не так" с C++ wchar_t?
#include <filesystem>
namespace fs = std::filesystem;
FILE* f = _wfopen(L"myfile.txt", L"rb");
auto file_size = fs::file_size(filename);
std::wstring buf;
buf.resize((size_t)file_size / sizeof(decltype(buf)::value_type));// buf in my code is a template object, so I use decltype(buf) to decide its type.
fread(&buf[0], 1, 2, f); // escape UCS2 BOM
fread(&buf[0], 1, file_size, f);