Как читать байты в файле
Там нет документации по этим файлам, но я уже понял, как это сделать на С++. Я пытаюсь перезаписать проект в python по нескольким причинам, но я сталкиваюсь с ошибкой.
Чтобы сначала объяснить, файл .lrf имеет метаданные сразу в начале файла в этом формате:
первые 4 байта для чего-то, о чем я не знаю.
Следующие 4 байта сохраняют длину метаданных в hexidecimal, вплоть до конца метаданных, которые после фактического содержимого воспроизведения.
байты после начальных 8 байтов - это метаданные в формате json
Проблема, с которой я столкнулась, - это фактически чтение длины метаданных. Это текущая функция, которая у меня есть:
Когда я вызываю эту функцию, оболочка возвращает трассировку:
Мое лучшее предположение заключается в том, что read() пытается прочитать символы, которые закодированы в некотором формате в формате unicode, но это, безусловно, просто байты, которые я пытаюсь прочитать. Есть ли способ прочитать их как байты? Кроме того, есть ли лучший способ пропустить байты при попытке прочитать файл?
спросил(а) 2014-03-01T02:33:00+04:00 7 лет, 8 месяцев назадДвоичные файлы должны обрабатываться в двоичном режиме:
Для пропуска байт я обычно использую файл seek (SEEK_CUR или SEEK_SET), или я просто делаю произвольное file.read(n) , если я не хочу беспокоиться о формальности. Только раз, когда я действительно использую поиск, я хотел бы перейти к определенной позиции.
Интерпретация двоичных данных. Я просто придерживаюсь метода распаковки, предоставляемого модулем struct , что позволяет легко определить, хотите ли вы интерпретировать последовательность байтов как int, float, char и т.д. Это как Я делал это в течение многих лет, поэтому, возможно, есть более эффективные подходы, такие как метод from_bytes , описанный в других ответах.
С помощью модуля struct вы можете делать такие вещи, как
Чтение в 3 (беззнаковых) целых числах одновременно. Так, например, учитывая формат, который вы изменили, я бы просто сказал
В 18 уровне начались первые задачи побайтного чтения файлов: прочитать файл, далее найти минимальные/максимальные байты или вывести в упорядоченном виде и т.п.
- Ввести с консоли имя файла
- Считать все байты из файла.
- Не учитывая повторений - отсортировать их по байт-коду в убывающем порядке.
- Вывести на экран
- Закрыть поток ввода-вывода
Решаем в лоб:
Решает все замечательно! Тест (если бы был — прошелся бы на ура). Но в жизни мало файлов содержащих только строчку "Мама мыла раму". Давайте скормим нашей программе файл в 46Мб (по нынешним меркам вроде и не особо много). Что такое, программа выполняется 220 секунд. Попытка скормить с вечера 1Gb файл (размер MPEG4 фильма не в самом лучшем качестве) не увенчалась успехом. Программа утром все еще читала - а мне идти на работу уже. В чем проблема? Наверное в использовании ArrayList<Integer> у которого внутри 1 миллиард элементов. Каждый элемент его занимает 16 байт минимум (Заголовок: 8 байт + Поле int: 4 байта + Выравнивание для кратности 8: 4 байта). Итого мы добровольно загоняем в память 16 Gb данных при размере оперативы в 8. Будем делать лучше. Нырнем в коллекции глубже. И ура, нашлось то, что нам нужно.
Встречаем TreeSet
- не допускает хранение двух одинаковых элементов (а значит мы будем хранить в памяти все 255 элементов, вместо миллиарда!)
- при манипуляциях со своими элементами автоматом упорядочивает (само сортирует - вот он, верх совершенства!)
Массив — побайтно
Имеем на выходе: 46Мб файл 158 секунд. 1Gb файл - 2 часа 55 минут. Опять улучшение, но небольшое. И мы сделали все простыми инструментами. Не использовали микроскоп для забивания гвоздей. Теперь лирическое отступление. Вспомним устройство компьютера. Память ОЗУ (DRAM) где обычно выполняется программа и хранятся переменные имеет высокую скорость доступа, но небольшой размер. Память на жестком/flash диске (HDD или Flash-накопители) где обычно хранятся файлы, наоборот имеет низкую скорость доступа, но большой размер. Так что когда мы побайтно читаем 1Gb файл (то есть миллиард раз обращаемся к HDD) - мы тратим много времени на работу с низкоскоростным устройством (по песчинке перекладываем песок с кузова КамАЗа в песочницу). Попробуем еще улучшить.
Т екстовые файлы хранят данные в виде текста (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 имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.
Как считать один байт из файла? Следующий код работает, но некоторые байты пропускаются ( 0x09, 0x0a, 0x0b, 0x0c, 0x0d ) и считывается следующий за ними байт.
Пытался еще сделать вот так,
но uint8_t* не кастится к char* . Использую именно uint8_t потому, что насколько я знаю стандартом размер char-а не регламентирован.
Я бы использовал char (чтоб он был не байтом - это надо потрудиться такое найти. ) Тогда
Ну, а если строго uint8_t (который, готов спорить, у вас он просто unsigned char . если нет - то даже интересно, с чем вы таким имеете дело) - то
- 0x09 - горизонтальная табуляция ( \t )
- 0x0a - перевод строки ( \n )
- 0x0b - вертикальная табуляция
- 0x0c - «прогон страницы», новая страница - команда для принтера начать печать с новой страницы
- 0x0d - возврат каретки ( \r )
Выглядит так, как-будто вы читаете всё таки текстовый файл как текст, может быть оставить в конструкторе только std::ifstream::binary ?
В крайнем случае всегда есть сишный fread() .
Для чтения файлов байтами есть специально для этого предназначенная функция fread(). Используйте ее.
Это все-таки наследие С, я думал в stream-ах есть замена для нее.В C++ очень много умных функций форматированного ввода, таких-как >>. Не пачкайтесь, а пользуйся std::basic_istream::read.
Согласно стандарту ISO/IEC 14882, <cstdio> такая же часть С++, как и <iostream> . Поэтому никто не мешает его использовать там, где это удобно. Ведь никто не переживает насчет такого "наследия с", как операции += , %= Никто и не помышляет убрать из С++ "наследие С", наоборот, на повестке дня добавление в стандарт С++ разного рода удобных инициализаций, которые появились в C11. Кстати, у <iostream> нет никаких преимуществ перед <cstdio> .
Читайте также: