0

Здраствуйте! Хочу считать символы из файла и вывести в консоль используя именно Win32 API. По какой то причине символы в консоль выводятся неправильно. Кодировка стоит utf-8 и русские символы работают нормально. Я пробовал записать в файл считанные символы и всё прекрасно работает. Совершенно не могу понять в чём проблема. Помогите пожалуйста!

#include <Windows.h>
#include <iostream>
#include <locale.h>
#include <io.h>
#include <fcntl.h>

typedef wchar_t uchar;

const int SymbolSize = (sizeof(uchar));

int SendError(const char* message) { std::cout << message << std::endl; return 0; }

HANDLE OpenFileRead(const wchar_t* file_path) { HANDLE hfile = CreateFileW(file_path, GENERIC_ALL, 0, 0, 3, FILE_ATTRIBUTE_NORMAL, 0); if (hfile == 0) { SendError("OpenFileError"); return 0; } return hfile; }

LPVOID MemAlloc(int bytes) { LPVOID Mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bytes); if (Mem == 0) { SendError("MemoryAllocError"); return 0; } return Mem; }

int Readfile(HANDLE file, void* buffer, int bytes_to_read) { BOOL answer = ReadFile(file, buffer, bytes_to_read, 0, 0); if (answer == 0) { SendError("ReadFileError"); return -1; } return 0; }

int Writefile(HANDLE file, void* buffer, int bytes_to_read) { BOOL answer = WriteFile(file, buffer, bytes_to_read, 0, 0); if (answer == 0) { SendError("ReadFileError"); return -1; } return 0; }

int main() { system("chcp 1251"); _setmode(_fileno(stdout), _O_U8TEXT); _setmode(_fileno(stdin), _O_U8TEXT); _setmode(_fileno(stderr), _O_U8TEXT); HANDLE file = OpenFileRead(L"C:\Users\rodio\OneDrive\Документы\ha lol.txt"); void* buffer = MemAlloc(20); Readfile(file, buffer, 12); uchar ubuffer = (uchar)buffer; std::wcout << L"Петя" << std::endl; std::wcout << ubuffer << std::endl; }

Вывод консоли:
Текущая кодовая страница: 1251
Петя
晲⁲††††

C:\Users\rodio\source\repos\TmodloaderDecompiler\x64\Debug\TmodloaderDecompiler.exe (процесс 5480) завершил работу с кодом 0. Нажмите любую клавишу, чтобы закрыть это окно…

Qzim 45
  • 35

1 Answers1

1

Отличия в том, что UTF-8 - это UTF-8, а 1251 - это (неожиданно) 1251! Это разные кодировки! И выводить символы UTF-8 в консоль, которая настроена на 1251, и удивляться, почему выводится всё неправильно, ну это лол!

Однако в вашем случае 1251 тут вообще не причём! Ибо для вывода вы используете wcout. А это уже кодировка UNICODE, которой в Windows соответствует стандарт UTF-16LE (если не ошибаюсь). Опять же - другая кодировка, не UTF-8!

Что же делать? Есть такая функция WinAPI, как MultiByteToWideChar(). С её помощью можно конвертировать строку из UTF-8 в UNICODE. Собственно, вместо строки

std::wcout << ubuffer << std::endl;

напишите следующее:

int wlen = MultiByteToWideChar(CP_UTF8, 0, ubuffer, -1, 0, 0);
wchar_t* wbuffer = (wchar_t*)MemAlloc(len*sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, ubuffer, -1, wbuffer, wlen);
std::wcout << wbuffer << std::endl;

Должно сработать (но есть нюанс - см. p.p.s ниже). Кстати, рекомендую не полагаться на флаг HEAP_ZERO_MEMORY при создании буфера, а обнулять символ в конце прочитанных данных непосредственно после вызова ReadFile(). Для этого параметер lpNumberOfBytesRead окажется весьма полезен.

P.S. Вызовы system("chcp 1251") и _setmode() не нужны.

P.P.S. Для справки. UTF-8 - это кодировка с символами переменной длины! Так, латинские буквы, цифры и прочие стандартные символы занимают один байт, и их коды полностью совпадают с ASCII. А вот русские буквы кидируются уже большим числом байт, и их коды радикально отличаются и от ASCII (что логично), и от UTF-16! И вообще, длина кодов символов в UTF-8 варьируется от 1 до 6 байт. Так что прочитав из UTF8-файла 12 байт, вы не обязательно прочитали 12 символов. А если это русский текст, то точно не 12, а меньше. А последний прочитанный символ, к тому же, может оказаться ещё и битым (прочитанным частично). Советую это учесть.

LShadow77
  • 2,157
  • Спасибо огромное! Правда, у меня без _setmode(); текст вообще не выводится. – Qzim 45 Oct 09 '23 at 04:17
  • @Qzim 45 а вот это уже странно, почему без _setmode() не выводится. Значит что-то не так. – LShadow77 Oct 09 '23 at 09:31