2017-09-16 105 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