简体   繁体   English

当 fscanf 返回一个不同于 1 的值时,循环中的条件语句检查变量集导致无限循环

[英]Conditional statement in loop checks against variable set when fscanf returns a value different than 1 causes an infinite loop

I'm expecting the loop to end when the end of the file is reached.我希望循环在到达文件末尾时结束。 I know that when it reaches it the value returned from fscanf will be different than 1, as it returns 1 whenever it reads something.我知道当它到达它时,从 fscanf 返回的值将不同于 1,因为它在读取某些内容时返回 1。

If I set i=3 the loop is infinite, but if I set i=2 the loop ends, which I find very weird, as the controlling expression (i!=3) is supposed to be evaluated before the adjustment one, so when I set i=3 , it breaks and should test than indeed i!=3 so the loop would end (which it doesn't).如果我设置i=3循环是无限的,但如果我设置i=2循环结束,我觉得这很奇怪,因为控制表达式 (i!=3) 应该在调整之前评估,所以当我设置了i=3 ,它会中断并且应该比i!=3测试,这样循环就会结束(它没有)。 When I set it to 2, it ends, so it must be incrementing it one more time and then checking.当我将它设置为 2 时,它就结束了,所以它必须再增加一次然后检查。

So, my first question is why is this happening?所以,我的第一个问题是为什么会发生这种情况?

My second question is that with the %[^\\n]s it only saves the start of the file, but with only %s it saves the whole file, but wrongly as it only scans until the space, but I want it to scanf until the new line.我的第二个问题是,使用%[^\\n]s它只保存文件的开头,但只使用%s保存整个文件,但错误的是它只扫描到空间,但我希望它扫描直到新线。

My file has 1 element per line (some with spaces)我的文件每行有 1 个元素(有些带有空格)

for(int i=0;i!=3;i++){
    switch(i){
      case 0:
        if(fscanf(recordsRegistry,"%[^\n]s", (recordsArray[recordsArrayPosition]).author)!=1){
          i=3;//stop condition
        }
        break;
      case 1:
        if(fscanf(recordsRegistry,"%[^\n]s", (recordsArray[recordsArrayPosition]).title)!=1){
          i=3;//stop condition
        }
        break;
      case 2:
        if(fscanf(recordsRegistry,"%hu", &((recordsArray[recordsArrayPosition]).numberOfSales))!=1){
          i=3;//stop condition
        }
        i=-1;
        recordsArrayPosition++;
        totalRecords++;
        recordsArray=realloc(recordsArray, totalRecords*recordStructSize) ;
        if(recordsArray==NULL){
          fprintf(stderr, "Could not reallocate memory at line %d.\n", __LINE__);
          return 3;
        }  
        break;
    }
  }

Example of the file being read:正在读取的文件示例:

LEANN RIMES
WHAT A WONDERFUL WORLD
4628
BLUE CHEER
WHAT DOESNT KILL YOU
9664
WITHIN TEMPTATION & KEITH CAPUTO
WHAT HAVE YOU DONE
3226
WITHIN TEMPATION
WHAT HAVE YOU DONE
8093
KOKO TAYLOR
WHAT IT TAKES (THE CHESS YEARS)
7160
DOOBIE BROTHERS
WHAT WERE ONCE VICES ARE NOW HABITS
2972
LIL'ED & THE BLUES IMPERIALS
WHAT YOU SEE IS WHAT YOU GET
9443
VARIOUS ARTISTS
WHAT'S SHAKIN
4473

The struct:结构:

typedef struct{
  char author[20], title[50];
  short unsigned int numberOfSales;
} RECORD;

New for looop:新的循环:

for(int i=0;i!=3;i++){
    switch(i){
      case 0:
        if(fgets(recordsArray[recordsArrayPosition].author, totalRecords, recordsRegistry)==NULL){
          //printf("aa\n");
          i=2;//stop condition
        }
        break;
      case 1:
        if(fgets(recordsArray[recordsArrayPosition].title, totalRecords, recordsRegistry)==NULL){
          //printf("aaa\n");
          i=2;//stop condition
        }
        break;
      case 2:
        if(fscanf(recordsRegistry,"%hu", &((recordsArray[recordsArrayPosition]).numberOfSales))!=1){
          //printf("aaaa\n");
          i=2;//stop condition
        }
        i=-1;
        recordsArrayPosition++;
        totalRecords++;
        recordsArray=realloc(recordsArray, totalRecords*recordStructSize) ;
        if(recordsArray==NULL){
          fprintf(stderr, "Could not reallocate memory at line %d.\n", __LINE__);
          return 3;
        }  
        break;
    }
  }

它打印什么

... when fscanf returns a value different than 1 causes an infinite loop ...当 fscanf 返回一个不同于 1 的值时会导致无限循环

when you set i to 3 that value will not be tested in i!=3 because before the test the i++ will be done当您将i设置为 3 时,将不会在i!=3 中测试该值,因为在测试之前将完成i++

set i to 2i设置为 2


with only %s ... it only scans until the space I want it to scanf until the new line.只有 %s ......它只扫描到我希望它扫描到新行的空间。

if you want to read line per line use fgets rather than fscanf , do not forget to remove the probable newline如果您想每行读取一行,请使用fgets而不是fscanf ,不要忘记删除可能的换行符

in the scanf family 's' matches a sequence of non-white-space characters, spaces are separatorscanf系列中 's' 匹配一系列非空白字符,空格是分隔符

man scanf says : man scanf说:

   s      Matches a  sequence  of  non-white-space  characters;  the  next
          pointer  must be a pointer to the initial element of a character
          array that is long enough to hold the  input  sequence  and  the
          terminating null byte ('\0'), which is added automatically.  The
          input string stops at white space or at the maximum field width,
          whichever occurs first.

warning you mix to read line and value, when you read the value the newline is not read, replace "%hu" by "%hu\\n" or much secure read the line then extract the number from it (I do that in my proposal)警告您混合读取行和值,当您读取换行符的值时,不会读取换行符,将“%hu”替换为“%hu\\n”或非常安全地读取该行,然后从中提取数字(我在我的提议)


from your remark从你的话来看

why will i++ test before i!=3?为什么 i++ 会在 i!=3 之前进行测试?

your :您的 :

 for(int i=0;i!=3;i++){ <body without continue> }

is equivalent to相当于

{ int i = 0;

  while (i != 3) {
    <body without continue>
    i++;
  }
}

Here a proposal :这里有一个建议:

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

typedef struct{
  char author[20], title[50];
  short unsigned int numberOfSales;
} RECORD;

#define MAXRECORDS 100

void removeEndSpaces(char * s)
{
  char * p = s + strlen(s);

  while ((s != p) && isspace((unsigned char) *--p))
    *p = 0;
}

int main()
{
  FILE * fp = fopen("f", "r");
  RECORD records[MAXRECORDS];
  int nrecords;
  char line[32];

  if (fp == NULL){
    perror("cannot read f");
    return -1;
  }

  for (nrecords = 0; nrecords != MAXRECORDS; nrecords += 1) {
    if (fgets(records[nrecords].author, sizeof(records[nrecords].author), fp) == NULL)
      break;
    removeEndSpaces(records[nrecords].author);

    if (fgets(records[nrecords].title, sizeof(records[nrecords].title), fp) == NULL) {
      fprintf(stderr, "invalid input file\n");
      break;
    }
    removeEndSpaces(records[nrecords].title);

    /* the more secure way to read the number is first to read the line then read the enumber in that line */
    if ((fgets(line, sizeof(line), fp) == NULL) ||
        (sscanf(line, "%hu", &records[nrecords].numberOfSales) != 1)) {
      fprintf(stderr, "invalid input file\n");
      break;
    }
  }

  /* nrecords values the number of records read without error */
  for (int i = 0; i != nrecords; i += 1)
    printf("%s : %s / %hu\n", 
           records[i].author, records[i].title, records[i].numberOfSales);

  return 0;
}

As you see it is useless to do your stuff with the index and the code is more clear如你所见,用索引做你的事情是没用的,代码更清晰

Supposing the file f contains your input, compilation and execution :假设文件f包含您的输入、编译和执行:

pi@raspberrypi:/tmp $ gcc -Wall -Werror -pedantic a.c -g
pi@raspberrypi:/tmp $ ./a.out
invalid input file
LEANN RIMES : WHAT A WONDERFUL WORLD / 4628
BLUE CHEER : WHAT DOESNT KILL YOU / 9664
pi@raspberrypi:/tmp $ 

As you see the file is invalid, the reason is the author "WITHIN TEMPTATION & KEITH CAPUTO" more the newline is too long to be saved in 20 characters, this is why you always need to check what happens and never suppose all is ok : in your initial code out of your other problems fscanf write out of the items with an undefined behavior.如您所见,文件无效,原因是作者“WITHIN TEMPTATION & KEITH CAPUTO”更多换行符太长而无法保存在 20 个字符中,这就是为什么您总是需要检查发生了什么并且永远不要假设一切正常:在您的其他问题的初始代码中, fscanf写出具有未定义行为的项目。 To read for instance up to 20 characters including the null character in a string with (f/s)scanf use the format "%20s"例如,要使用(f/s)scanf读取最多 20 个字符,包括字符串中的空字符,请使用格式“%20s”

If I resize the field author to 40 all is ok :如果我将字段作者的大小调整为 40,则一切正常:

pi@raspberrypi:/tmp $ gcc -Wall -Werror -pedantic a.c -g
pi@raspberrypi:/tmp $ ./a.out
LEANN RIMES : WHAT A WONDERFUL WORLD / 4628
BLUE CHEER : WHAT DOESNT KILL YOU / 9664
WITHIN TEMPTATION & KEITH CAPUTO : WHAT HAVE YOU DONE / 3226
WITHIN TEMPATION : WHAT HAVE YOU DONE / 8093
KOKO TAYLOR : WHAT IT TAKES (THE CHESS YEARS) / 7160
DOOBIE BROTHERS : WHAT WERE ONCE VICES ARE NOW HABITS / 2972
LIL'ED & THE BLUES IMPERIALS : WHAT YOU SEE IS WHAT YOU GET / 9443
VARIOUS ARTISTS : WHAT'S SHAKIN / 4473
pi@raspberrypi:/tmp $ 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM