Запись бинарных файлов с использованием C ++: имеет ли значение локаль по умолчанию? - PullRequest
8 голосов
/ 02 декабря 2009

У меня есть код, который манипулирует двоичными файлами, используя fstream с установленным двоичным флагом и используя неформатированные функции ввода-вывода для чтения и записи. Это работает правильно на всех системах, которые я когда-либо использовал (биты в файле точно такие же, как и ожидалось), но в основном это все английские США. Мне было интересно, что эти байты могут быть изменены с помощью codecvt в другой системе.

Похоже, стандарт говорит, что использование неформатированного ввода-вывода ведет себя так же, как ввод символов в потоковый буфер с помощью sputc / sgetc. Это приведет к вызову функций переполнения или потери в потоке, и это звучит так, как будто это приводит к тому, что материал проходит через некоторый код (например, см. 27.8.1.4.3 в стандарте c ++). Для basic_filebuf создание этого codecvt определено в 27.8.1.1.5. Это выглядит так, как будто результаты будут зависеть от того, что возвращает basic_filebuf.getloc ().

Итак, мой вопрос, могу ли я предположить, что массив символов, записанный с использованием ofstream.write в одной системе, может быть дословно восстановлен с помощью ifstream.read в другой системе, независимо от того, какую конфигурацию локали любой человек может использовать в своей системе ? Я бы сделал следующие предположения:

  1. Программа использует по умолчанию локаль (то есть программа не изменение настроек локали на все).
  2. Обе системы имеют CHAR_BIT 8, имеют одинаковый битовый порядок в каждом байте, сохраняют файлы в виде октетов и т. Д.
  3. У объектов потока установлен двоичный флаг.
  4. Нам не нужно беспокоиться о каких-либо различиях в порядке байтов на этом этапе. Если какие-либо байты в массиве следует интерпретировать как многобайтовое значение, преобразования с порядком байтов будут обрабатываться в соответствии с требованиями на более позднем этапе.

Если языковой стандарт по умолчанию не гарантирует прохождения через все это без изменений в какой-либо конфигурации системы (я не знаю, арабский или что-то в этом роде), то каков наилучший способ записи бинарных файлов с использованием C ++?

Ответы [ 3 ]

1 голос
/ 03 декабря 2009

Если у вас установлен двоичный флаг, все, что вы пишете, будет записано в файл дословно. Нет конверсий. Как вы интерпретируете байты, зависит от вас (и, возможно, от локали).

Еще одна вещь: есть вероятность поломки в разных местах. Например, если ваш источник данных создал двоичные данные в зависимости от локали (и формат этих данных будет меняться в зависимости от локали - это плохая идея, кстати). Это может вызвать проблемы при загрузке данных на машины с другим языком. Это ошибка дизайна.

Если вы просто используете стандартные типы данных / структуры, которые имеют одинаковый формат / формат независимо от того, в какой локали они были созданы во всем, все должно быть в порядке.

1 голос
/ 04 декабря 2009

Спасибо за помощь. Я просто подумал, что было бы полезно опубликовать дополнительную информацию об этом, которая не помещалась бы в комментарии.

Стандартным языком для программ на C ++ всегда является языковой стандарт "C" (http://www.cplusplus.com/reference/clibrary/clocale/setlocale/). Если это единственный языковой стандарт, используемый в вашей программе, это означает, что его поведение не зависит от конкретной конфигурации языкового стандарта компьютера. это также означает, что неформатированный ввод / вывод для символа не подвергается никакому преобразованию кода (хотя wchar_t может быть другой историей). Это означает, что (учитывая предположения в вопросе) чтение и запись должны позволять двоичные данные подлежит восстановлению без изменений.

(из чтения документации) Вы можете глобально установить языковой стандарт приложения в соответствии с системным значением по умолчанию, вызвав setlocale (LC_ALL, ""), что будет означать, что потоки, построенные из этой точки, будут использовать системный языковой стандарт по умолчанию. Чтобы установить его обратно в локаль "C", вы можете вызвать setlocale (LC_ALL, "C"), что будет означать, что именно это будут использовать потоки, созданные в будущем. Вы также можете указать, что local "C" должен использоваться для потока, который уже создан, вызывая stream.imbue (locale :: classic ()).

0 голосов
/ 02 декабря 2009

В Windows все должно быть хорошо, но в других ОС вы должны также проверять окончания строк (как безопасность). Языковой стандарт C / C ++ по умолчанию - «C», который не зависит от языкового стандарта системы.

Это не гарантия. Как вы знаете, компилятор C / C ++ и его целевые машины сильно различаются. Итак, вы ждете неприятностей, если сохраните все эти предположения. Затраты на изменение локали незначительны, если вы не пытаетесь делать это сотни раз в секунду.

...