2017-09-16 65 views
0

我正在嘗試逐行讀取文件並將其分割成單詞。這些詞應該保存到一個數組中。但是,該程序僅獲取文本文件的第一行,當它嘗試讀取新行時,程序崩潰。從文件中分割字符串並將它們放入數組會導致程序崩潰

FILE *inputfile = fopen("file.txt", "r"); 
char buf [1024]; 
int i=0; 
char fileName [25]; 
char words [100][100]; 
char *token; 

while(fgets(buf,sizeof(buf),inputfile)!=NULL){ 

     token = strtok(buf, " "); 
     strcpy(words[0], token); 
     printf("%s\n", words[0]); 
     while (token != NULL) { 


      token = strtok(NULL, " "); 
      strcpy(words[i],token); 
      printf("%s\n",words[i]); 
      i++; 

     } 

    } 
+0

謝謝你的工作,但你能解釋爲什麼嗎? – Pantelis

回答

1

從興很好的答案後,我決定寫我的FULL簡單的程序,實現你的任務,並告訴一些關於我的解決辦法。我的程序逐行讀取一個文件,將其作爲輸入參數給出,並將下一行保存到緩衝區中。

代碼:

#include <assert.h> 
#include <errno.h> 
#define _WITH_GETLINE 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define assert_msg(x) for (; !(x) ; assert(x)) 

int 
main(int argc, char **argv) 
{ 
     FILE *file; 
     char *buf, *token; 
     size_t length, read, size; 

     assert(argc == 2); 

     file = fopen(argv[1], "r"); 
     assert_msg(file != NULL) { 
       fprintf(stderr, "Error ocurred: %s\n", strerror(errno)); 
     } 

     token = NULL; 
     length = read = size = 0; 

     while ((read = getline(&token, &length, file)) != -1) { 
       token[read - 1] = ' '; 

       size += read; 
       buf = realloc(buf, size); 
       assert(buf != NULL); 

       (void)strncat(buf, token, read); 
     } 
     printf("%s\n", buf); 

     fclose(file); 
     free(buf); 
     free(token); 

     return (EXIT_SUCCESS); 
} 

對於文件file.txt

that is a 
text 
which I 
would like to 
read 
from file. 

我得到了一個結果:

$ ./program file.txt 
that is a text which I would like to read from file. 

幾件事情,這是值得要說的解決方案:

  1. 而不是fgets(3)我使用getline(3)函數,因爲有簡單的方法來獲得有關字符串長度的知識(read變量)和自動內存分配得到的字符串(token)。重要的是要記住free(3)它。對於類Unix系統,默認情況下不提供getline(3)以避免兼容性問題。因此,在<stdio.h>標題前使用#define _WITH_GETLINE宏以使該功能可用。
  2. buf僅包含保存字符串所需的必需空間量。在從文件buf讀取一行後,所需的空間量由realloc(3)擴展。這是多一點「普遍」的解決方案嗎?記住釋放堆中分配的對象是很重要的。
  3. 我也用strncat(3)它確保不多於read字符(長度爲token)將被保存到buf。這也不是使用strncat(3)的最佳方式,因爲我們也應該測試字符串截斷。但總的來說,它比簡單使用strcat(3)要好,因爲惡意用戶可以通過緩衝區溢出攻擊任意改變正在運行的程序的功能。 strcat(3)strncat(3)也增加了終止\0
  4. A getline(3)返回帶有換行符的標記,因此我決定將它從新行替換爲空格(在從文件中給出的單詞中創建句子的上下文中)。我也應該消除最後的空間,但我不想複雜的源代碼。

從不是強制性的東西,我還定義了我自己的宏assert_msg(x)這是能夠運行assert(3)功能,並顯示錯誤的文本消息。但它只是一個功能,但由於我們能夠在錯誤的嘗試打開文件時看到錯誤消息。

1

問題是在inner while循環中獲取下一個標記,並將結果傳遞給strcpy,而不檢查NULL結果。

while(fgets(buf,sizeof(buf),inputfile)!=NULL){ 

    token = strtok(buf, " "); 
    strcpy(words[0], token); 
    printf("%s\n", words[0]); 

    while (token != NULL) {//not at the end of the line. yet! 
     token = strtok(NULL, " ");//get next token. but token == NULL at end of line 
     //passing NULL to strcpy is a problem 
     strcpy(words[i],token); 
     printf("%s\n",words[i]); 
     i++; 
    } 
} 

通過將支票插入狀態的同時,傳遞NULL作爲第二個參數的strcpy得以避免。

while ((token = strtok (NULL, " ")) != NULL) {//get next token != NULL 
    //if token == NULL the while block is not executed 
    strcpy(words[i],token); 
    printf("%s\n",words[i]); 
    i++; 
} 
1

淨化你的循環,而不要重複自己:


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

int main(void) 
{ 

    FILE *inputfile = fopen("file.txt", "r"); 
    char buf [1024]; 
    int i=0; 
    char fileName [25]; 
    char words [100][100]; 
    char *token; 

for(i=0; fgets(buf,sizeof(buf),inputfile);) { 
     for(token = strtok(buf, " "); token != NULL; token = strtok(NULL, " ")){ 
      strcpy(words[i++], token); 
      } 
     } 
    return 0; 
    } 
相關問題