Какой смысл использовать std::ios_base::binary?

У меня была проблема с чтением файлов Linux под Windows. Вот обсуждение проблемы: Использование fstream ::seekg под окнами в файле, созданном в Unix.

Проблема была устранена путем открытия текстового файла с указанным std::ios_base::binary.

Но в чем смысл этого режима? Если указано, вы все еще можете работать с файлом как с текстовым файлом (запись с помощью mystream << "Hello World" << std::endl и чтение с помощью std::getline).

Под Windows единственная разница, которую я мог заметить, заключается в том, что mystream << "Hello World" << std::endl использует:

  • 0x0D 0x0A в качестве разделителя строк, если std::ios_base::binary не был указан (EOL и возврат каретки)
  • 0x0A в качестве разделителя строк, если было указано std::ios_base::binary (только EOL)

Блокнот некорректно отображает строки при открытии файлов, созданных с помощью std::ios_base::binary. Лучшие редакторы, такие как vi или Wordpad, их показывают.

Это действительно единственная разница между файлами, сгенерированными с std::ios_base::binary и без него? В документации написано Consider stream as binary rather than text., что это значит в итоге?

Безопасно ли всегда устанавливать std::ios_base::binary, если мне не нужно открывать файл в Блокноте и я хочу, чтобы fstream::seekg всегда работало?


person jpo38    schedule 18.11.2014    source источник
comment
Я бы также проверил с \0 символами в строках. В двоичном виде они, вероятно, просто выводятся, в то время как для недвоичного они могут быть интерпретированы как ограничитель строки.   -  person Gábor Bakos    schedule 18.11.2014
comment
@GáborBakos Если речь идет только о системах Unix и Windows, то единственными отличиями являются окончания строк и 0x1A, которые Windows рассматривает как символ конца файла, по крайней мере, при вводе.   -  person James Kanze    schedule 18.11.2014


Ответы (3)


Различия между двоичным и текстовым режимами определяются реализацией, но касаются только самого нижнего уровня: они не меняют значения таких вещей, как << и >> (которые вставляют и извлекают текстовые данные). Кроме того, формально вывод всех, кроме нескольких непечатаемых символов (например, '\n'), является неопределенным поведением, если файл находится в текстовом режиме.

Для наиболее распространенных ОС: под Unix разницы нет; оба идентичны. В Windows '\n' внутри будет отображаться на двухсимвольную последовательность CR, LF (0x0D, 0x0A) извне, а 0x1A будет интерпретироваться как конец файла при чтении. Однако в более экзотических (и в основном вымерших) ОС они могли быть представлены совершенно другими типами файлов на уровне ОС, и было невозможно прочитать файл в текстовом режиме, если он был записан в двоичном режиме, и наоборот. Или вы могли увидеть что-то другое: лишний пробел в конце строки или отсутствие '\n' в двоичном режиме.

Что касается всегда установки std::ios_base::binary: моя политика для переносимых файлов заключается в том, чтобы решить, как именно я хочу их отформатировать, установить двоичный код и вывести то, что я хочу. Часто это CR, LF, а не просто LF, поскольку это сетевой стандарт. С другой стороны, большинство программ для Windows не имеют проблем только с LF, но я сталкивался с несколькими программами для Unix, у которых есть проблемы с CR, LF; который выступает за систематическое использование только LF (что тоже проще). Такой подход означает, что я получаю одинаковые результаты независимо от того, работаю ли я под Unix или под Windows.

person James Kanze    schedule 18.11.2014
comment
Хорошо ли я понимаю, что установка std::ios_base::binary или нет для файла чтение не имеет значения (за исключением исправления упомянутой выше ошибки), а установка std::ios_base::binary или нет для файла запись может привести к различиям, основанным на на платформе? - person jpo38; 18.11.2014
comment
@ jpo38 Нет. Выбор между двоичным и текстовым файлами влияет как на чтение, так и на запись: в Windows при чтении CR, LF будут сопоставлены с LF, а 0x1A приведет к остановке чтения. А на некоторых экзотических системах может не открыться в бинарном режиме, если файл был записан в тексте, или наоборот. - person James Kanze; 18.11.2014
comment
@JamesKanze - я думаю, другие прокомментировали, что (по крайней мере, в MacOS) операторы потока ИГНОРИРУЮТ двоичный режим, если он у вас установлен: это означает, что если вы используете ›› (оператор форматирования извлечения) для чтения двоичных данных из потока, вы увидите расширение/преобразование CR,LF, даже если вы НЕ ожидали этого в двоичном режиме! Я выследил сложные ошибки формата двоичных файлов, появившиеся из-за использования ››. Эти проблемы были легко решены с помощью простого read(). - person SMGreenfield; 19.06.2021

Я обнаружил (потеряв два часа работы, пытаясь понять, что происходит) ситуацию, когда указание std::ios_base::binary действительно имеет огромное значение.

std::vector<char> data{ 0x01, 0x02, 0x0A, 0x0B };
{
    std::fstream tfat;
    tfat.open( "binary", std::ios_base::out | std::ios_base::binary );
    tfat.write( &(data[0]), data.size() );
    tfat.close();
}
{
    std::fstream tfat;
    tfat.open( "not_binary", std::ios_base::out );
    tfat.write( &(data[0]), data.size() );
    tfat.close();
}

Тогда «бинарный» файл содержит 4 байта: 0x01, 0x02, 0x0A, 0x0B Но «не_бинарный» файл содержит 5 байтов: 0x01, 0x02, 0x0D, 0x0A, 0x0B

0x0D (\r) был вставлен перед 0x0A (\n). Пока я пишу 4 байта, я ожидал, что в итоге в файле будет 4 байта.

Так что это заставляет меня понять, почему std::ios_base::binary необходимо использовать при записи данных в файл, даже если не используется оператор <<.

person jpo38    schedule 10.04.2020

Значение текстового потока и двоичного потока зависит от платформы и несколько непредсказуемо.

Но что касается популярных платформ, тут все просто: в Linux и MacOS X нет никакой разницы. В Windows единственное отличие состоит в том, что внутренний \n преобразуется во внешний поток в \r\n.

person Sebastian Redl    schedule 18.11.2014
comment
В Windows 0x1A будет рассматриваться как конец файла в текстовом режиме. - person James Kanze; 18.11.2014