2017-09-16 64 views
-1

我寫了這段代碼,它讀取我的文本的每個字符並將它放入我的char數組中。我的問題是沒有檢測到文件的末尾,所以fscanf()在文本結束後每次返回最後一個字符時都會返回,直到我的數組被填充。我怎樣才能防止呢?我編程在C在C中檢測txt文件的EOF

我的代碼:

int main() { 
    char array[50][50]; 
    char buff; 
    FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r"); 

    for (int i = 0; i < 50; i++) { 
     for (int k = 0; k < 50; k++) { 
      fscanf(cola, "%c", &buff); 
      array[i][k] = buff; 
     } 
    } 

    fclose(cola); 

    for (int i = 0; i < 50; i++) { 
     for (int k = 0; k < 50; k++) { 
      printf("%c", array[i][k]); 
     } 
    } 
    return 0; 
} 

謝謝您的幫助。

Here is a Screenshot of my Code

+3

請不要發表圖片的代碼。相反,創建一個包含實際代碼的代碼塊。 –

+4

檢查'fscanf'的返回值。 – BLUEPIXY

+0

我現在編輯我的文章。 –

回答

1

替換:

for (int i = 0; i < 50; i++) { 
    for (int k = 0; k < 50; k++) { 
     fscanf(cola, "%c", &buff); 
     array[i][k] = buff; 
    } 
} 

有:

for (int i = 0; i < 50; i++) { 
    for (int k = 0; k < 50; k++) { 
     int c = getc(cola); 
     if (c == EOF) 
      break; 
     array[i][k] = c; 
    } 
} 

由於buff是那麼不使用,不定義它。請注意,返回類型getc()int,而不僅僅是char。總是檢查I/O功能是否成功/失敗。在您的原始代碼中,您甚至不檢查I/O操作是否成功,這使得檢測EOF成爲不可能。

請注意,此代碼提出了許多可能或可能不合理的假設。例如,你假設文件中的每一行由49個字符和一個換行符組成;你也假設你永遠不需要把信息打印成'字符串'(你現有的代碼不會;它會逐個字符地打印,所以它是'安全的')。

你可能想描述輸入爲:

  • 閱讀多達50行最多49個字符加在每行換行,並將結果存儲在變量array每行是一個空值終止字符串。

這對常見問題(短行,長行,沒有足夠的行)更具彈性。該代碼可能是:

enum { LINE_LEN = 50, NUM_LINES = 50 }; 
char array[NUM_LINES][LINE_LEN]; 
int i; 
for (i = 0; i < LINE_LEN; i++) 
{ 
    int c; 
    int k; 
    for (k = 0; k < LINE_LEN; k++) 
    { 
     c = getc(cola); 
     if (c == EOF || c == '\n') 
      break; 
     if (k == LINE_LEN - 1) 
     { 
      /* Too long - gobble excess */ 
      while ((c = getc(cola)) != EOF && c != '\n') 
       ; 
      break; 
     } 
     array[i][k] = c; 
    } 
    array[i][k] = '\0'; 
    if (c == EOF) 
     break; 
} 
int num_lines = i; // You have num_lines lines of data in your array 

我發現可口可樂™ASCII藝術形象的一個版本https://www.ascii-code.com/ascii-art/logos/coca-cola.php這類似於你在你的圖片是什麼,但也有許多其他來源及其變體:

  __        ___ __  .ama  , 
     ,d888a       ,d88888888888ba. ,88"I) d 
    a88']8i       a88".8"8) `"8888:88 " _a8' 
    .d8P' PP      .d8P'.8 d)  "8:88:baad8P' 
    ,d8P' ,ama, .aa, .ama.g ,mmm d8P' 8 .8'  88):888P' 
,d88' d8[ "8..a8"88 ,8I"88[ I88' d88 ]IaI"  d8[   
a88' dP "bm8mP8'(8'.8I 8[  d88' `"   .88   
,88I ]8' .d'.8  88' ,8' I[ ,88P ,ama ,ama, d8[ .ama.g 
[88' I8, .d' ]8, ,88B ,d8 aI (88',88"8) d8[ "8. 88 ,8I"88[ 
]88 `888P' `8888" "88P"8m" I88 88[ 8[ dP "bm8m88[.8I 8[ 
]88,   _,,aaaaaa,_  I88 8" 8 ]P' .d' 88 88' ,8' I[ 
`888a,. ,aadd88888888888bma. )88, ,]I I8, .d')88a8B ,d8 aI 
    "888888PP"'  `8""""""8 "888PP' `888P' `88P"88P"8m" 

此文件最長的行是第一個在67個字符加上換行符;最短的是61個字符加換行符。該文件總共只有13行和845個字符(LF行尾)。因此,你的程序不適合處理這個特定的數據文件。它看起來有2,500個字符,並且不會得到它們。

我的完整測試代碼被人做了手腳從標準輸入讀取數據,而不是一個固定的文件名。

#include <stdio.h> 

int main(void) 
{ 
    FILE *cola = stdin; 

    enum { LINE_LEN = 80, NUM_LINES = 50 }; 
    char array[NUM_LINES][LINE_LEN]; 
    int i;  // Need value of i after loop 
    for (i = 0; i < NUM_LINES; i++) 
    { 
     int c; // Need value of c after loop 
     int k; 
     for (k = 0; k < LINE_LEN; k++) 
     { 
      c = getc(cola); 
      if (c == EOF || c == '\n') 
       break; 
      if (k == LINE_LEN - 1) 
      { 
       /* Too long - gobble excess */ 
       while ((c = getc(cola)) != EOF && c != '\n') 
        ; 
       break; 
      } 
      array[i][k] = c; 
     } 
     array[i][k] = '\0'; 
     if (c == EOF) 
      break; 
    } 
    int num_lines = i; // You have num_lines lines of data in your array 

    for (i = 0; i < num_lines; i++) 
     puts(array[i]); 

    return 0; 
} 

我在顯示的數據文件上測試了它,最後一行是空行,並且在空白行後面包含了超過79個字符的幾行。它正確處理所有這些特殊情況。請注意,處理用戶輸入很難;處理不正當的用戶輸入是困難的。代碼不太緊湊。您可以更改規則,然後更改代碼以匹配。我不確定這是編碼這個最簡單的方法;它確實有效,但是。有一個函數來處理內部輸入循環可能會更好;外部循環可以測試該函數的返回值。這將減少特殊情況的處理。

#include <assert.h> 
#include <limits.h> 
#include <stdio.h> 

static int read_line(FILE *fp, size_t buflen, char *buffer) 
{ 
    assert(buflen < INT_MAX); 
    int c;  // Need value of c after loop 
    size_t k; // Need value of k after loop 
    for (k = 0; k < buflen; k++) 
    { 
     if ((c = getc(fp)) == EOF || c == '\n') 
      break; 
     if (k == buflen - 1) 
     { 
      /* Too long - gobble excess */ 
      while ((c = getc(fp)) != EOF && c != '\n') 
       ; 
      break; 
     } 
     buffer[k] = c; 
    } 
    buffer[k] = '\0'; 
    return (k == 0 && c == EOF) ? EOF : (int)k; 
} 

int main(void) 
{ 
    enum { LINE_LEN = 80, NUM_LINES = 50 }; 
    char array[NUM_LINES][LINE_LEN]; 
    int i; 
    for (i = 0; i < NUM_LINES; i++) 
    { 
     if (read_line(stdin, LINE_LEN, array[i]) == EOF) 
      break; 
    } 
    int num_lines = i; 

    for (i = 0; i < num_lines; i++) 
     puts(array[i]); 

    return 0; 
} 

這會產生與以前版本相同輸入的相同輸出。

+0

這個單獨更改不能解決問題,打印循環將訪問數組的未初始化部分,調用未定義的行爲。 – chqrlie

+0

@chqrlie - 你可能是對的。我只處理標題問題(關於不檢測EOF);我沒有看到其他任何代碼。我什至沒有編譯任何東西。我也沒有適當的溢出保護等。 –

+0

令人印象深刻的努力和良好的洞察力,其他人都錯過了行長問題。我猜assert(buflen chqrlie

-1
int main() { 
//char array[50][50]; 
char buff; 
int t; 
FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r"); 

if (cola == NULL) 
{ 
    printf("Cannot open file \n"); 
    exit(0); 
} 
while (1) { 
    t = fgetc(cola); 
    if (t == EOF) 
     break; 
    buff = t; 
    printf("%c", buff); 
} 


fclose(cola); 

return 0; 
} 
+1

在使用'printf(「%c」,buff);'打印字符之前,您應該測試'EOF' **。此外,由於您使用'fgetc(可樂)'從流中讀取數據,因此您必須**定義'int'類型的'buff'作爲'EOF'來正確檢測,並且您可以使用'putchar buff)'輸出。 – chqrlie

+0

@chqrlie'fgetc()'返回一個int值,但是當我們將返回值存儲在char數據類型中時,它會自動存儲返回ASCII的等效字符。即隱含地進行了類型化。 – Aashish

+1

問題在於,如果您在測試之前將'getc()'及其親屬的值保存在'char'中,則會丟棄EOF指示。如果普通'char'是一個有符號的類型,你會錯誤地將一個有效的字符識別爲EOF;如果它是無符號類型,則永遠不會檢測到EOF。兩者都不好。 –

2

fscanf()返回成功轉換次數。您應該測試返回值,也專門處理換行符:

#include <stdio.h> 

int main(void) { 
    char array[50][50]; 
    char buff; 
    FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r"); 

    if (cola == NULL) { 
     return 1; 
    } 
    for (int i = 0; i < 50; i++) { 
     for (int k = 0; k < 50; k++) { 
      if (fscanf(cola, "%c", &buff) != 1 || buff == '\n') { 
       array[i][k] = '\0'; 
       break; 
      } 
      array[i][k] = buff; 
     } 
    } 
    fclose(cola); 

    for (int i = 0; i < 50; i++) { 
     for (int k = 0; k < 50 && array[i][k] != '\0'; k++) { 
      printf("%c", array[i][k]); 
     } 
     printf("\n"); 
    } 
    return 0; 
} 

如果使用getc()而不是fscanf()來讀取文件的字節代碼可以簡化爲:

#include <stdio.h> 

int main(void) { 
    char array[50][51]; 
    int c, i, k, n; 
    FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r"); 

    if (cola == NULL) { 
     return 1; 
    } 
    for (n = 0; n < 50; n++) { 
     for (k = 0; k < 50; k++) { 
      if ((c = getc(cola)) == EOF || c == '\n') { 
       break; 
      } 
      array[n][k] = c; 
     } 
     array[n][k] = '\0'; 
     if (c == EOF && k == 0) 
      break; 
    } 
    fclose(cola); 

    for (i = 0; i < n; i++) { 
     puts(array[i]); 
    } 
    return 0; 
} 
+0

你是對的 - 一開始沒有看到它。 – chux