2017-01-20 2 views
-2

Я делаю программу под названием «Менеджер книг», теперь все, что нужно, - это создание функций для сохранения и загрузки книг (структур) из файла. Мне интересно, как их читать. Вот мое определение структуры:Как читать структуры из файла в c?

typedef 
struct book { 
    char * title; 
    char * authorName; 
    char * authorSurname; 
    int releaseYear; 
    char * genre; 
    int flags[10]; 
} Book; 

Я сделал функцию, которая позволяет мне читать одну книгу из файла, но это ужасно, и я не понимаю, как читать множество книг. Я хочу прочитать его в своем массиве.

Book allBooks[LIBRARY_SIZE]; 

Sample-файл, который я хочу прочитать от внешности:

Christine,Stephen,King,1980,Horror 
Harry Potter,J.K,Rowling,2000,Fantasy 

Любые идеи?

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

void loadBook(Book * book, FILE * plik){ 
    char jakasTablica[50]; 
    char jakasTablica2[50]; 
    char jakasTablica3[50]; 
    char jakasTablica4[50]; 
    int i = 0; 
    char znak; 
    fscanf(plik,"%c",&znak); 
    while(znak != ','){ 
     jakasTablica[i] = znak; 
     i += 1; 
     fscanf(plik,"%c",&znak); 
    } 
    jakasTablica[i] = '\0'; 

    strcpy(book->title, jakasTablica); 

    i = 0; 
    fscanf(plik,"%c",&znak); 
    while(znak != ','){ 
     jakasTablica2[i] = znak; 
     i += 1; 
     fscanf(plik,"%c",&znak); 
    } 
    jakasTablica2[i] = '\0'; 

    strcpy(book->authorName, jakasTablica2); 

    i = 0; 
    fscanf(plik,"%c",&znak); 
    while(znak != ','){ 
     jakasTablica3[i] = znak; 
     i += 1; 
     fscanf(plik,"%c",&znak); 
    } 
    jakasTablica3[i] = '\0'; 

    strcpy(book->authorSurname, jakasTablica3); 

    i = 0; 
    fscanf(plik,"%c",&znak); 
    while(znak != ','){ 
     jakasTablica4[i] = znak; 
     i += 1; 
     fscanf(plik,"%c",&znak); 
    } 
    jakasTablica4[i] = '\0'; 

    strcpy(book->genre, jakasTablica4); 

    i = 0; 
    int zmienna = 0; 
    fscanf(plik, "%d", &zmienna); 
    book->releaseYear = zmienna; 
} 
+0

Почему бы вам не показать, что вы попробовали? – Stargateur

+0

Пожалуйста, разместите свой код, который вы используете, чтобы прочитать одну книгу из файла. –

+0

Вы уже разработали алгоритм? Если да, объясните это и сообщите нам, где вы столкнулись с проблемой его реализации. Если нет, покажите нам, что у вас есть, и объясните, где вы застряли. –

ответ

3

Чтобы ответить, как читать каждый разделенные запятой линии, которую вы хотите сделать следующее:

#include <stdio.h> 
#include <string.h> 

int main (void) 
{ 
    FILE *fp; 
    char a[30], b[30], c[30], d[30], e[30]; 
    char line[100]; 

    fp=fopen("./data.txt", "r"); 
    fgets(line, sizeof(line), fp); 
    sscanf(line, "%29[^,],%29[^,],%29[^,],%29[^,],%29[^,]", a,b,c,d,e); 

    printf("%s %s %s %s %s\n", a,b,c,d,e); 
    fclose(fp); 
    return(0); 
} 

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

Как насчет названий книг с ,? Возможно, вам будет лучше использовать CSV parsing library. Если вы используете библиотеку, подобную той, которую вы должны были бы поставить " вокруг любого поля, содержащего ,, прежде чем записывать его в текстовый файл, чтобы анализатор CSV мог его обработать ,


Вы можете использовать двоичный файл ввода-вывода напрямую с помощью fread()/fwrite() structs. Вам придется использовать структуру фиксированного размера. Ниже приведен простой пример. Он не выполняет всю необходимую проверку ошибок, но этого должно быть достаточно, чтобы вы начали.

#include <stdio.h> 
#include <string.h> 

typedef struct 
{ 
    char title[60]; 
    char author[20]; 
    int year_published; 
    int pages; 
}Book; 



int main (void) 
{ 
    FILE *fp; 
    Book b1,b2; 
    Book book; 

    strcpy(b1.title, "Title Book 1"); 
    strcpy(b1.author, "Book 1 Author"); 
    b1.year_published = 1991; 
    b1.pages = 123; 

    strcpy(b2.title, "Title Book 2"); 
    strcpy(b2.author, "Book 2 Author"); 
    b2.year_published = 1992; 
    b2.pages = 456; 


    fp=fopen("./books.dat", "w+"); 
    if(fp) 
    { 
     fwrite(&b1, sizeof(b1), 1, fp); 
     fwrite(&b2, sizeof(b2), 1, fp); 
     fclose(fp); 
     printf("Wrote file\n"); 
     printf("Now reopen the file and read the structs\n"); 

     fp = fopen("./books.dat", "r"); 
     if(fp) 
     { 
      fread(&book, sizeof(book), 1, fp); 
      printf("%s %s %d %d\n", book.title, book.author, book.year_published, book.pages); 

      fread(&book, sizeof(book), 1, fp); 
      printf("%s %s %d %d\n", book.title, book.author, book.year_published, book.pages); 

      fclose(fp); 
     } 
     else 
     { 
      printf(" Failed to open created data file.\n"); 
     } 
    } 
    else 
    { 
     printf("Failed to open file\n"); 
    } 

    return 0; 
} 
+0

Да, похоже, отличное решение, единственное, что я хочу, чтобы он написал его как стандартный .txt, чтобы я мог изменить свою «базу данных книги», даже если я не запускаю программу. –

+0

@ Karol.T Я добавил раздел о том, как читать строки, разделенные запятыми. – Chimera

+0

@DavidBowling Вы правы. – Chimera

0

Изменить определение книги в:

typedef 
struct book { 
    char title[50]; 
    char authorName[50]; 
    char authorSurname[50]; 
    int releaseYear; 
    char genre[50]; 
    int flags[10]; 
} Book; 

, а затем в loadBook:

... 
int n; 
for (n= 0; n < LIBRARY_SIZE; ++n) { 
    ... 
    Book[n].title[i] = znak; 
    ... 

т.д.

удачи.

1

Вот как я решил свою проблему, используя код Химеры.

void readBooks(Book * List){ 
FILE * plik; 
plik=fopen("booksList.txt","r"); 
int iloscKsiazekDoDodania; 
char pom; 
pom = fgetc(plik); 

while(!feof(plik)){ 
    if(pom == '\n') iloscKsiazekDoDodania += 1; 
    pom = fgetc(plik); 
} 
printf("%d\n", iloscKsiazekDoDodania); 
fclose(plik); 
plik = fopen("booksList.txt","r"); 


for(int i = 0; i < iloscKsiazekDoDodania; i++){ 
    char a[50], b[50], c[50], d[50]; 
    char line[100]; 
    int x; 
    fgets(line, 99, plik); 
    sscanf(line, "%[^,],%[^,],%[^,],%[^,],%d", a,b,c,d,&x); 
    addRandom(a,b,c,x,d,List); 
} 

fclose(plik); 
} 

и моя писанина функция:

void saveBooks(Book * List){ 
FILE * plik; 
plik=fopen("booksList.txt","w"); 
for(int i = 0; i < allBooksSize; i++){ 
    fprintf(plik,"%s,", List[i].title); 
    fprintf(plik,"%s,", List[i].authorName); 
    fprintf(plik,"%s,", List[i].authorSurname); 
    fprintf(plik,"%s,", List[i].genre); 
    fprintf(plik,"%d\n", List[i].releaseYear); 
} 
fclose(plik); 
} 

Спасибо за помощь.

+2

''% [^,] '->' "% 49 [^,]' и проверить результат 'sscanf()'. '% d' сканирует неправильное подполе на' 'Кристина, Стивен, Кинг, 1980, Ужас". – chux

+0

Показатели кода 'pom', не связанные с вопросом. – chux

2

OP хочет прочитать строку текста, разделенного запятой, в обычном struct.

Настоятельно рекомендую отделить чтение от разбора.

char buffer[LINE_SIZE]; 
    if (fgets(buffer, sizeof buffer, plik) == NULL) return ...; 

Затем разобрать строку и возвращает значение, указывающее, 1: успех, 0: сбой или наступает конец файла: EOF

// void loadBook(Book * book, FILE * plik){ 
int loadBook(Book * book, FILE * plik) { 

Теперь разобрать buffer[]. Есть много подходит с различными плюсами и минусами. Следующее упрощает использование "%n", чтобы сохранить смещение сканирования до этой точки.

#define LINE_SIZE 512 
// scan until encountering a comma or linefeed 
#define FS "%*[^,\n]%n" 
#define FI "%d %n" 

int loadBook(Book * book, FILE * plik) { 
    memset(book, 0, sizeof *book); // zero fill 
    char buffer[LINE_SIZE]; 
    if (fgets(buffer, sizeof buffer, plik) == NULL) { 
    return EOF; 
    } 

    int n1, n2, n3, n4, n5, i; 
    n5 = 0; 
    sscanf(buffer, FS "," FS "," FS "," FI "," FS, 
     &n1, &n2, &n3, &i, &n4, &n5); 

    // If scan was incomplete or did not finish on a \n, return 0 
    if (n5 == 0 || buffer[n5] != '\n') return 0; 

    // Let us use a check for a sane year as a data qualifier test 
    // https://wiganlanebooks.co.uk/blog/interesting/ 
    // 10-of-the-oldest-known-surviving-books-in-the-world/ 
    if (i < -600 || i > 2999) return 0; 

    buffer[n1] = 0; book->title = strdup(buffer); 
    buffer[n2] = 0; book->authorName = strdup(buffer + n1 + 1); 
    buffer[n3] = 0; book->authorSurname = strdup(buffer + n2 + 1); 
    book->releaseYear = i; 
    buffer[n5] = 0; book->genre = strdup(buffer + n4 + 1); 
    return 1; 
} 

strdup() является общей, но нестандартной функцией, которая выделяет память и дублирует строку. Sample code Обязательно освободите выделенное пространство, когда закончите с book.

+0

Интересный подход. Итак, если строка ввода больше, чем буфер, или если данные для поля 'year_published' находятся вне диапазона тестирования, функция возвращает значение' 0'. –

+0

Узнал что-то новое о '% n'. Спасибо. – Chimera

+1

@DavidBowling Да, ''% n "' в конце формата можно использовать в качестве простого теста для успешного сканирования _complete_. Но, возможно, буфер слишком мал: [максимальное имя книги] (http://www.news18.com/news/india/worlds-longest-book-title-1022-words-and-no-spaces-276244.html) – chux

 Смежные вопросы

  • Нет связанных вопросов^_^