簡體   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