2012-02-09 34 views
35

Tôi đã làm việc trên một bài tập nhỏ cho lớp CIS của tôi và rất bối rối bởi các phương pháp C sử dụng để đọc từ một tập tin. Tất cả những gì tôi thực sự cần làm là đọc qua một dòng tập tin bằng dòng và sử dụng thông tin thu thập được từ mỗi dòng để thực hiện một vài thao tác. Tôi đã thử sử dụng phương pháp getline và những người khác không có may mắn. Mã của tôi hiện nay là như sau:Đi qua một dòng tập tin văn bản theo dòng trong C

int main(char *argc, char* argv[]){ 
     const char *filename = argv[0]; 
     FILE *file = fopen(filename, "r"); 
     char *line = NULL; 

     while(!feof(file)){ 
     sscanf(line, filename, "%s"); 
     printf("%s\n", line); 
     } 
    return 1; 
} 

Ngay bây giờ tôi nhận được một lỗi seg với phương pháp sscanf và tôi không chắc chắn lý do tại sao. Tôi là một tổng số C noob và chỉ tự hỏi nếu có một số hình ảnh lớn mà tôi đã mất tích. Cảm ơn

+1

Mã này thậm chí không được biên dịch. 'sscanf (dòng, tên tập tin,"% s ");' nên là 'sscanf (dòng, tập tin,"% s ");' – Mawg

+0

Lưu ý rằng ['while (! feof (file))' luôn luôn sai] (http : //stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong). –

+0

Có thể trùng lặp của [C đọc tập tin dòng theo dòng] (https://stackoverflow.com/questions/3501338/c-read-file-line-by-line) –

Trả lời

93

Rất nhiều sự cố trong một vài dòng. Tôi có thể quên một số:

  • argv [0] là tên chương trình, không phải là đối số đầu tiên;
  • nếu bạn muốn đọc trong một biến, bạn phải cấp phát bộ nhớ của nó
  • ta không bao giờ lặp trên feof, một vòng trên một chức năng IO cho đến khi nó thất bại, feof sau đó phục vụ để quy kết nguyên nhân của thất bại,
  • sscanf ở đó để phân tích cú pháp một dòng, nếu bạn muốn phân tích cú pháp tệp, hãy sử dụng fscanf,
  • "% s" sẽ dừng ở không gian đầu tiên dưới định dạng cho gia đình? scanf
  • để đọc một dòng, tiêu chuẩn chức năng là fgets,
  • trả về 1 từ phương tiện chính thất bại

Vì vậy

#include <stdio.h> 

int main(int argc, char* argv[]) 
{ 
    char const* const fileName = argv[1]; /* should check that argc > 1 */ 
    FILE* file = fopen(fileName, "r"); /* should check the result */ 
    char line[256]; 

    while (fgets(line, sizeof(line), file)) { 
     /* note that fgets don't strip the terminating \n, checking its 
      presence would allow to handle lines longer that sizeof(line) */ 
     printf("%s", line); 
    } 
    /* may check feof here to make a difference between eof and io failure -- network 
     timeout for instance */ 

    fclose(file); 

    return 0; 
} 
+19

đừng quên 'fclose (file)' trước khi trở về . – vivisidea

+5

'fclose (file)' thực sự không cần thiết, vì nó đang xảy ra trong 'main' và nó sẽ tự động đóng tất cả các bộ đệm tệp đã mở. – Leandros

+11

@Leandros luôn an toàn hơn là xin lỗi! – Vallentin

6

Để đọc một dòng từ một tệp, bạn nên sử dụng hàm fgets: Nó đọc chuỗi từ tệp được chỉ định tối đa ký tự mới hoặc EOF.

Việc sử dụng sscanf trong mã của bạn sẽ không hoạt động chút nào khi bạn sử dụng filename làm chuỗi định dạng để đọc từ line thành một chuỗi ký tự không đổi %s.

Lý do SEGV là bạn ghi vào bộ nhớ không được phân bổ được trỏ đến bởi line.

2

Ngoài các câu trả lời khác, trên thư viện C gần đây (tuân thủ Posix 2008), bạn có thể sử dụng getline. Xem this answer (đối với câu hỏi có liên quan).

3

Giả sử bạn đang làm việc với một số delimiter khác, chẳng hạn như một tab \t, thay vì một dòng mới \n.

Cách tiếp cận tổng quát hơn đối với dấu phân tách là sử dụng getc(), mỗi lần lấy một ký tự.

Lưu ý rằng getc() trả về một int, để chúng tôi có thể kiểm tra sự bình đẳng với EOF.

Thứ hai, chúng ta định nghĩa một mảng line[BUFFER_MAX_LENGTH] loại char, để lưu trữ lên đến BUFFER_MAX_LENGTH-1 ký tự trên stack (chúng ta phải tiết kiệm mà ký tự cuối cùng cho một nhân vật \0 terminator).

Sử dụng một mảng tránh nhu cầu sử dụng mallocfree để tạo con trỏ ký tự có độ dài phù hợp trên heap.

#define BUFFER_MAX_LENGTH 1024 

int main(int argc, char* argv[]) 
{ 
    FILE *file = NULL; 
    char line[BUFFER_MAX_LENGTH]; 
    int tempChar; 
    unsigned int tempCharIdx = 0U; 

    if (argc == 2) 
     file = fopen(argv[1], "r"); 
    else { 
     fprintf(stderr, "error: wrong number of arguments\n" 
         "usage: %s textfile\n", argv[0]); 
     return EXIT_FAILURE; 
    } 

    if (!file) { 
     fprintf(stderr, "error: could not open textfile: %s\n", argv[1]); 
     return EXIT_FAILURE; 
    } 

    /* get a character from the file pointer */ 
    while(tempChar = fgetc(file)) 
    { 
     /* avoid buffer overflow error */ 
     if (tempCharIdx == BUFFER_MAX_LENGTH) { 
      fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n"); 
      return EXIT_FAILURE; 
     } 

     /* test character value */ 
     if (tempChar == EOF) { 
      line[tempCharIdx] = '\0'; 
      fprintf(stdout, "%s\n", line); 
      break; 
     } 
     else if (tempChar == '\n') { 
      line[tempCharIdx] = '\0'; 
      tempCharIdx = 0U; 
      fprintf(stdout, "%s\n", line); 
      continue; 
     } 
     else 
      line[tempCharIdx++] = (char)tempChar; 
    } 

    return EXIT_SUCCESS; 
} 

Nếu bạn phải sử dụng một char *, sau đó bạn vẫn có thể sử dụng mã này, nhưng bạn strdup() mảng line[], một khi nó được lấp đầy với giá trị của một dòng đầu vào. Bạn phải free chuỗi trùng lặp này sau khi bạn đã hoàn tất hoặc bạn sẽ bị rò rỉ bộ nhớ:

#define BUFFER_MAX_LENGTH 1024 

int main(int argc, char* argv[]) 
{ 
    FILE *file = NULL; 
    char line[BUFFER_MAX_LENGTH]; 
    int tempChar; 
    unsigned int tempCharIdx = 0U; 
    char *dynamicLine = NULL; 

    if (argc == 2) 
     file = fopen(argv[1], "r"); 
    else { 
     fprintf(stderr, "error: wrong number of arguments\n" 
         "usage: %s textfile\n", argv[0]); 
     return EXIT_FAILURE; 
    } 

    if (!file) { 
     fprintf(stderr, "error: could not open textfile: %s\n", argv[1]); 
     return EXIT_FAILURE; 
    } 

    while(tempChar = fgetc(file)) 
    { 
     /* avoid buffer overflow error */ 
     if (tempCharIdx == BUFFER_MAX_LENGTH) { 
      fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n"); 
      return EXIT_FAILURE; 
     } 

     /* test character value */ 
     if (tempChar == EOF) { 
      line[tempCharIdx] = '\0'; 
      dynamicLine = strdup(line); 
      fprintf(stdout, "%s\n", dynamicLine); 
      free(dynamicLine); 
      dynamicLine = NULL; 
      break; 
     } 
     else if (tempChar == '\n') { 
      line[tempCharIdx] = '\0'; 
      tempCharIdx = 0U; 
      dynamicLine = strdup(line); 
      fprintf(stdout, "%s\n", dynamicLine); 
      free(dynamicLine); 
      dynamicLine = NULL; 
      continue; 
     } 
     else 
      line[tempCharIdx++] = (char)tempChar; 
    } 

    return EXIT_SUCCESS; 
} 
+1

Tôi sẽ bỏ phiếu xuống bất kỳ 'while (! Feof (file))' ngay cả trường hợp xảy ra một khi nó là mặt trăng màu xanh, nơi nó không phải là damageable (Lưu ý rằng ở đây nó có lẽ sẽ không bao giờ đúng, có một sự ngắt quãng để rời khỏi vòng lặp trong trường hợp đó, 'while (true)' cũng sẽ hoạt động.) Có quá nhiều người nghĩ đó là thành ngữ chính xác. – AProgrammer

+0

Tôi không biết đó là vấn đề. Tôi thành thật muốn tìm hiểu thêm về điều này. Các vấn đề với việc sử dụng đó là gì? –

+0

Có rất nhiều câu hỏi mà điều này xuất hiện, ví dụ: http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong chẳng hạn. – AProgrammer

Các vấn đề liên quan