Как прочитать структуру из файла c
Обновлено: 21.11.2024
-
Вы еще не сталкивались с объектно-ориентированным программированием (по крайней мере, некоторые из вас).
-
Стандартная библиотека C предоставляет ряд функций для доступа к тексту или форматированным данным
-
Вы должны сначала открыть файл перед чтением данных из файла
|
-
Текстовые данные — это данные, представленные с использованием кода ASCII
-
Функция форматированного чтения fread() используется для чтения форматированных данных
-
Когда вы закончите запись файла, вы должны закрыть файл, чтобы избежать возможной потери данных
-
Функция форматированной печати fprintf() используется для записи форматированных данных
-
Доступ "последовательный файл" означает, что для чтения N-го байта в файле необходимо сначала прочитать N-1-й байт
(По индукции это означает, что вы должны сначала прочитать все, что предшествует чему-либо)
-
смещение               новая позиция в файле. (Это точное местоположение новой позиции зависит от того, откуда )
Новая позиция равна смещению в байтах от начала файла |
Новая позиция равна смещению в байтах от текущей позиции чтения в файле |
Новая позиция равна смещению в байтах от конца файла | TR>
-
Когда вы читаете файл данных, вы должны знать, какие данные в нем хранятся.
Чтение из файла в структуры..
Потратив много времени на поиск предыдущих сообщений, я все еще застрял на следующем. По сути, я пытаюсь открыть и прочитать из файла, сохраняя данные файла в структуру. Пожалуйста, смотрите ниже:
вот моя структура, объявленная в моем заголовочном файле.
Теперь мне нужно открыть существующий файл и прочитать данные в приведенной выше структуре. данные файла имеют следующий формат .
.. и вот мой код на данный момент (не очень далеко, я знаю!).
Как лучше всего читать данные файла и сохранять их в структуре? любые идеи будут оценены!
Во-вторых, если данные в файле отображаются постоянно, как указано выше, вы можете использовать:
Я попытаюсь реализовать предложенный код. однако у меня есть более сложная структура для загрузки, которая включает вложенную структуру - см. ниже:
файл для этого имеет следующий формат:
Есть предложения? Могу ли я реализовать аналогичный код, предложенный для первой структуры? Что делать с вложенной структурой?
Любая помощь приветствуется!
И СНОВА, в определении вашей структуры BusStruct вам не нужен MAX_CLASS внутри структуры. Вместо этого вы собираетесь создать такое количество структур. Итак, ваше определение будет таким:
еще раз спасибо. я реализовал код, но получаю сообщение об ошибке:
"значение в индексе не является ни массивом, ни указателем"
спасибо за комментарии. вот мой последний код (обратите внимание, что структуры немного отличаются, но вы поняли идею).
файл для чтения имеет формат:
последний код для чтения данных файла в структуру:
в основном я вызываю функцию для загрузки структуры:
вот функция загрузки:
Примечание: f2 — это просто имя файла, которое передается функции загрузки.
Я получаю сообщение об ошибке в начале строки с fscanf, как упоминалось выше (ниже /* загрузить данные в структуру */).
Я ценю всю помощь!
Вообще говоря, если вам абсолютно не нужно читать базу данных вручную (например:
Андерсон, Джон Х., 29 декабря 1976 г.
Эндрюс, Джейн М., 15 января 1980 г.
.
) вы не должны писать их таким образом. Большинство программ имеют тенденцию записывать структуры в двоичный файл.
Чтобы структура выглядела так:
Может быть записан в файл и выглядеть следующим образом:
Первоначально опубликовано master5001
Вообще говоря, если вам абсолютно не нужно читать вашу базу данных вручную, вы не должны писать их таким образом. Большинство программ имеют тенденцию записывать структуры в двоичный файл.
.
Вы можете просто прочитать это обратно в структуру, и вам не нужно будет выполнять синтаксический анализ.
Это противоположный совет, который я видел ранее. Я предполагаю, что вы пропагандируете что-то вроде следующего.
Если это так, обратите внимание, что он не обязательно переносим с платформы на платформу, с компилятора на компилятор или даже с одним и тем же компилятором при компиляции с разными параметрами. И то, что человек удобочитаем, имеет свои преимущества.
спасибо всем за помощь. я поиграю с предложенными подходами и опубликую свое решение.
еще раз ура!
Ну, я реализовал первоначальное предложение и смог загрузить структуру с первой строкой текста в файле.
У меня все еще есть проблема с попыткой загрузить дополнительные строки текста из файла. код отлично работает для одной строки, но если есть еще строки данных файла, я получаю сообщение об ошибке «Ошибка шины».
вот фрагмент кода, который вызывает функцию загрузки структуры:
а вот функция загрузки:
есть идеи, как загрузить несколько строк из текстового файла в массив структур?
Я очень новичок в C, поэтому, пожалуйста, извините мое невежество, но как именно я могу это сделать? мне нужно передать указатель на файл, который содержит данные и прочитать из функции продаж в буфер, а затем в основном прочитать буфер в структуру?
Если вы можете указать мне (извините за каламбур) правильное направление, я был бы признателен.
Внимательно изучите свою функцию load_sales(). Вы вызываете эту функцию MAX_LOG раз. Внутри этой функции вы снова и снова открываете один и тот же файл и закрываете его. Каждый раз, когда вы открываете файл, указатель файла указывает на первый байт в файле. Таким образом, независимо от того, сколько раз вы вызываете эту функцию, возвращаемые данные всегда будут первой строкой в файле.
Чтобы решить эту проблему, вы можете открыть файл fopen() непосредственно перед циклом for и внутри функции load_sales(), удалив код для fopen()/fclose(). Остальное все выглядит хорошо.
Надеюсь, это поможет.
Дэйв_Синкула, ваш код о подводит итог тому, что я сказал. Вот почему большинство людей не согласны со мной. Если вы переносите часть данных с одной машины на другую (обычно в случае с базами данных), способ чтения байтов может быть другим. Таким образом, короткое замыкание можно хранить следующим образом:
старший байт, младший байт
или
младший байт, старший байт
Поэтому он не так портативен в плане чтения данных. Однако, если вы не создаете программу, вывод которой должен считываться на нескольких машинах, вам следует использовать двоичный код. После получения, если вам необходимо использовать обычный текстовый формат, не делайте этого. Многие базы данных являются частными и содержат информацию, например информацию о сотрудниках. Это может отлично подойти в качестве двоичной базы данных. В других базах данных может храниться такая информация, как данные о вождении людей, которые могут быть прочитаны тысячами компьютеров по всему миру — это должно быть более переносимым.
Первоначально опубликовано master5001
Dave_Sinkula, ваш код about резюмирует то, что я сказал. Вот почему большинство людей не согласны со мной. Если вы переносите часть данных с одной машины на другую (обычно в случае с базами данных), способ чтения байтов может отличаться.
В этом руководстве вы узнаете об обработке файлов в C. Вы научитесь обрабатывать стандартный ввод-вывод в C с помощью функций fprintf(), fscanf(), fread(), fwrite(), fseek() и т. д. помощь примеров.
Файл — это контейнер на компьютерных запоминающих устройствах, используемый для хранения данных.
Зачем нужны файлы?
- При завершении программы все данные теряются. Сохранение в файле сохранит ваши данные, даже если программа завершит работу.
- Если вам нужно ввести большое количество данных, ввод их всех займет много времени.
Однако, если у вас есть файл, содержащий все данные, вы можете легко получить доступ к содержимому файла с помощью нескольких команд на языке C. - Вы можете легко перенести свои данные с одного компьютера на другой без каких-либо изменений.
Типы файлов
При работе с файлами необходимо знать два типа файлов:
1. Текстовые файлы
Текстовые файлы — это обычные файлы .txt. Вы можете легко создавать текстовые файлы с помощью любых простых текстовых редакторов, таких как Блокнот.
Когда вы откроете эти файлы, вы увидите все содержимое файла в виде обычного текста. Вы можете легко редактировать или удалять содержимое.
Они требуют минимальных усилий для обслуживания, легко читаются, обеспечивают наименьшую безопасность и занимают больше места для хранения.
2. Бинарные файлы
Двоичные файлы — это в основном файлы .bin на вашем компьютере.
Вместо того, чтобы хранить данные в виде обычного текста, они хранят их в двоичной форме (0 и 1).
Они могут содержать больший объем данных, их нелегко читать, и они обеспечивают более высокий уровень безопасности, чем текстовые файлы.
Операции с файлами
В C вы можете выполнять четыре основные операции с файлами, текстовыми или двоичными:
- Создание нового файла
- Открытие существующего файла
- Закрытие файла
- Чтение и запись информации в файл
Работа с файлами
При работе с файлами необходимо объявить указатель типа файл. Это объявление необходимо для связи между файлом и программой.
Открытие файла — для создания и редактирования
Открытие файла выполняется с помощью функции fopen(), определенной в заголовочном файле stdio.h.
Синтаксис открытия файла в стандартном вводе-выводе:
- Предположим, что файл newprogram.txt не существует в папке E:\cprogram. Первая функция создает новый файл с именем newprogram.txt и открывает его для записи в соответствии с режимом 'w'.
Режим записи позволяет создавать и редактировать (перезаписывать) содержимое файла. - Теперь предположим, что второй двоичный файл oldprogram.bin существует в папке E:\cprogram. Вторая функция открывает существующий файл для чтения в бинарном режиме 'rb'.
Режим чтения позволяет только читать файл, вы не можете записывать в файл.
Закрытие файла
Файл (как текстовый, так и двоичный) должен быть закрыт после чтения/записи.
Закрытие файла выполняется с помощью функции fclose().
Здесь fptr — это указатель файла, связанный с файлом, который нужно закрыть.
Чтение и запись в текстовый файл
Для чтения и записи в текстовый файл мы используем функции fprintf() и fscanf().
Это просто версии файлов printf() и scanf() . Единственное отличие состоит в том, что fprintf() и fscanf() ожидают указатель на структуру FILE.
Пример 1. Запись в текстовый файл
Эта программа получает номер от пользователя и сохраняет его в файле program.txt .
После того, как вы скомпилируете и запустите эту программу, вы увидите текстовый файл program.txt, созданный на диске C вашего компьютера. Когда вы откроете файл, вы увидите введенное целое число.
Пример 2. Чтение из текстового файла
Эта программа считывает целое число из файла program.txt и выводит его на экран.
Если вы успешно создали файл из примера 1, запустив эту программу, вы получите введенное целое число.
Другие функции, такие как fgetchar() , fputc() и т. д., можно использовать аналогичным образом.
Чтение и запись в двоичный файл
Функции fread() и fwrite() используются для чтения и записи в файл на диске соответственно в случае двоичных файлов.
Запись в двоичный файл
Для записи в двоичный файл необходимо использовать функцию fwrite(). Функции принимают четыре аргумента:
- адрес данных для записи на диск
- размер данных для записи на диск
- количество таких данных
- указатель на файл, в который вы хотите записать.
Пример 3. Запись в двоичный файл с помощью fwrite()
В этой программе мы создаем новый файл program.bin на диске C.
Мы объявляем структуру threeNum с тремя числами — n1, n2 и n3 и определяем ее в основной функции как num.
Теперь внутри цикла for мы сохраняем значение в файле с помощью fwrite() .
Первый параметр принимает адрес num, а второй параметр принимает размер структуры threeNum .
Поскольку мы вставляем только один экземпляр num , третий параметр равен 1 . И последний параметр *fptr указывает на файл, в котором мы сохраняем данные.
Наконец, мы закрываем файл.
Чтение из двоичного файла
Функция fread() также принимает 4 аргумента, как и функция fwrite(), описанная выше.
Пример 4. Чтение из двоичного файла с помощью функции fread()
В этой программе вы читаете один и тот же файл program.bin и перебираете записи одну за другой.
Проще говоря, вы читаете одну запись threeNum размера threeNum из файла, на который указывает *fptr, в структуру num .
Вы получите те же записи, что и в примере 3.
Получение данных с помощью fseek()
Если у вас есть много записей в файле и вам нужно получить доступ к записи в определенной позиции, вам нужно пройтись по всем записям перед этим, чтобы получить запись.
Это приведет к потере большого количества памяти и рабочего времени. Более простой способ получить необходимые данные можно с помощью fseek() .
Как следует из названия, fseek() ищет курсор для данной записи в файле.
Синтаксис fseek()
Первый поток параметров — это указатель на файл. Второй параметр — это позиция искомой записи, а третий параметр указывает место, где начинается смещение.
Я хочу прочитать CSV-файл в массив структур с окончанием NULL.
- Можно ли переместить условие fscanf внутрь условия цикла while вместо бесконечного (1)?
- Как установить "конец" массива с одним NULL, например "line_pointer[i] = NULL" в операторе else?
3 ответа 3
Ответ user3629249 хорош. Единственное, что я должен добавить:
неудобно. Зачем проходить акробатику добавления указателя? Почему бы просто
\$\begingroup\$ Я согласен с использованием «индекса», а не добавления указателя. Однако я не хотел путать ОП \$\endgroup\$
Следующий предлагаемый код:
- чисто компилируется
- выполняет желаемую функцию
- учитывает комментарии к вопросу операционной системы
- правильно проверяет наличие ошибок при вызове malloc() и realloc(), а также fopen() и fscanf()
- цикл while() использует функцию: fscanf(), а не '1' в качестве условия цикла
- использует значение i, чтобы определить, когда достигнут конец данных
- устанавливает последний экземпляр структуры в NULL в соответствии с запросом OP
- согласно ответу Рейндериена и для удобства чтения такие операторы, как &(line_pointer + i)->value1 (в котором используется добавление указателя), можно заменить на: &(line_pointer[i].value1 (в котором используется индексирование)
а теперь предлагаемый код:
вместо этого вы можете попробовать этот цикл:
поэтому есть только одно место для выделения памяти
\$\begingroup\$ Почему этот код выделяет дополнительный экземпляр? Если вы просто скажете что вы изменили, но не почему, ваш ответ почти не будет иметь значения, поскольку он не объясняет лучшие практики. Кроме того, вы забыли правильно отформатировать код. \$\конечная группа\$
\$\begingroup\$ Меня гораздо больше интересовало показать OP, как использовать вызов fscanf() для управления циклом while(), поскольку именно об этом спрашивает OP. Я также продемонстрировал, как вызывать realloc(), чтобы избежать утечки памяти. Я также продемонстрировал, как обрабатывать ошибки. \$\конечная группа\$
\$\begingroup\$ @GarfieldCat, опубликуйте сообщение об ошибке, которое вы получаете в операторе, выполняющем realloc() \$\endgroup\$
\$\begingroup\$ @RolandIllig, и каковы «лучшие методы» для текущего сценария? \$\конечная группа\$
Я отвечу на ваши вопросы в обратном порядке:
Как установить "конец" массива с одним NULL, например, line_pointer[i] = NULL в операторе else?
Вы можете (с memset , как показано в других ответах), но стандарт C не гарантирует, что значение с плавающей запятой будет установлено равным нулю. Вместо этого я бы предложил установить каждое значение в структуре равным нулю, как вы уже делаете.
Можно ли переместить условие fscanf внутрь условия while -loop вместо бесконечного (1)?
Возможно, вы сможете это сделать, но я не рекомендую это делать. Это затруднит чтение и понимание вашего кода.
Некоторые другие проблемы:
Проверить возвращаемое значение семейства функций * alloc
Здесь может произойти сбой, если вам не хватает памяти:
Вы должны добавить проверку точно так же, как и с csvFile:
Конечно, лучше сформулировать сообщение об ошибке с помощью perror :
perror сработает и при сбое fopen.
Правильно проверьте возвращаемое значение fscanf
Вместо того, чтобы проверять, достигли ли вы конца файла, проверьте, не прочитали ли вы столько переменных, сколько ожидали. Это обеспечивает минимальный аспект проверки данных:
Используйте EXIT_FAILURE вместо 1
Поскольку вы используете EXIT_SUCCESS для демонстрации успеха, вы должны стремиться к согласованности (и переносимости), используя EXIT_FAILURE вместо 1 в exit .
Используйте возврат вместо выхода
В дополнение к вышесказанному, в main вы можете отказаться от использования exit и вместо этого использовать return . Во всяком случае, при нормальных обстоятельствах нет необходимости использовать выход в программе.
Не приводить возвращаемое значение malloc / calloc / realloc
Возвращаемое значение realloc может быть неявно преобразовано в любой указатель объекта. Приводить его необязательно, и, на мой взгляд, лучше избегать ненужных приведения.
Используйте операторы доступа к массивам и структурам вместо арифметики указателей
Мне это кажется странным:
Вместо этого вы можете использовать доступ к массиву и расширение . оператор:
Это равносильно тому, когда вы берете адрес участника .
Используйте одинаковые отступы
В некоторых местах вы используете два пробела, а в других — один и четыре. Я предлагаю придерживаться одного стиля отступов, каким бы он ни был. Лично я использую четыре или вкладку, но вы можете использовать все, что хотите, если это последовательно. Дополнительные сведения см. в Руководстве по стилю кодирования ядра Linux.
Условия Йоды
Наоборот, ваши условия. Это трудно читать и практически не приносит никакой пользы, учитывая, что теперь компиляторы предупреждают об опечатках, которые первоначально разрешались условиями Йоды:
Читайте также: