У меня есть проект MFC, который читает и пишет из и в файлы ANSI. Набор символов приложения установлен на Unicode.
добавление
У меня нет возможности изменить / повлиять на кодировку входного и выходного файла, потому что в моем контексте речь идет о преобразователе между устаревшим программным обеспечением.
Ожидаемая кодировка символов на самом деле окна-1252.
При чтении и записи некоторых файлов я заметил, что некоторые редко используемые символы, такие как Š (0x8A)
заменить на ? (0x3F)
при чтении и написании их с CStdioFile
, Я создал тестовый файл, чтобы увидеть, какие символы затрагиваются в диапазоне между 0x30
а также 0xFF
,
Я скопировал эти символы в TestFile (В кодировке ANSI) (символы от 0x30 до 0xFF)
И результирующий файл выглядел так этот:
Измененные символы находятся вокруг одного и того же региона и заменены на 0x3F '?'
— начиная с 0x80
вплоть до 0x9F
, Как ни странно, есть некоторые исключения, такие как 0x81
, 0x8D
, 0x90
а также 0x9D
которые не были затронуты.
Пример кода для проверки поведения:
//prepare vars
CFileException fileException;
CStdioFile filei;
CStdioFile fileo;
CString strText;//open input file
filei.Open(TEXT("test.txt"), CFile::modeRead | CFile::shareExclusive | CFile::typeText, &fileException);
//open output file
fileo.Open(TEXT("testout.txt"), CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive | CFile::typeText, &fileException);
//read and write
BOOL eof = filei.ReadString(strText) <= 0;
fileo.Write(CStringA(strText), CStringA(strText).GetLength());
//clean up
filei.Close();
fileo.Close();
Почему он это делает и что мне нужно сделать, чтобы сохранить все символы?
Отключение режима Юникод решит проблему, но, к сожалению, в моем случае это не вариант.
Резюме
Вот выдержка из вещей, которые были мне полезны из принятого ответа:
Не конвертировать из CStringW
в CStringA
просто вызвав его конструктор. При преобразовании из Unicode в «ANSI» (Windows1252) используйте CW2A
:
CStringA strTextA(strText, CP_ACP)` //CP_ACP converts to ANSI
fileo.Write(strTextA, strTextA.GetLength());
Еще проще: используйте CStdioFile::WriteString
метод вместо CStdioFile::WriteS
:
fileo.Open(TEXT("testout.txt"), CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive | CFile::typeText, &fileException);
fileo.WriteString(strText);
Проблема в том, что по умолчанию, если вы используете CStdioFile::Open
метод CStdioFile
способен только читать / записывать файлы ANSI, но вы можете открыть файл-поток самостоятельно и тогда вы сможете указать правильную кодировку:
FILE* fStream = NULL;
errno_t e = _tfopen_s(&fStream, _T("C:\\Files\\test.txt"), _T("rt,ccs=UNICODE"));
if (e != 0)
return; // failed to open file
CStdioFile f(fStream);
CString sRead;
f.ReadString(sRead);
f.Close();
Если вы хотите написать файл, вам нужно использовать _T("wt,ccs=UNICODE")
множество вариантов.
Другая очевидная проблема заключается в том, что вы звоните Write
вместо WriteString
, Там нет необходимости конвертировать CStringW
в CStringA
в случае WriteString
, Если требуется использовать Write
по какой-то причине вам придется правильно конвертировать CStringW
в CStringA
позвонив в CW2A()
с CP_UTF8
,
Вот пример кода, который использует общее назначение CFile
класс и Write
вместо CStdioFile
а также WriteString
:
CStringW sText = L"Привет мир";
CFile file(_T("C:\\Files\\test.txt"), CFile::modeWrite | CFile::modeCreate);
CStringA sUTF8 = CW2A(sText, CP_UTF8);
file.Write(sUTF8 , sUTF8.GetLength());
Пожалуйста, имейте в виду, что CFile
конструктор, который открывает файл и Write
метод броска CFileException
тип исключений. Таким образом, вы должны обращаться с ними.
Используйте следующие параметры при открытии потоков текстовых файлов, чтобы указать тип кодировки:
"ccs=UNICODE"
соответствует UTF-16 (Big Endian) "ccs=UTF-8"
соответствует UTF-8, "ccs=UTF-16LE"
соответствует UTF-16LE (Little Endian) Других решений пока нет …