繁体   English   中英

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

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

我希望循环在到达文件末尾时结束。 我知道当它到达它时,从 fscanf 返回的值将不同于 1,因为它在读取某些内容时返回 1。

如果我设置i=3循环是无限的,但如果我设置i=2循环结束,我觉得这很奇怪,因为控制表达式 (i!=3) 应该在调整之前评估,所以当我设置了i=3 ,它会中断并且应该比i!=3测试,这样循环就会结束(它没有)。 当我将它设置为 2 时,它就结束了,所以它必须再增加一次然后检查。

所以,我的第一个问题是为什么会发生这种情况?

我的第二个问题是,使用%[^\\n]s它只保存文件的开头,但只使用%s保存整个文件,但错误的是它只扫描到空间,但我希望它扫描直到新线。

我的文件每行有 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;
    }
  }

正在读取的文件示例:

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

结构:

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

新的循环:

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;
    }
  }

它打印什么

...当 fscanf 返回一个不同于 1 的值时会导致无限循环

当您将i设置为 3 时,将不会在i!=3 中测试该值,因为在测试之前将完成i++

i设置为 2


只有 %s ......它只扫描到我希望它扫描到新行的空间。

如果您想每行读取一行,请使用fgets而不是fscanf ,不要忘记删除可能的换行符

scanf系列中 's' 匹配一系列非空白字符,空格是分隔符

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.

警告您混合读取行和值,当您读取换行符的值时,不会读取换行符,将“%hu”替换为“%hu\\n”或非常安全地读取该行,然后从中提取数字(我在我的提议)


从你的话来看

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

您的 :

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

相当于

{ int i = 0;

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

这里有一个建议:

#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;
}

如你所见,用索引做你的事情是没用的,代码更清晰

假设文件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 $ 

如您所见,文件无效,原因是作者“WITHIN TEMPTATION & KEITH CAPUTO”更多换行符太长而无法保存在 20 个字符中,这就是为什么您总是需要检查发生了什么并且永远不要假设一切正常:在您的其他问题的初始代码中, fscanf写出具有未定义行为的项目。 例如,要使用(f/s)scanf读取最多 20 个字符,包括字符串中的空字符,请使用格式“%20s”

如果我将字段作者的大小调整为 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