C прочитать файл unicode
У меня проблемы с чтением и использованием содержимого файлов Unicode.
Я работаю над сборкой выпуска Unicode, и я пытаюсь прочитать содержимое из файла Unicode, но данные содержат странные символы, и я не могу найти способ преобразовать данные в ASCII.
Поскольку вы упомянули WideCharToMultiByte, я предполагаю, что вы имеете дело с Windows.
"прочитать содержимое файла Unicode . найти способ конвертировать данные в ASCII"
Это может быть проблемой. Если вы конвертируете Unicode в ASCII (или другую устаревшую кодовую страницу), вы столкнетесь с риском повреждения / потери данных. Поскольку вы «работаете над сборкой выпуска Unicode», вам нужно читать Unicode и оставаться Unicode.
Таким образом, ваш последний буфер должен быть wchar_t (или WCHAR , или CStringW , то же самое).
Таким образом, ваш файл может быть utf-16 или utf-8 (utf-32 встречается довольно редко). Для utf-16 также может иметь значение порядок байтов. Если есть спецификация, это очень поможет.
- открыть файл с помощью wopen или _wfopen как двоичный
- прочтите первые байты, чтобы определить кодировку с помощью спецификации
- если кодировка - utf-8, прочитать массив байтов и преобразовать в wchar_t с помощью WideCharToMultiByte и CP_UTF8
- если используется кодировка utf-16be (big endian), прочитать в массиве wchar_t и _swab
- если кодировка - utf-16le (little endian), прочтите массив wchar_t и все готово
Кроме того (если вы используете более новую Visual Studio), вы можете воспользоваться расширением MS для _wfopen . Он может принимать кодировку как часть режима (что-то вроде _wfopen(L"newfile.txt", L"rw, ccs="); с кодировкой UTF-8 или UTF-16LE). Он также может определять кодировку на основе спецификации.
Предупреждение: быть кроссплатформенным проблематично, wchar_t может быть 2 или 4 байта, процедуры преобразования не переносимы .
Нам потребуется дополнительная информация, чтобы ответить на вопрос (например, вы пытаетесь прочитать файл Unicode в буфер char или буфер wchar_t ? Какую кодировку использует файл?), Но для теперь вы можете убедиться, что не сталкиваетесь с эта проблема, если ваш файл имеет кодировку Unicode и вы используете fgetws в текстовом режиме.
Когда функция ввода-вывода потока Unicode работает в текстовом режиме, предполагается, что исходный или целевой поток представляет собой последовательность многобайтовых символов. Следовательно, функции потокового ввода Unicode преобразуют многобайтовые символы в широкие символы (как если бы они вызывали функцию mbtowc). По той же причине функции потокового вывода Unicode преобразуют широкие символы в многобайтовые символы (как если бы при вызове функции wctomb).
Юникод - это преобразование числовых кодов в символы. Шаг перед Unicode - это кодирование файла: как преобразовать несколько последовательных байтов в числовой код? Вы должны проверить, хранится ли файл с прямым порядком байтов, прямым порядком байтов или чем-то еще.
Часто BOM (маркер порядка байтов) записывается как первые два байта в файле: либо FF FE, либо FE FF.
Предполагаемый способ обработки кодировок - позволить системе локали сделать это.
Перед открытием потока вы должны установить правильный языковой стандарт.
Кстати, вы помечаете свой вопрос C ++, вы писали о fgets и fgetws, но не о IOStreams; ваша проблема C ++ или C?
Тогда широкий ввод-вывод (wstream, fgetws) должен работать, если ваша среда правильно установлен для Unicode. В противном случае вам придется изменить свое окружение (я не как это работает под Windows, для Unix установка переменной LC_ALL - это способ, см. locale -a для поддерживаемых значений). В качестве альтернативы, заменив пустая строка по языку также будет работать, но тогда вы жестко закодируете locale в вашей программе, и ваши пользователи, возможно, не оценят это.
Если ваша система не поддерживает адекватный языковой стандарт, в C ++ есть возможность написать фасет для преобразования самостоятельно. Но это выходит за рамки этого ответа.
Вы НЕ МОЖЕТЕ надежно преобразовать Unicode, даже UTF-8, в ASCII. Наборы символов («плоскости» в документации Unicode) не отображаются обратно в ASCII - именно поэтому Unicode существует в первую очередь.
Во-первых: я предполагаю, что вы пытаетесь читать Unicode в кодировке UTF8 (поскольку вы можете читать некоторые символы). Вы можете проверить это, например, в Notpad ++
Для вашей проблемы - я бы предложил использовать какую-то библиотеку. Вы можете попробовать QT, QFile поддерживает Unicode (как и остальную часть библиотеки).
Открывает файл. Доступны более безопасные версии этих функций, которые выполняют дополнительные проверки параметров и возвращают коды ошибок. см. раздел fopen_s , _wfopen_s .
Remarks
fopen Функция открывает файл, указанный параметром filename. По умолчанию строка с узким именем интерпретируется с помощью кодовой страницы ANSI ( CP_ACP ). в Windows классических приложениях это можно изменить на кодовую страницу OEM ( CP_OEMCP ) с помощью SetFileApisToOEM функции. С помощью функции можно AreFileApisANSI определить, интерпретируется ли имя файла с помощью ANSI или системной кодовой страницы OEM по умолчанию. _wfopen — это двухбайтовая версия fopen ; аргументы для _wfopen представляют собой двухбайтовые строки. В противном случае поведение _wfopen и fopen идентично. Использование исключительно _wfopen не влияет на набор закодированных символов, используемый в файловом потоке.
fopen принимает пути, допустимые в файловой системе, в точке выполнения; fopen принимает UNC-пути и пути, содержащие сопоставленные сетевые диски, если выполняющая код система имеет доступ к общей папке или сопоставленному диску во время выполнения. При построении путей для fopen убедитесь, что драйверы, пути или сетевые общие папки будут доступны в среде выполнения. В пути в качестве разделителей каталогов можно использовать прямую (/) или обратную (\) косую черту.
Всегда проверяйте возвращаемое значение, чтобы узнать, равен ли указатель NULL, прежде чем выполнять какие-либо дальнейшие операции с файлом. При возникновении ошибки задается глобальная переменная errno , которая может использоваться для получения конкретных сведений об ошибке. Дополнительные сведения см errno . в статьях, _doserrno _sys_errlist , и _sys_nerr .
По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это, см. раздел глобальное состояние в CRT.
Универсальное текстовое сопоставление функций
Подпрограмма TCHAR.H | Не определено _UNICODE & _MBCS | _MBCS определено | _UNICODE определено |
---|---|---|---|
_tfopen | fopen | fopen | _wfopen |
Режим символьной строки определяет тип доступа, запрашиваемый для файла, как показано ниже.
mode | Доступ |
---|---|
Cерверный | Открывает для чтения. Если файл не существует или его невозможно найти, вызов fopen завершается ошибкой. |
Белая | Открывает пустой файл для записи. Если указанный файл существует, его содержимое удаляется. |
конкретного | Открывается для записи в конце файла (добавление) без удаления маркера в конце файла (EOF) перед записью новых данных в файл. Создает файл, если он не существует. |
"r +" | Открывает для чтения и записи. Файл должен существовать. |
"w +" | Открывает пустой файл для чтения и записи. Если файл существует, его содержимое удаляется. |
"a +" | Открывается для чтения и добавления. Операция добавления включает удаления маркера EOF перед записью новых данных в файл. Маркер EOF не восстанавливается по окончании записи. Создает файл, если он не существует. |
При открытии файла с помощью типа доступа "a" или типа доступа "a +" все операции записи выполняются в конце файла. Указатель файла может быть перемещен с помощью fseek или rewind , но он всегда возвращается в конец файла перед выполнением любой операции записи. Поэтому невозможно перезаписать существующие данные.
Режим "a" не УДАЛЯЕТ маркер EOF перед добавлением к файлу. После добавления команда MS-DOS TYPE отображает данные только до первоначального маркера EOF и не отображает данные, добавленные в файл. Перед добавлением в файл в режиме "a +" удаляется маркер EOF. После добавления команда TYPE MS-DOS отображает все данные в файле. Для добавления в потоковый файл, заканчивающийся маркером EOF CTRL + Z, требуется режим "a +" .
При указании типа доступа "r +", "w +"или "a +" разрешены операции чтения и записи (считается, что файл открыт для обновления). Однако при переходе от чтения к записи операция ввода должна получить маркер конца файла. Если маркер EOF отсутствует, необходимо воспользоваться промежуточным вызовом функции размещения файла. Функции размещения файла — fsetpos , fseek и rewind . При переходе от записи к чтению необходимо воспользоваться промежуточным вызовом функции fflush или функции размещения файла.
В дополнение к предыдущим значениям к режиму можно добавить следующие символы, чтобы указать режим преобразования для символов новой строки.
Модификатор режима | Режим преобразования |
---|---|
t | Откройте файл в текстовом (переведенном) режиме. |
b | Открыть в двоичном (непреобразованном) режиме; переводы, включающие символы возврата каретки и перевода строки, подавляются. |
В текстовом режиме CTRL + Z интерпретируется как символ EOF на входе. В файлах, открытых для чтения и записи с помощью "a +", FOPEN проверяет наличие CTRL + Z в конце файла и удаляет его, если это возможно. Это делается потому, что использование fseek и ftell для перемещения в файле, заканчивающемся клавишей CTRL + Z, может привести fseek к неправильному поведению в конце файла.
В текстовом режиме сочетания возврата каретки и перевода строки преобразуются в однострочные каналы ввода, а символы перевода строки преобразуются в сочетания символов возврата каретки и перевода строки на выходе. Если функция ввода-вывода потока Юникода работает в текстовом режиме (по умолчанию) исходный или конечный поток рассматривается как последовательность многобайтовых символов. Поэтому входные функции потока Юникода преобразуют многобайтовые символы в расширенные (как если бы для этого вызывалась функция mbtowc ). По той же причине выходные функции потока Юникода преобразуют расширенные символы в многобайтовые (как если бы для этого вызывалась функция wctomb ).
Если t или b не задан в режиме, то режим преобразования по умолчанию определяется глобальной переменной _fmode . Если t или b является префиксом аргумента, функция завершается ошибкой и возвращает значение NULL.
Дополнительные сведения об использовании текстового и двоичного режимов в Юникоде, а также многобайтового потока ввода-вывода см. в статьях Text and Binary Mode File I/O и Ввод-вывод в поток в кодировке Юникод в текстовом и двоичном режиме.
Следующие параметры могут быть добавлены в режим для указания дополнительных поведений.
Модификатор режима | Поведение |
---|---|
x | Вызывает сбой функции, если имя файла уже существует. Может использоваться только с описателями "w" или "w +". |
c | Включите флаг фиксации для связанного имени файла , чтобы содержимое файлового буфера записывалось непосредственно на диск при fflush вызове или _flushall . |
n | Сбросьте флаг фиксации для связанного файла с состоянием "без фиксации". Это значение по умолчанию. Оно также переопределяет глобальный флаг фиксации при соединении программы с COMMODE.OBJ. Значение по умолчанию глобального флага фиксации — без фиксации (no-commit), если только программа не связана явно с файлом COMMODE.OBJ (см. раздел Параметры ссылок). |
N | Указывает, что файл не наследуется дочерними процессами. |
S | Указывает, что кэширование оптимизировано для последовательного доступа с диска, но не ограничивается им. |
R | Указывает, что кэширование оптимизировано для случайного доступа с диска, но не ограничивается им. |
T | Определяет файл как временный. По возможности он не сбрасывается на диск. |
D | Определяет файл как временный. Он удаляется, если закрывается последний указатель файла. |
CCS =кодирование | Задает кодировку, используемую для этого файла (один из UTF-8, UTF-16LEили Unicode). Не указывайте никакое значение, если требуется использовать кодировку ANSI. |
Допустимые символы для строки режима , используемой в fopen и _fdopen , соответствуют аргументам офлаг , которые используются в _open и _sopen , как показано ниже.
Символы в строке режима | Эквивалентное значение офлаг для _open/_sopen |
---|---|
конкретного | _O_WRONLY | _O_APPEND (обычно _O_WRONLY | _O_CREAT | _O_APPEND) |
a + | _O_RDWR | _O_APPEND (обычно _O_RDWR | _O_APPEND | _O_CREAT ) |
r | _O_RDONLY |
r + | _O_RDWR |
w | _O_WRONLY (обычно _O_WRONLY | _O_CREAT | _O_TRUNC) |
w + | _O_RDWR (обычно _O_RDWR | _O_CREAT | _O_TRUNC) |
b | _O_BINARY |
t | _O_TEXT |
x | _O_EXCL |
c | None |
n | None |
S | _O_SEQUENTIAL |
R | _O_RANDOM |
T | _O_SHORTLIVED |
D | _O_TEMPORARY |
CCS = Юникод | _O_WTEXT |
CCS = UTF-8 | _O_UTF8 |
CCS = UTF-16LE | _O_UTF16 |
Если используется режим RB , вам не нужно переносить код, и если предполагается читать большую часть большого файла или не беспокоиться о производительности сети, можно также подумать, следует ли использовать файлы Win32, сопоставленные с памятью, в качестве параметра.
Generic-Text Routine Mappings
TCHAR.H routine | _UNICODE & _MBCS not defined | _MBCS defined | _UNICODE defined |
---|---|---|---|
_tfopen_s | fopen_s | fopen_s | _wfopen_s |
The character string mode specifies the kind of access that's requested for the file, as follows.
mode | Access |
---|---|
"r" | Opens for reading. If the file doesn't exist or cannot be found, the fopen_s call fails. |
"w" | Opens an empty file for writing. If the given file exists, its contents are destroyed. |
"a" | Opens for writing at the end of the file (appending) without removing the end-of-file (EOF) marker before new data is written to the file. Creates the file if it doesn't exist. |
"r+" | Opens for both reading and writing. The file must exist. |
"w+" | Opens an empty file for both reading and writing. If the file exists, its contents are destroyed. |
"a+" | Opens for reading and appending. The appending operation includes the removal of the EOF marker before new data is written to the file. The EOF marker isn't restored after writing is completed. Creates the file if it doesn't exist. |
When a file is opened by using the "a" or "a+" access type, all write operations occur at the end of the file. The file pointer can be repositioned by using fseek or rewind , but it's always moved back to the end of the file before any write operation is carried out so that existing data cannot be overwritten.
The "a" mode doesn't remove the EOF marker before appending to the file. After appending has occurred, the MS-DOS TYPE command only shows data up to the original EOF marker and not any data that's appended to the file. The "a+" mode does remove the EOF marker before appending to the file. After appending, the MS-DOS TYPE command shows all data in the file. The "a+" mode is required for appending to a stream file that is terminated with the CTRL+Z EOF marker.
When the "r+" , "w+" , or "a+" access type is specified, both reading and writing are allowed. (The file is said to be open for "update".) However, when you switch from reading to writing, the input operation must come across an EOF marker. If there's no EOF marker, you must use an intervening call to a file-positioning function. The file-positioning functions are fsetpos , fseek , and rewind . When you switch from writing to reading, you must use an intervening call to either fflush or to a file-positioning function.
Starting in C11, you can append "x" to "w" or "w+" to cause the function fail if the file exists, instead of overwriting it.
In addition to the values above, the following characters can be included in mode to specify the translation mode for newline characters:
mode modifier | Translation mode |
---|---|
t | Open in text (translated) mode. |
b | Open in binary (untranslated) mode; translations involving carriage-return and line feed characters are suppressed. |
In text (translated) mode, CTRL+Z is interpreted as an end-of-file character on input. In files opened for reading/writing with "a+" , fopen_s checks for a CTRL+Z at the end of the file and removes it, if possible. This is done because using fseek and ftell to move within a file that ends with a CTRL+Z, may cause fseek to behave improperly near the end of the file.
Also, in text mode, carriage return/line feed combinations are translated into single line feeds on input, and line feed characters are translated to carriage return-line feed combinations on output. When a Unicode stream-I/O function operates in text mode (the default), the source or destination stream is assumed to be a sequence of multibyte characters. The Unicode stream-input functions convert multibyte characters to wide characters (as if by a call to the mbtowc function). For the same reason, the Unicode stream-output functions convert wide characters to multibyte characters (as if by a call to the wctomb function).
If t or b isn't given in mode , the default translation mode is defined by the global variable _fmode. If t or b is prefixed to the argument, the function fails and returns NULL .
For more information about using text and binary modes in Unicode and multibyte stream-I/O, see Text and Binary Mode File I/O and Unicode Stream I/O in Text and Binary Modes.
mode modifier | Behavior |
---|---|
c | Enable the commit flag for the associated filename so that the contents of the file buffer are written directly to disk if either fflush or _flushall is called. |
n | Reset the commit flag for the associated filename to "no-commit." This is the default. It also overrides the global commit flag if you link your program with COMMODE.OBJ. The global commit flag default is "no-commit" unless you explicitly link your program with COMMODE.OBJ (see Link Options). |
n | Specifies that the file isn't inherited by child processes. |
S | Specifies that caching is optimized for, but not restricted to, sequential access from disk. |
R | Specifies that caching is optimized for, but not restricted to, random access from disk. |
t | Specifies a file as temporary. If possible, it isn't flushed to disk. |
D | Specifies a file as temporary. It's deleted when the last file pointer is closed. |
ccs= encoding | Specifies the encoded character set to use (one of UTF-8 , UTF-16LE , or UNICODE ) for this file. Leave unspecified if you want ANSI encoding. |
Valid characters for the mode string used in fopen_s and _fdopen correspond to oflag arguments used in _open and _sopen , as follows.
Characters in mode string | Equivalent oflag value for _open / _sopen |
---|---|
a | _O_WRONLY | _O_APPEND (usually _O_WRONLY | _O_CREAT | _O_APPEND ) |
a+ | _O_RDWR | _O_APPEND (usually _O_RDWR | _O_APPEND | _O_CREAT ) |
R | _O_RDONLY |
r+ | _O_RDWR |
w | _O_WRONLY (usually _O_WRONLY | _O_CREAT | _O_TRUNC) |
w+ | _O_RDWR (usually _O_RDWR | _O_CREAT | _O_TRUNC) |
b | _O_BINARY |
t | _O_TEXT |
c | None |
n | None |
S | _O_SEQUENTIAL |
R | _O_RANDOM |
t | _O_SHORTLIVED |
D | _O_TEMPORARY |
ccs=UNICODE | _O_WTEXT |
ccs=UTF-8 | _O_UTF8 |
ccs=UTF-16LE | _O_UTF16 |
If you're using rb mode, memory mapped Win32 files might also be an option if you don't need to port your code, you expect to read much of the file, or you don't care about network performance.
Libraries
The c , n , and t mode options are Microsoft extensions for fopen_s and _fdopen and shouldn't be used where you want ANSI portability.
Всем рано или поздно приходится работать с различными кодировками. Заметив в коде своей команды различные, порой странные, подходы к решению этих проблем, пришлось провести разъяснительную беседу. Ниже поделюсь своим видением правильной работы с не-ASCII символами в коде. Буду рад конструктивной критике.
Error Conditions
pFile | filename | mode | Return Value | Contents of pFile |
---|---|---|---|---|
NULL | any | any | EINVAL | unchanged |
any | NULL | any | EINVAL | unchanged |
any | any | NULL | EINVAL | unchanged |
Remarks
Files that are opened by fopen_s and _wfopen_s aren't sharable. If you require that a file be sharable, use _fsopen , _wfsopen with the appropriate sharing mode constant—for example, _SH_DENYNO for read/write sharing.
The fopen_s function opens the file that's specified by filename. _wfopen_s is a wide-character version of fopen_s ; the arguments to _wfopen_s are wide-character strings. _wfopen_s and fopen_s behave identically otherwise.
fopen_s accepts paths that are valid on the file system at the point of execution; UNC paths and paths that involve mapped network drives are accepted by fopen_s as long as the system that's executing the code has access to the share or mapped network drive at the time of execution. When you construct paths for fopen_s , don't make assumptions about the availability of drives, paths, or network shares in the execution environment. You can use either forward slashes (/) or backslashes (\) as the directory separators in a path.
These functions validate their parameters. If pFile , filename , or mode is a null pointer, these functions generate an invalid parameter exception, as described in Parameter Validation.
Always check the return value to see if the function succeeded before you do any further operations on the file. If an error occurs, the error code is returned and the global variable errno is set. For more information, see errno , _doserrno , _sys_errlist , and _sys_nerr .
By default, this function's global state is scoped to the application. To change this, see Global state in the CRT.
Кодировка исходников
Прежде чем приступить к рассмотрению примера, следует (на всякий случай) пару слов сказать о кодировке исходных текстов программы. Все свои исходники я держу в UTF-8 (если они содержат широкие строковые константы с не-ASCII символами, то в файлы добавляю BOM), что и всем советую. Современные компиляторы сами преобразуют «широкие» символы, помеченные в исходниках как L"" в UCS-2 (или UCS-4). Понятно, что правильное преобразование зависит от кодировки исходников. gcc по-умолчанию считает, что работает с UTF-8 текстом, чтобы его переубедить придётся специально указывать значение параметра -finput-charset. Компилятору от MS нужно немножечко помочь — добавить в UTF-8 файл BOM (Byte Order Mark). К сожалению у Borland C++ Compiler version 5.5 с UTF-8 проблемы.
Для тех, кто собрался кинуть в меня камень поясню два момента: первый — мне не удобно читать код с «unicode escape» типа:
std::wstring wstr(L"\u0410\u0411\u0412\u0413\u0413");
второй — речь идёт не только об интерфейсе пользователя, поэтому вынести все широкие строковые константы в отдельный модуль и как-то с ними работать (наподобие gettext), не вариант.
Итак решено — исходники в UTF-8 с BOM. Если кто не знает, в vim BOM можно добавить к файлу с помощью команды «set bomb». Если BOM в файле уже есть vim его никуда не денет.
Поддержка Юникода
fopen поддерживает файловые потоки Юникода. Чтобы открыть файл Юникода, передайте флаг CCS , который задает нужную кодировку fopen , следующим образом.
File * FP = fopen ("newfile.txt", "RT +, CCS =Encoding");
Допустимые значения кодировки : Unicode, UTF-8и UTF-16LE.
Если файл открывается в режиме Юникода, функции ввода преобразуют данные, считываемые из файла в данные UTF-16, хранимые с типом wchar_t . Затем функции, которые выполняют запись в файл, открытый в режиме Юникода, ожидают буферы, содержащие данные UTF-16, хранимые с типом wchar_t . Если кодировка файла — UTF-8, при его записи данные UTF-16 преобразуются в UTF-8, а содержимое файла с кодировкой UTF-8 преобразуется в данные UTF-16 при его считывании. Попытка чтения или записи нечетного числа байтов в режиме Юникода приводит к ошибке проверки параметров . Для чтения или записи данных, хранимых в программе в кодировке UTF-8, используйте режим текстового или двоичного файла вместо режима Юникода. Вам необходимо реализовать все обязательные преобразования кодировки.
Если файл уже существует и открыт для чтения или добавления, метка порядка байтов (BOM), если она присутствует в файле, определяет кодирование. Кодировка спецификации имеет приоритет над кодировкой, заданной флагом CCS . Кодировка CCS используется только в том случае, если отсутствует спецификация или файл является новым файлом.
Обнаружение спецификации применяется только к файлам, открываемым в режиме Юникода (то есть путем передачи флага CCS ).
В следующей таблице перечислены режимы, используемые для различных флагов CCS , заданных в fopen и метках порядка байтов в файле.
Пример 1
Следующая программа открывает два файла. Она использует fclose для закрытия первого файла и _fcloseall для закрытия всех остальных файлов.
Unicode support
fopen_s supports Unicode file streams. To open a new or existing Unicode file, pass a ccs flag that specifies the desired encoding to fopen_s :
Allowed values of encoding are UNICODE , UTF-8 , and UTF-16LE . If there no value is specified for encoding , fopen_s uses ANSI encoding.
If the file already exists and is opened for reading or appending, the Byte Order Mark (BOM), if present in the file, determines the encoding. The BOM encoding takes precedence over the encoding that's specified by the ccs flag. The ccs encoding is only used when no BOM is present or if the file is a new file.
BOM-detection only applies to files that are opened in Unicode mode; that is, by passing the ccs flag.
The following table summarizes the modes for various ccs flags that are given to fopen_s and for Byte Order Marks in the file.
Encodings Used Based on ccs Flag and BOM
ccs flag | No BOM (or new file) | BOM: UTF-8 | BOM: UTF-16 |
---|---|---|---|
UNICODE | UTF-8 | UTF-8 | UTF-16LE |
UTF-8 | UTF-8 | UTF-8 | UTF-16LE |
UTF-16LE | UTF-16LE | UTF-8 | UTF-16LE |
Files that are opened for writing in Unicode mode have a BOM written to them automatically.
If mode is "a, ccs= encoding " , fopen_s first tries to open the file with both read access and write access. If successful, the function reads the BOM to determine the encoding for the file; if unsuccessful, the function uses the default encoding for the file. In either case, fopen_s then reopens the file with write-only access. (This applies to a mode only, not a+ .)
Requirements
Function | Required header |
---|---|
fopen_s | |
_wfopen_s | or |
For more compatibility information, see Compatibility.
Пример работы с различными кодировками
Ну вот и подобрались к самому интересному. Как я и говорил, код простой и понятный. Маленькое замечание по стандартным потокам — по умолчанию фасеты для них не задействуются так как они синхронизированы с stdio для производительности. Следует указать sync_with_stdio(false).
Фасет codecvt для преобразования кириллицы из cp1251 в ucs-2 и обратно
Пример 2
Следующая программа создает файл (или перезаписывает его, если имеется) в текстовом режиме с кодировкой Юникода. Затем она записывает две строки в файл и закрывает его. Выходные данные — это файл с именем _wfopen_test.xml, который содержит данные из раздела Output.
Opens a file. These versions of fopen , _wfopen have security enhancements, as described in Security Features in the CRT.
Syntax
Возвращаемое значение
Каждая из этих функций возвращает указатель на открытый файл. Значение указателя null обозначает ошибку. Если параметр filename или mode имеет значение NULL или является пустой строкой, эти функции активируют обработчик недопустимых параметров, который описан в разделе Проверка параметров. Если выполнение может быть продолжено, эти функции возвращают значение NULL и задают значениееинвал.
Принцип работы
Логика работы с различными кодировками в C++ проста и прозрачна. В общем виде она отражена в схеме. Программа работает в одной — своей внутренней кодировке, а правильно локализованные потоки отвечают за преобразование кодировки данных из внешнего кода во внутренний и обратно. Внутреннюю кодировку программы лучше всего зафиксировать раз и навсегда. Если программа работает с не-ASCII символами, то самый логичный выбор для внутренней кодировки — Юникод, причём использование UTF-8 и char для параметризации STL как правило неоправданно (хотя существуют ситуации, в которых это необходимо); более логично перейти на расширенные символы wchar_t и использовать UCS-2. За преобразование данных из внешней кодировки во внутреннюю отвечает фасет codecvt. Локализованные потоки сами вызовут соответствующие функции фасета при получении данных (о том кто такие фасеты я писал ранее).
Вышесказанное поясню комментированным примером, в котором прочитаем данные из cp1251 файла, покажем как boost::xpressive работает с юникодом и выведем кириллицу в cout в кодировке cp866 (консоль windows по умолчанию).
Примечания
Подробней о UNICODE модно почитать тут
Подробней о фасетах — в книге Страуструпа «Язык программирования C++», третье специальное издание, приложение
Подробней о boost::xpressive тут
UPD 20150813:
Хабрапользователь Cyapa провел расследование и выяснил, что фасет codecvt работает только с потоками, работающими с bisic_filebuf. Согласно стандарту (п. 27.9.1.1, кроме того это отмечено у Страуструпа) реализации не обязаны вызывать методы фасета codecvt для других буферов — в частности basic_stringbuf. Таким образом если вы создали локаль со своим фасетом codecvt и надеетесь, что std::stringstream, которому назначена эта локаль (с помощью imbue) будет дергать этот фасет, вы ошибаетесь.
В учебнике Лафоре на стр.551-553 рассказано про форматированный вывод данных в файл, получение данных из файла.
Даются две небольшие программы. Первая выводит в файл символ, целое и вещественное числа, две строки и сообщает об окончании вывода (в консоль). Вторая — читает из созданного первой программой файла данные и выводит их на экран (в консоль).
Привычно (подробности — тут) переделываю эти программы вместо работы с обычными символами (char) для работы с широкими символами (wchar_t), чтобы можно было работать в программе с Юникодом (в том числе с русскими буквами). То есть тип char меняем на wchar_t, string на wstring, ofstream на wofstream, ifstream на wifstream, cout на wcout, символьные и строковые литералы предваряем префиксом L.
Кстати, чтобы смена cout на wcout сработала, в Windows требуется переключить стандартный поток вывода в формат Юникода (в Линуксе это делается по-другому) с помощью функции _setmode, которая требует подключения заголовочных файлов и .
Как оказалось, для вывода в файл (чтения из файла) широких символов недостаточно использования классов, работающих с широкими символами — wofstream и wifstream (я ожидал, что запись в файл будет произведена в кодировке UTF-16, исходя из того, что символы в Windows хранятся в кодировке UCS-2 (подмножество UTF-16), но этого не случилось).
Для правильного вывода русских букв в файл в данном случае требуется для текущей локали (объект соответствующего класса locale, содержащий набор параметров, определяющих региональные настройки) правильно настроить ее фасет класса codecvt (набор параметров локали поделен на отдельные разделы, называемые фасетами), регулирующий преобразование символов из одной кодировки в другую.
Раз уж итоговую кодировку текстового файла, в который будем записывать данные, всё равно нужно будет указывать, то выбираю кодировку UTF-8, как самую популярную на сегодня для текстовых файлов. Для этой кодировки имеется специальный фасет класса codecvt_utf8.
Для использования указанных классов в программу должны быть включены соответствующие заголовочные файлы и .
Итак, в начале функции main обеих программ создадим константу, содержащую локаль с нужным фасетом для преобразования символов при сохранении в файл в кодировке UTF-8:
А после создания объекта-потока нужного класса свяжем наш поток с определенной ранее локалью, воспользовавшись методом ios::imbue:
для второй программы:
После этого обе программы должны работать правильно, а информация будет храниться в текстовом файле в кодировке UTF-8.
Синтаксис
Parameters
pFile
A pointer to the file pointer that will receive the pointer to the opened file.
mode
Type of access permitted.
Кодирования, используемые на основе CCS-флага и метки BOM
Флаг CCS | Нет метки BOM (или новый файл) | BOM: UTF-8 | BOM: UTF-16 |
---|---|---|---|
UNICODE | UTF-16LE | UTF-8 | UTF-16LE |
UTF-8 | UTF-8 | UTF-8 | UTF-16LE |
UTF-16LE | UTF-16LE | UTF-8 | UTF-16LE |
В файлы, открытые для записи в режиме Юникода, метка BOM записывается автоматически.
Если mode имеет значение, CCS =Encoding, fopen сначала пытается открыть файл, используя доступ на чтение и запись. Если это завершается успешно, функция считывает метку BOM, чтобы определить кодировку для файла; если операция завершается сбоем, функция использует для файла кодировку по умолчанию. В любом случае fopen затем снова открывает файл с правами только на запись. (Это относится только к режиму " a" , а не к режиму "a +" .)
Требования
Компонент | Обязательный заголовок |
---|---|
fopen | |
_wfopen | либо |
_wfopen является расширением Майкрософт. Дополнительные сведения о совместимости см. в разделе Совместимость.
Параметрырежимовc, n, t, S, R, tи Dявляются расширениями Майкрософт для fopen и _fdopen и не должны использоваться, если требуется переносимость ANSI.
Return Value
Zero if successful; an error code on failure. For more information about these error codes, see errno , _doserrno , _sys_errlist , and _sys_nerr .
Параметры
mode
Включенный тип доступа.
Читайте также: