2017-12-29 38 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

큰 문제 중 하나는' "% s"'형식이 * 공백으로 구분 된 * 문자열을 읽는다는 것입니다. 또 다른 문제는 루프 상태에서 feof()를 사용해서는 안된다는 것입니다 (http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong). 세 번째 문제는 [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()

는 파일 입력의 라인을 읽은 다음 구문 분석합니다. 디버깅에 유용하고 문자열을 null 문자를 보장 -
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() 너무 멀리 떨어져

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);  

하지 않거나 보험에 가입

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

... strtol(), 또는 그 이상의 오류 검사에 대한 sscanf(). 유효한 연도 값을 확인해야할까요?

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; 

좋아, 코드는 "*""**"가 발생하지

경우


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에 할당하고이 항목을 책 배열에 추가합니다.

실습에서 제외 될 수있는 몇 가지 개선 사항은 입력 파일 이름을 하드 코딩하지 않고 명령 줄 인수를 사용하는 것입니다. 출력을 파일에 기록하면 출력 파일이 두 번째 명령 행 인수가되는 경우에도 유용합니다. 마지막으로 주석에서 언급했듯이 사용중인 배열의 크기에 대한 입력 크기 검사를 제공 할 수 있고 제공해야합니다.

+0

'strtok (s, * \ t \ n \ r \ v \ f ");에 오는 짧은 것은 하나의' '*''가 필드 구분자 역할을한다는 것입니다. – chux

+0

@chux 사실이지만, OP는 출력에서 ​​일반 정보를 원했고'*'는 그 정보의 일부가되지 않을 것이라고 생각했습니다. OP가 그의 필드에서 공백으로 구분 된 이름을 허용하기 위해 공백 대신에'*'를 사용하는 것처럼 보입니다. – atru

+0

많은 관심을 가져 주셔서 감사합니다. – ali