2017-12-29 33 views
-1

ファイルからフォーマットされたデータを読み取るように、Cの関数fscanfを使用するが、それはは、どのように私はこれらのデータを読み込み、構造体の配列に入れたい

#include<stdio.h> 
struct book { 
    char bookName[50]; 
    char authorName[50]; 
    long price; 
    int year; 
} 
main() { 
    FILE *data; 
    data=fopen("library.txt", "r"); 
    if (data == NULL) { 
     printf("File Could not be opened!\n"); 
    } 
    else { 
     struct book myBook; 
     struct book books[20]; 

     int n=0; 

     while (!feof(data)) 
     { 
      fscanf(data,"%s***%s***%d***%d\n",&myBook.bookName,&myBook.authorName,&myBook.price,&myBook.year); 
      books[n]=myBook; 
      n++; 
     } 

     int i; 
     for(i=0;i<n;i++){ 
      printf("%s - %s - %d - %d \n",books[i].bookName,books[i].authorName,books[i].price,books[i].year); 
     } 
    } 
} 

動作しないと出力が

C - b - 0 - 232159429 
programing***Fatemeh - b - 0 - 232159429 
Kazemi***15000***1391 - b - 0 - 232159429 
C - b - 0 - 232159429 
programs***Ali - b - 0 - 232159429 
Ahmadpour***20000***1392 - b - 0 - 232159429 
Programing***Mona - b - 0 - 232159429 
Ghassemi***25000***1389 - b - 0 - 232159429 
C - b - 0 - 232159429 
programing - b - 0 - 232159429 
(advanced)***Sara - b - 0 - 232159429 
Hamidi***40000***1385 - b - 0 - 232159429 
です

しかし、私の実際のデータは、私が何をすべき

C programing***Fatemeh Kazemi***15000***1391 
Cprograms***Ali Ahmadpour***20000***1392 
Programing***Mona Ghassemi***25000***1389 
C programing (advanced)***Sara Hamidi***40000***1385 

のですか? fscanfはスペースのみで動作しますが、私はデータを分離するために***を使用する必要があります

+7

主な問題の1つは、 '"%s "'形式が*スペースで区切られた*文字列を読み込むことです。もう一つの問題はループ状態で 'feof()'を使うべきではないということです(http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong)。 3番目の問題は、['fscanf'](http://en.cppreference.com/w/c/io/fscanf)*が何を返すかをチェックしないことです。四番目は、配列のためにアドレス&演算子 '&'( '&myBook.bookName'のように)を使うべきではないということです。 –

+2

@ usr2564301フォーマット指定子の外にはありません。書式文字列中の '*'は、入力中のリテラル '*'にマッチします。 ''%* s "'は全く異なるものです。 –

+0

fscanf(データ、 "%s ***%s ***%d ***%d \ n"、&myBook.bookName、&myBook.authorName、&myBook.price、&myBook.year); – ali

答えて

0

fscanf()を使用しないでください。エラーチェックは難しく、不完全です。

while (!feof(data))次の読み取りが成功することを保証しません。

// while (!feof(data)){ 
// fscanf(data,"%s***%s***%d***%d\n",.... 

使用fgets()ファイル入力のラインを読み、解析します。デバッグに便利と文字列のヌル文字を保証 -

size_t n = 0; 
// Work with an ample sized buffer 
char buf[sizeof (struct book) * 2]; 
while (n < 20 && fgets(buf, sizeof buf, data)) { 

今、私たちはゼロでmyBookをあらかじめ記入してみましょう"***"

char *sep[3]; 
    sep[0] = strstr(buf, "***"); 
    if (sep[0] == NULL) break; 
    sep[1] = strstr(sep[0] + 3, "***"); 
    if (sep[1] == NULL) break; 
    sep[2] = strstr(sep[1] + 3, "***"); 
    if (sep[2] == NULL) break; 

を探してデータを解析します。

struct book myBook; 

は、セパレータが離れすぎて使用atol()か...以上、エラーチェックのための

myBook.price = atol(sep[1] + 3); 

... strtol()、またはsscanf()

if (sep[0] - buf >= sizeof myBook.bookName) break; 
    memcpy(myBook.bookName, buf, sep[0] - buf); // 
    if (sep[1] - sep[0] - 3 >= sizeof myBook.authorName) break; 
    memcpy(myBook.authorName, sep[0] + 3, sep[1] - sep[0] - 3);  

いないことを確認します。おそらく有効な年の値をチェックするでしょうか?

char *endptr; 
    errno = 0; 
    myBook.year = strtol(sep[2] + 3, &endptr, 10); 
    if (sep[2] + 3 == endptr) break; 
    if (errno) break; 
    if (myBook.year < year_MIN || myBook.year > year_MAX) break; 

[OK]を、コードはそれ

books[n] = myBook; 
    n++;  
} 

作られたが、私は"*""**"が発生したことがない場合は

私のデータを分離するために***を使用する必要があり、コードエラーチェックの少ない以下を使用することができます。常に入力関数の戻り値を確認してください。

while (n < 20 && fgets(buf, sizeof buf, data)) { 
    if (sscanf(buf, "%49[^*]***%49[^*]***%ld***%d", 
     myBook.bookName, myBook.authorName, &myBook.price, &myBook.year) != 4) { 
    break; // badly formatted data 
    } 
    books[n] = myBook; 
    n++;  
} 
+0

あなたの支援のために多くのご協力ありがとうございます。 – ali

+0

@aliなぜあなたが 'while(!feof(data))をコード化したのか不思議です。どのようなテキストがそれを提案したか – chux

+1

ユーザー:2410359私の大学の教授が書いた別の質問からの解決策の一部です – ali

0

このプログラムは、現在の形式でデータを読み取り、として出力を生成します「 - 」区切りのエントリを(あなたが提供printf文のように):

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

#define MAXBUF 500 

int parse_string(char* s,char* a[]); 

struct book { 
    char bookName[50]; 
    char authorName[50]; 
    long price; 
    int year; 
}; 

int main() { 

    FILE *data; 
    char buf[MAXBUF]; 
    char *sp, *words[MAXBUF]; 
    int nw; 

    data=fopen("library.txt", "r"); 
    if (data == NULL) { 
     printf("File Could not be opened!\n"); 
    } 
    else { 
     struct book myBook; 
     struct book books[20]; 

     int n=0; 

     // Read each line into buf and parse it into words, 
     // assign words into structure fields 
     while((sp = fgets(buf, MAXBUF, data))!= NULL){ 
      nw=parse_string(buf, words); 

      // If number of words different than number of 
      // fields in the structure exit with an error 
      if (nw!=4){ 
       printf("Error - number of parsed words does not match number of fields in the structure!\n"); 
       exit(1); 
      } 

      // Assign values, convert strings to long and int 
      strncpy(myBook.bookName, words[0], strlen(words[0])+1); 
      strncpy(myBook.authorName, words[1], strlen(words[1])+1); 

      myBook.price = atol(words[2]); 
      myBook.year = atoi(words[3]); 

      // Add to book array 
      books[n] = myBook; 

      n++; 
     } 

     // Print 
     int i; 
     for(i=0;i<n;i++){ 
      printf("%s - %s - %ld - %d \n",books[i].bookName,books[i].authorName,books[i].price,books[i].year); 
     } 
    } 
} 


/* Function to parse a line of input into an aray of words */ 
/* s - pointer to the char array (line) to be parsed 
* a - array of pointers to parsed elements 
* returns number of elements in a*/ 
int parse_string(char* s, char* a[]) 
{ 
    int nw,j; 
    a[0] = strtok(s,"*\t\n\r\v\f"); 
    nw = 1; 
    while((a[nw]= strtok(NULL,"*\t\n\r\v\f"))!=NULL) 
     nw++; 
    return nw; 
} 

などはコメントで提案し、これは、プログラムは、一度にファイル行を読み取るためにfgetsを使用します。次に、解析関数を使用して、各行を指定された区切り文字に基づいて単語に構文解析します。構文解析機能は小さく、strtokとハードコーディングされた区切り文字を使用します。入力に基づいて、区切り文字としてスペースを使用せず、その代わりに*を使用します。これは合理的に新しい行のような他のデリミタを可能にします。

次のステップは、book構造のフィールドに各ワードを割り当てることです。これに先だって、解析関数(nw)から戻ってくる単語の数が現在のフィールド数に等しいかどうかのチェックが行われます。最後に、もしあれば、それは会員に必要な変換をmyBookに割り当て、このエントリをブック配列に追加します。

練習として除外できる改善点は、入力ファイル名をハードコードするのではなく、コマンドライン引数を有効にすることです。出力をファイルに書き込むことも良いでしょう。この場合、出力ファイルは2番目のコマンドライン引数になります。最後に、コメントで説明したように、使用している配列のサイズに対して入力サイズのチェックを行うことができます。

+0

'strtok(s、" * \ t \ n \ r \ v \ f ");'に至る短いことは、単一の ''*' 'がフィールド区切り文字としても機能するということです。 – chux

+0

@chux trueですが、私はOPが出力にプレーンな情報を必要とし、 '*'はその情報の一部ではないと考えました。 OPはスペースの代わりに '*'を使用しているので、フィールドにスペース区切りの名前を使用することができます。 – atru

+0

あなたの注意のためにたくさんありがとう – ali

関連する問題