Как передать бинарный файл
Qt5 - файловая операция (2) чтение и запись двоичных файлов
В предыдущем разделе мы познакомились с использованием QFile и QFileInfo. QIODevice предоставляет базовые операции, такие как read () и readLine (). В то же время Qt также обеспечивает более высокий уровень операций: дляДвоичный поток QDataStreamИ используется дляQTextStream для текстового потока. В этом разделе мы объясним использование QDataStream и некоторые методы. Следующий раздел связан с QTextStream.
QDataStream обеспечивает сериализацию двоичных данных на основе QIODevice. Поток данных - это двоичный поток, который вообще не зависит от базовой операционной системы, процессора или порядка байтов (прямой или прямой порядок байтов). Например, поток данных, записанный на ПК с платформой Windows, может быть прочитан непосредственно на машине SPARC под управлением Solaris без какой-либо обработки. Поскольку поток данных является двоичным потоком, мы также можем напрямую читать и записывать некодированные двоичные данные, такие как изображения, видео, аудио и т. Д.
QDataStream может обращаться к базовым типам C ++, таким как int, char, short и т. Д., А также к сложным типам данных, таким как пользовательские классы. Фактически, QDataStream делит сложный класс на множество базовых единиц для хранения классов.
В сочетании с QIODevice QDataStream может легко читать и записывать файлы, сетевые сокеты и т. Д. Начиная с кода:
В этом коде мы сначала открываем файл с именем file.dat (обратите внимание, что для простоты мы не проверяли, был ли файл открыт успешно, что недопустимо в формальной программе). Затем мы передаем указатель только что созданного файлового объекта экземпляру QDataStream. Подобно стандартному потоку вывода std :: cout, QDataStream также перегружает оператор перенаправления вывода <<. Код очень прост: выведите «ответ» и число 42 в поток данных.
Следует отметить: для чтения и записи лучше использовать целочисленные типы Qt, такие как qint32 в программе. Это гарантирует одинаковое поведение на любой платформе и любом компиляторе.
Давайте рассмотрим пример, чтобы увидеть, как Qt хранит данные. Например, строка char * при сохранении сначала сохраняет длину строки, включая терминатор \ 0 (32-битное целое число), затем содержимое строки и терминатор \ 0. При чтении сначала прочтите всю длину как 32-битное целое число, а затем извлеките содержимое всей строки в соответствии с этой длиной.
Однако, если вы запустите этот код напрямую, вы получите пустой файл file.dat без записанных данных. Это потому, что наш файл обычно не закрывается. По соображениям производительности данные фактически записываются только при закрытии файла. Следовательно, мы должны добавить строку кода в конце:
Прочтите этот ответ:
Про этот код сказать нечего. Единственное, что следует отметить, это то, что вы должны читать данные в порядке записи. Другими словами, последовательность записи данных программы должна быть определена заранее. В этом примере мы сначала записываем строку, а затем записываем число, затем сначала считываем строку, а затем число. Если порядок обратный, поведение программы будет неопределенным, и это приведет к прямому сбою программы в тяжелых случаях.
Поскольку двоичный поток представляет собой чистые байтовые данные, проблема в том, что если разные версии программы читаются по-разному (как упоминалось ранее, Qt гарантирует согласованность чтения и записи содержимого, но не гарантирует, что между разными версиями Qt Согласованность ), данные будут неверными. Следовательно, мы должны предоставить механизм для обеспечения согласованности между различными версиями. Обычно мы будем использовать следующий код для написания:
Здесь мы добавили две строки кода:
Используется для написания магических чисел. Так называемое магическое число - это метод, часто используемый для двоичного вывода. Бинарный формат не читается людьми и обычно имеет один и тот же суффикс (например, dat), поэтому у нас нет способа отличить два бинарных файла, которые являются законными. Таким образом, у двоичного формата, который мы определяем, обычно есть магический номер, определяющий законность файла. В этом примере мы пишем 0xA0B0C0D0 в начале файла и сначала проверяем, равно ли это число 0xA0B0C0D0 при чтении. Если это не так, это означает, что файл не в распознаваемом формате, поэтому нет необходимости продолжать чтение. Обычно двоичные файлы имеют такое магическое число.Например, магическое число файлов классов Java - 0xCAFEBABE, которое можно просмотреть с помощью программы просмотра двоичных файлов. Магическое число - это 32-битное целое число без знака, поэтому мы используем quint32 для получения независимого от платформы 32-битного целого числа без знака.
Версия файла логотипа. Мы используем магические числа, чтобы определить тип файла, чтобы определить, является ли файл законным. Однако между разными версиями файла также могут быть различия: мы можем сохранить целое число в первой версии, а вторая версия может сохранить строку. Чтобы идентифицировать разные версии, мы можем только записать версию в файл. Например, наша версия сейчас 123.
Следующая строка посвящена версии:
В приведенном выше предложении указан номер версии файла, но метод чтения в разных версиях Qt может отличаться. Таким образом, мы должны указать, какую версию Qt читать. Здесь мы указываем читать содержимое в формате Qt 5.9.
После того, как мы напишем такой файл, нам нужно добавить ряд суждений при чтении:
Этот код выполняется согласно предыдущему объяснению. Сначала прочтите магическое число, чтобы проверить, легален ли файл. Если это допустимо, прочтите версию файла: меньше 100 или больше 123 не поддерживаются. Если он находится в диапазоне поддерживаемых версий (100 <= версия <= 123), когда он меньше или равен 110, читать в формате Qt_4_0, в противном случае читать в формате Qt_5_9. После установки этих параметров начинайте чтение данных.
Пока что мы представили связанный контент о QDataStream. Итак, поскольку QIODevice предоставляет такие функции, как read (), readLine (), почему существует QDataStream? В чем разница между QDataStream и QIODevice? Разница в том, что QDataStream предоставляет формат потока, и его производительность обычно лучше, чем при прямом вызове исходного API. Давайте посмотрим, в какой форме проходит следующий фрагмент кода:
Т екстовые файлы хранят данные в виде текста (sic!). Это значит, что если, например, мы записываем целое число 12345678 в файл, то записывается 8 символов, а это 8 байт данных, несмотря на то, что число помещается в целый тип. Кроме того, вывод и ввод данных является форматированным, то есть каждый раз, когда мы считываем число из файла или записываем в файл происходит трансформация числа в строку или обратно. Это затратные операции, которых можно избежать.
Текстовые файлы позволяют хранить информацию в виде, понятном для человека. Можно, однако, хранить данные непосредственно в бинарном виде. Для этих целей используются бинарные файлы.
Выполните программу и посмотрите содержимое файла output.bin. Число, которое ввёл пользователь записывается в файл непосредственно в бинарном виде. Можете открыть файл в любом редакторе, поддерживающем представление в шестнадцатеричном виде (Total Commander, Far) и убедиться в этом.
Запись в файл осуществляется с помощью функции
Функция возвращает число удачно записанных элементов. В качестве аргументов принимает указатель на массив, размер одного элемента, число элементов и указатель на файловый поток. Вместо массив, конечно, может быть передан любой объект.
Запись в бинарный файл объекта похожа на его отображение: берутся данные из оперативной памяти и пишутся как есть. Для считывания используется функция fread
Функция возвращает число удачно прочитанных элементов, которые помещаются по адресу ptr. Всего считывается count элементов по size байт. Давайте теперь считаем наше число обратно в переменную.
fseek
Одной из важных функций для работы с бинарными файлами является функция fseek
Эта функция устанавливает указатель позиции, ассоциированный с потоком, на новое положение. Индикатор позиции указывает, на каком месте в файле мы остановились. Когда мы открываем файл, позиция равна 0. Каждый раз, записывая байт данных, указатель позиции сдвигается на единицу вперёд.
fseek принимает в качестве аргументов указатель на поток и сдвиг в offset байт относительно origin. origin может принимать три значения
- SEEK_SET - начало файла
- SEEK_CUR - текущее положение файла
- SEEK_END - конец файла. К сожалению, стандартом не определено, что такое конец файла, поэтому полагаться на эту функцию нельзя.
В случае удачной работы функция возвращает 0.
Дополним наш старый пример: запишем число, затем сдвинемся указатель на начало файла и прочитаем его.
Вместо этого можно также использовать функцию rewind, которая перемещает индикатор позиции в начало.
В си определён специальный тип fpos_t, который используется для хранения позиции индикатора позиции в файле.
Функция
используется для того, чтобы назначить переменной pos текущее положение. Функция
используется для перевода указателя в позицию, которая хранится в переменной pos. Обе функции в случае удачного завершения возвращают ноль.
возвращает текущее положение индикатора относительно начала файла. Для бинарных файлов - это число байт, для текстовых не определено (если текстовый файл состоит из однобайтовых символов, то также число байт).
Рассмотрим пример: пользователь вводит числа. Первые 4 байта файла: целое, которое обозначает, сколько чисел было введено. После того, как пользователь прекращает вводить числа, мы перемещаемся в начало файла и записываем туда число введённых элементов.
Вторая программа сначала считывает количество записанных чисел, а потом считывает и выводит числа по порядку.
Примеры
1. Имеется бинарный файл размером 10*sizeof(int) байт. Пользователь вводит номер ячейки, после чего в неё записывает число. После каждой операции выводятся все числа. Сначала пытаемся открыть файл в режиме чтения и записи. Если это не удаётся, то пробуем создать файл, если удаётся создать файл, то повторяем попытку открыть файл для чтения и записи.
2. Пишем слова в бинарный файл. Формат такой - сначало число букв, потом само слово без нулевого символа. Ели длина слова равна нулю, то больше слов нет. Сначала запрашиваем слова у пользователя, потом считываем обратно.
3. Задача - считать данные из текстового файла и записать их в бинарный. Для решения зачи создадим функцию обёртку. Она будет принимать имя файла, режим доступа, функцию, которую необходимо выполнить, если файл был удачно открыт и аргументы этой функции. Так как аргументов может быть много и они могут быть разного типа, то их можно передавать в качестве указателя на структуру. После выполнения функции файл закрывается. Таким образом, нет необходимости думать об освобождении ресурсов.
4. Функция saveInt32Array позволяет сохранить массив типа int32_t в файл. Обратная ей loadInt32Array считывает массив обратно. Функция loadInt32Array сначала инициализирует переданный ей массив, поэтому мы должны передавать указатель на указатель; кроме того, она записывает считанный размер массива в переданный параметр size, из-за чего он передаётся как указатель.
5. Создание таблицы поиска. Для ускорения работы программы вместо вычисления функции можно произвести сначала вычисление значений функции на интервале с определённой точностью, после чего брать значения уже из таблицы. Программа сначала производит табулирование функции с заданными параметрами и сохраняет его в файл, затем подгружает предвычисленный массив, который уже используется для определения значений. В этой программе все функции возвращают переменную типа Result, которая хранит номер ошибки. Если функция отработала без проблем, то она возвращает Ok (0).
6. У нас имеются две структуры. Первая PersonKey хранит логин, пароль, id пользователя и поле offset. Вторая структура PersonInfo хранит имя и фамилию пользователя и его возраст. Первые структуры записываются в бинарный файл keys.bin, вторые структуры в бинарный файл values.bin. Поле offset определяет положение соответствующей информации о пользователе во втором файле. Таким образом, получив PersonKey из первого файла, по полю offset можно извлечь из второго файла связанную с данным ключом информацию.
Зачем так делать? Это выгодно в том случае, если структура PersonInfo имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.
Получение бинарных данных используя JavaScript arrays
Свойство responseType объекта XMLHttpRequest можно задать для изменения ожидаемого типа ответа с сервера. Возможные значения: пустая строка (по умолчанию), "arraybuffer", "blob", "document", "json" и "text". Свойство response будет содержать тело сущности в соответствии с типом ответа, как ArrayBuffer, Blob, Document, JSON или string. Это значение равно null, если запрос не завершён или не был успешным.
Альтернатива вышеуказанному методу использует интерфейс Blob для непосредственного построения Blob с данными arraybuffer.
Также вы можете прочитать двоичный файл как Blob , установив строку" blob " в свойство responseType.
Получение бинарных данных в старых браузерах
Функция load_binary_resource(), показанная ниже, загружает двоичные данные из указанного URL, возвращая их вызывающему объекту.
Магия происходит в строке 5, которая переопределяет тип MIME, заставляя браузер рассматривать его как обычный текст, используя пользовательский набор символов. Это говорит браузеру не анализировать его и пропускать байты через необработанные.
The example above fetches the byte at offset x within the loaded binary data. The valid range for x is from 0 to filestream.length-1 .
Получение бинарных данных из различных источников
Библиотека jBinary для работы с бинарными данными в JavaScript позволяет загрузить данные из любого источника, автоматически определяя лучший способ для этого в текущем браузере или Node.js:
Отправка бинарных данных
В примере ниже на лету создаётся текстовый файл и отправляется методом POST на сервер. Здесь используется обычный текст, но нетрудно представить себе пример с бинарным файлом.
Отправка типизированных массивов как бинарных данных
Точно так же можно отправлять типизированные массивы JavaScript.
Отправка форм и загрузка файлов
Примеры для Firefox
В этом примере двоичные данные передаются асинхронно методом POST и нестандартным методом Firefox's sendAsBinary() .
В строке 4 заголовок Content-Length устанавливается в 741, что означает, что размер данных 741 байт. Разумеется, это значение должно соответствовать реальному размеру данных.
В строке 5 метод sendAsBinary() начинает запрос.
Примечание: Нестандартный метод sendAsBinary начиная с Gecko 31 (Firefox 31 / Thunderbird 31 / SeaMonkey 2.28) считается устаревшим и скоро будет удалён. Вместо него, как показывалось выше, можно использовать стандартный метод send(Blob data) .В общем там все просто. Устанавливаешь соединение с сервером по 80-му порту.
Дальше пишеш в сокет строку запроса (POST\turi\tprotoclo\r\n. ) + заголовки(заголоаок=значение\r\n) + \r\n + двоичный файл + \r\n + \r\n. Дальше ждеш ответа сервера
Возможно я где ошибся, давно уже стандарта не читал, но принцип такой.
Структура запросов, перечень заголовков и примеры это все есть в стандарте
Кстати немножно соврал. В секции BODY нельзя передавать двоичный файл, нужно его преобразовать, т.е. поменять все байты старше 127 на %кодсимвола в виде текста. Передача чисто-двоичных данных стандартом тоже предусмотрено, только немного по другому. В общем нет смысла пересказывать стандарт — его надо читать
А где можно ознакомиться со стандартом?
Здравствуйте, erslgoeirjh, Вы писали:
E>А где можно ознакомиться со стандартом?
Здравствуйте, Аноним, Вы писали:
А>Передача чисто-двоичных данных стандартом тоже предусмотрено, только немного по другому.
P.S. иногда передают и в чисто бинарном виде, указывая при этом соответственный MIME-тип и размер бинарных данных. но стандартом это не рекомендуется делать в связи с несовместимостью с некоторыми платформами(7-битными к примеру). просто на это разработчики иногда банально забивают болт.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.Здравствуйте, DSD, Вы писали:
DSD>P.S. иногда передают и в чисто бинарном виде, указывая при этом соответственный MIME-тип и размер бинарных данных. но стандартом это не рекомендуется делать в связи с несовместимостью с некоторыми платформами(7-битными к примеру). просто на это разработчики иногда банально забивают болт.
А как это сделать? Как я понимаю, надо указать content-type: application/octet-stream.
Но не могли бы Вы, DSD, расписать, расписать всё это -- что идёт сначала, что потом, как указывается имя файла, как указывается размер файла, надо ли где ставить пустые строки (как в случае с ontent-type: text/html) или нет, что нужно передавать ещё, помимо имени и свойств файла?
Я так понимаю, тебе нужен обычный POST запрос, которым заливают файлы на сервера.
тогда приблизительно так:
Content-Length — общая длина POST-буффера в байтах (указывать в данном случае этот заголовок не обязательно)
в заголовке Content-Type по идее правильно будет так:
то есть boundary указывается с новой строки, но с символом табуляции в начале этой строки.
это значит, что тело http-запроса будет в формате multipart/form-data, или multipart MIME message с разделителем частей "---------------------------7d52322441049c". этот разделитель генерируется от балды в каждом запросе и должен НЕ ВСТРЕЧАТЬСЯ в самих передаваемых данных.
далее, после http-заголовка должна идти пучтая строка, т.е. отправляем "\r\n\r\n", после чего отправляем само тело запроса(именуемый в народе POST-буффер).
допстим, мы хотим передать файл d:\somefile.jpg в параметре формы с именем send_file, тогда тело запроса будет выглядеть примерно так:
разберем все это.
- -----------------------------7d52322441049c — наш разделитель, предваряемый еще двумя символами "--"(!обратить внимание! в заголовке был указан на два символа короче!). Обозначает начало очередной части запроса(у нас она одна, но вообще их может быть несколько).
- Content-Disposition: form-data; — значит, что эта часть запроса — поле WWW-формы.
- name="send_file"; — имя этого поля.
- filename="d:\somefile.jpg" — тут, думаю, ясно и без комментариев
- Content-Type: image/jpeg — MIME-тип передаваемого файла.
- Content-Transfer-Encoding: base64 — значит, что тело файла закодировано методом base64. если бы мы не указали этого, то могли бы просто пустить этот файл в бинарном виде.
- далее пустая строка "\r\n\r\n" — разделитель заголовка и тела части.
- дальше идут непосредственно данные файла в формате base64. сами данные я, естесственно, не приводил.
- -----------------------------7d52322441049c-- — разделитель частей, добавленные в его конец еще два символа "--" означают, что это была последняя часть, иначе он бы начинал следующую часть.
если бы мы передавали еще какие-нибудь параметры, кроме файла, выглядело бы так:
P.S. единственное, чего я не знаю(лень было дочитать доки, наверное ), так это почему такие странные правила формирования разделителя — добавлять "--" в его начало.
P.P.S. Заголовок:тоже можно записать как:то есть в несколько строк с табуляцией в начале каждой следующей.
P.P.P.S. если всегда использовать Content-Transfer-Encoding: base64 или любой другой(и кстати, это можно использовать для всех частей, а не только для части с бинарными данными), то по крайней мере появляется гарантия, что в данных запроса не будут встречаться случайные("поддельные") boundary-разделители.
Уйдемте отсюда, Румата! У вас слишком богатые погреба."Content-Transfer-Encoding: 7BIT" is assumed if the Content-Transfer-Encoding header field is not present.
Здравствуйте, Sinclair, Вы писали:
S>Нет. См. RFC 2045:
S>
"Content-Transfer-Encoding: 7BIT" is assumed if the Content-Transfer-Encoding header field is not present.
только что проверил "на живца", т.е. через сниффер. IE отдает:
Уйдемте отсюда, Румата! У вас слишком богатые погреба.The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
mean that the identity (i.e. NO) encoding transformation has been
performed. As such, they serve simply as indicators of the domain of
the body data, and provide useful information about the sort of
encoding that might be needed for transmission in a given transport
system. The terms "7bit data", "8bit data", and "binary data" are
all defined in Section 2.
Здравствуйте, Sinclair, Вы писали:
Ну и х. шут с ним :o) убедил
Здравствуйте, DSD, Вы писали:
DSD>Здравствуйте, erslgoeirjh, Вы писали:
DSD>Я так понимаю, тебе нужен обычный POST запрос, которым заливают файлы на сервера.
DSD, спасибо Вам за подробное объяснение, но у меня есть маленький вопрос: а что писать в MIME-заголовке, исли используется Content-type равный не multipart, а application/octet-strem? Ведь во втором случае не надо разбивать файл на части.
Здравствуйте, DSD, Вы писали:
DSD>Здравствуйте, erslgoeirjh, Вы писали:
DSD>Я так понимаю, тебе нужен обычный POST запрос, которым заливают файлы на сервера.
Здравствуйте, erslgoeirjh, Вы писали:
можешь так же взять "чей-то" код. например слей исходники PHP, найди там файлики base64.c/base64.h (их там, кстати, не один вариант) и адаптируй под себя.
E>И при каких значениях Content-Type производится это кодирование -- при multipart, или же также при application или других значениях?
при любых. если указываешь "Content-Transfer-Encoding: base64", то соответственно, и кодируешь поток этим алгоритмом. Можно указать в этом заголовке другой алгоритм, и тогда, соответственно, нужно будет кодировать поток другим алгоритмом.
В данной теме показано как можно сохранять данные в бинарных файлах без использования стандартных средств pickle или struct языка Python.
Содержание
- 1. Понятие о бинарных файлах. Представление информации в бинарных файлах
- 2. Запись/чтение списка, который содержит вещественные числа. Пример
- 3. Запись/чтение кортежа, содержащего строки символов. Пример
- 4. Запись/чтение множества, содержащего вещественные числа. Пример
- 5. Запись/чтение двумерной матрицы строк заданного размера. Пример
- 6. Запись/чтение словаря. Пример
- 7. Копирование одного бинарного файла в другой
- 8. Объединение двух бинарных файлов
Поиск на других ресурсах:
1. Понятие о бинарных файлах. Представление информации в бинарных файлах
В языке Python существуют средства для работы с бинарными или двоичными файлами. Бинарные файлы используют строки типа bytes . Это значит при чтении бинарных данных из файла возвращается объект типа bytes .
Пример, демонстрирующий особенности представления информации в бинарных файлах.
Результат работы программы
На основании примера выше можно сделать следующие выводы:
2. Запись/чтение списка, который содержит вещественные числа. Пример
Результат работы программы
3. Запись/чтение кортежа, содержащего строки символов. Пример
Результат работы программы
4. Запись/чтение множества, содержащего вещественные числа. Пример
Множество, которое содержит только однотипные объекты можно записать в файл. В данном примере записывается множество вещественных чисел.
Результат работы программы
Вид файла myfile6.bin
5. Запись/чтение двумерной матрицы строк заданного размера. Пример
В примере матрица представлена в виде списка.
Результат работы программы
Вид файла myfile7.txt
6. Запись/чтение словаря. Пример
Пусть задан некоторый словарь, который нужно записать в бинарный файл.
Результат работы программы
Вид файла myfile8.txt
7. Копирование одного бинарного файла в другой
8. Объединение двух бинарных файлов. Пример
В примере реализована операция объединения двух файлов в результирующий третий файл. Сначала данные с файлов-источников считываются в списки. Затем происходит конкатенация этих списков и запись результирующего списка в файл результата.
Читайте также: