【问题标题】:How can I convert this "for" loop to use EOF in C?如何将此“for”循环转换为在 C 中使用 EOF?
【发布时间】:2014-02-13 02:42:55
【问题描述】:
char* readScores(int* actualCount) {

char* stringArray = (char*)malloc(100*sizeof(char));

for(int i = 0; i <= 13; i++) {
    *actualCount = *actualCount + 1;
    scanf("%s", &stringArray[i]);
    printf("stringArray = %s when i = %d\t actualCount = %d\n", &stringArray[i], i, *actualCount);
}
printf("Finished!");

return stringArray;
}

使用这段代码,我正在循环一个包含一些单词的 .txt 文件。当我运行这段代码时,我发现我确实在数组中的正确位置得到了正确的单词,但是一旦循环到达文件的末尾,程序就会给我一个段错误。我猜这是因为循环达到 EOF 值并引发错误,但我不知道如何修复它。这是结果的截图:http://i.imgur.com/kduzBnp.png

感谢您的帮助!

编辑

我将代码更改为:

while(scanf("%s", &stringArray) != EOF) {
    *actualCount = *actualCount + 1;
    printf("stringArray = %s when actualCount = %d\n", &stringArray, *actualCount);
}

但是,我仍然收到具有相同输出值的段错误。所以我知道它正在正确读取文件,我只是不明白为什么它会在最后停止并引发错误。

【问题讨论】:

  • 代码可以使用scanf()的返回值,但更好的方法是使用fgets()
  • 这个函数到底应该做什么?如所写,它从标准输入读取多个字,返回指向最后一个字的指针,并将&amp;actualCount 设置为读取的字数(假设您传入一个指向初始值为0 的int 的指针)。
  • 这个特殊的功能应该可以做到这一点。程序作为一个整体读取文件,计算每个单词出现的次数,然后按出现次数对它们进行排序。我不知道为什么它给了我一个错误。
  • 我认为它实际上会返回一个指向字符串的指针,该字符串由除最后一个字符串之外的所有字符串的第一个字符组成,然后是最后一个字符。
  • 为什么要这样做?关于 scanf 的工作原理,我是否遗漏了一些东西?我怎样才能改变它?

标签: c string loops eof


【解决方案1】:

只要scanf 成功地从标准输入中读取一个单词,您就想继续循环。

所以循环的终止条件应该使用scanf返回的结果。

scanf 返回成功读取的项目数。如果成功读取一个单词,它将返回1,否则返回0EOF

但是您的代码还有一些其他问题。

char* stringArray = (char*)malloc(100*sizeof(char));

这不会为 100 个字符串分配空间,甚至不会为 100 个指向字符串的指针分配空间。它只是为 100 个 chars 的单个数组分配空间。最好写成:

char *stringArray = malloc(100);

强制转换malloc 的结果是不必要的,可以掩盖错误,sizeof(char) 定义为 1。

for 循环将连续的字读入内存,从分配数组中的位置 0 开始,然后是位置 1,然后是位置 2,依此类推,每次读取都会覆盖前一个的部分。

此外,具有"%s" 格式的scanf 本质上是不安全的,因为无论目标数组的大小如何,它都会读取与用户类型(或从stdin 连接的任何内容)一样多的字符。

使用fgets() 读取文本行,然后使用sscanf 解析输入可能更安全(尽管sscanf 有其自身的一些问题)。

如果你想存储多个字符串,你需要为它们分配空间。我不太确定你想提供更具体的建议。

【讨论】:

  • 查看我的编辑。我们还没有学会如何使用 fgets(),所以我不会在这里使用它。我也不太担心 malloc 问题之类的问题,除非它们会影响最终结果,所以我不会更改它们。不过,我感谢您的帮助!
  • 你最好担心malloc()的问题,除非你想对这个程序感到沮丧很长时间。如果你不这样做,它会破坏你的功能的全部目的。
  • 好的,我将 malloc() 更改为您的版本,但我仍然遇到段错误。
  • @PaulGriffiths:malloc 调用并不完全不正确。 sizeof(char) 的转换和乘法没有错,只是没有必要。
  • @KeithThompson:我认为他的malloc() 调用是不正确的,因为它没有做他想做的事情,除非调用他的变量stringArray&amp;stringArray[i] 的原始使用都是红鲱鱼而他只想保留其中一个词,这样以后就很难数数和排序了。
【解决方案2】:

scanf() 通常是一个不好用的函数,但是,从 cmets 收集到,如果你坚持使用它,下面的代码会做我认为你想做的事情,在这里:

#include <stdio.h>
#include <stdlib.h>

char ** create_string_array(const size_t num_strings, const size_t maxlen) {
    char ** new_array = malloc(num_strings * sizeof(*new_array));
    if ( new_array == NULL ) {
        perror("Couldn't allocate memory for string array.");
        exit(EXIT_FAILURE);
    }

    for ( size_t i = 0; i < num_strings; ++i ) {
        new_array[i] = malloc(maxlen);
        if ( new_array[i] == NULL ) {
            perror("Couldn't allocate memory for string.");
            exit(EXIT_FAILURE);
        }
    }

    return new_array;
}

void free_string_array(char ** array, const size_t num_strings) {
    for ( size_t i = 0; i < num_strings; ++i ) {
        free(array[i]);
    }
    free(array);
}

char ** readScores(int* actualCount) {

    char ** stringArray = create_string_array(14, 100);

    for (int i = 0; i < 14; ++i) {
        int status = scanf("%s", stringArray[i]);
        if ( status == 0 || status == EOF ) {
            break;
        }
        *actualCount = *actualCount + 1;
        printf("stringArray = %s when i = %d\t actualCount = %d\n",
            stringArray[i], i, *actualCount);
    }
    printf("Finished!\n");

    return stringArray;
}

int main(void) {
    int num_strings = 0;
    char ** stringArray = readScores(&num_strings);

    printf("There were %d strings read. They are:\n", num_strings);

    for ( int i = 0; i < num_strings; ++i ) {
        printf("%2d: %s\n", i + 1, stringArray[i]);
    }

    free_string_array(stringArray, num_strings);

    return EXIT_SUCCESS;
}

并带有合适的数据文件输出:

paul@local:~/src/c/scratch$ cat data_file
Twas
brillig
and
the
slithy
toves
did
gyre
and
gimble
in
the
wabe
paul@local:~/src/c/scratch$ cat data_file | ./strcount
stringArray = Twas when i = 0    actualCount = 1
stringArray = brillig when i = 1     actualCount = 2
stringArray = and when i = 2     actualCount = 3
stringArray = the when i = 3     actualCount = 4
stringArray = slithy when i = 4  actualCount = 5
stringArray = toves when i = 5   actualCount = 6
stringArray = did when i = 6     actualCount = 7
stringArray = gyre when i = 7    actualCount = 8
stringArray = and when i = 8     actualCount = 9
stringArray = gimble when i = 9  actualCount = 10
stringArray = in when i = 10     actualCount = 11
stringArray = the when i = 11    actualCount = 12
stringArray = wabe when i = 12   actualCount = 13
Finished!
There were 13 strings read. They are:
 1: Twas
 2: brillig
 3: and
 4: the
 5: slithy
 6: toves
 7: did
 8: gyre
 9: and
10: gimble
11: in
12: the
13: wabe
paul@local:~/src/c/scratch$ 

Keith 已经指出了您遇到的基本错误,即,如果您需要一个动态的字符串数组,那么您需要在 malloc() 内存中获取指向 char * 的指针数组,然后对于每个指针,@987654326 @内存用于字符串本身,您的原始代码没有这样做,您需要检查来自scanf() 的返回以获取0EOF

【讨论】:

    【解决方案3】:

    你可以用 while 循环替换你的 for 循环。

    while (fgets(buffer, buffer_size, fp)) {
        // do something with buffer
    }
    if (ferror(fp)) {
        // handle the error, usually exit or return
    } else {
        // continue execution
    }
    

    【讨论】:

    • 不过,这不是 C。
    • 很抱歉让您快速阅读它。想想我该睡觉了。
    【解决方案4】:
    char* readScores(int* actualCount) {
        char* stringArray = (char*)malloc(100*sizeof(char));
        int i=0, len;
        while(scanf("%s%n%*c", &stringArray[i], &len)==1){
            printf("stringArray = %s when i = %d\t actualCount = %d\n", &stringArray[i], i, ++*actualCount);
            i += len + 1;
        }
        printf("Finished!");
    
        return stringArray;
    }
    

    【讨论】:

      【解决方案5】:

      您提供的代码没有问题。我自己和我在 Windows 上的 MSVC 在使用它时都没有遇到任何麻烦。下面是它的样子:

      #include <stdio.h>
      #include <stdlib.h>
      #include <Windows.h>
      
      int main( )
      {
          FILE * asd;
          char* stringArray = calloc( 100, sizeof(char) );
          int actualCount = 0;
      
          freopen_s( &asd, "wsx.txt", "w+", stdin );
      
          fprintf_s( stdin, "this is a test this is only a test this also is a test" );
          rewind( stdin );
      
          for ( int i = 0; i <= 13; i++ ) {
              actualCount = actualCount + 1;
              scanf_s( "%s", &stringArray[i], 100 - i );
              printf_s( "stringArray = %s when i = %d\t actualCount = %d\n", &stringArray[i], i, actualCount );
          }
          printf( "Finished!" );
      
          Sleep( 5000 );
          return 0;
      }
      

      并且输出与您期望的一样,没有错误...所以我的回答是您遇到的问题是由于您编写的代码以外的其他原因造成的。

      【讨论】:

      • 1.在你的答案中包含&lt;Windows.h&gt; 有点糟糕,在这里; 2.“您提供的代码没有问题”显然不正确; 3.您的代码使用fscanf_s(),这是一个与OP代码使用的完全不同的功能,您不能真正声称“您提供的代码没有问题”,因为您编译了不同的代码并且没有发现问题。
      • @PaulGriffiths 意见 [...] 说真的,Windows.h 是为Sleep如果你说它是一个坏味道,那就是一个坏味道。尽管如此,它们可以被省略。休息只是诬告...
      • 您的代码通过写入文件然后立即读取而调用未定义的行为,而无需介入定位操作(fseek() 等)或fflush()。您的 fscanf_s() 很可能会获得 EOF,因为文件位置位于文件末尾。你无法判断它是否有效,因为你没有测试它的返回值。你的测试数据不超过100个字符,所以有些问题没有触发。对于这段代码,酸性测试是在循环之后打印“字符串数组”中的数据。
      • 另外,你误用了fscanf_s();当您使用%s 格式时,您必须提供两个参数:字符指针以及存储在指针中的最大允许长度。电话应该是if (fscanf_s(asd, "%s", &amp;stringArray[i], 100-i) != 1) …report error…
      • @JonathanLeffler 谢谢,今天下午我也意识到了这一点,并在这里进行了更改。在这里也更正了。
      猜你喜欢
      • 2023-04-01
      • 2021-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-22
      相关资源
      最近更新 更多