簡體   English   中英

C- 使用 fgets 或 sscanf 讀取多行文件?

[英]C- reading a multi-line file with fgets or sscanf?

比如說我有一個文本文件

person1
25
500
male
person2
..
..
..
person3
..

不管有多少人,我想將文件的 4 行讀入文件中每個人的結構。

我怎樣才能做到這一點? 我試過只使用多個 fget,但我不知道如何循環到文件末尾,同時一次讀取四行

謝謝

一些示例行。 我會讓你提供程序的 rest。

#define MAX 1000

  ...

  FILE *f;
  char line1[MAX], line2[MAX], line3[MAX], line4[MAX];

  ...

  while(fgets(line1, MAX, f) != NULL)
    {
      if (fgets(line2, MAX, f) == NULL ||
          fgets(line3, MAX, f) == NULL ||
          fgets(line4, MAX, f) == NULL)
        {
         /* insert code here to handle end of file in unexpected place */
          break;
        }
      /* insert code here to do your sscanf and anything else you want */
    }

    ....

繼續上面的評論,當您在數據文件中有一組固定的重復行需要讀入struct時,這是您應該考慮scanf()/fscanf()而不是推薦的fgets()/sscanf()的唯一例外之一fgets()/sscanf()的每一行。

為什么?

scanf()格式化的輸入 function (相比之下fgets()面向行的輸入 function )。 如果您有跨多行的格式化輸入, scanf()/fscanf()會忽略空格'\n'字符是空格),並允許您將多行作為單個輸入使用(使用正確制作的格式字符串

使用scanf()/fscanf()將數據讀入字符串(或數組)時,必須使用field-width修飾符來限制讀入數組的值的數量,以避免寫入超出數組末尾調用未定義行為如果輸入超出您的數組范圍。 每當您使用scanf()/fscanf()/sscanf() (整個系列)時,這都適用。 在沒有字段寬度修飾符的情況下使用來讀取數組數據並不比使用gets()更好。

那么如何制作你的格式字符串呢? 讓我們看一個具有 4 個成員的示例結構,類似於您在問題中顯示的內容,例如

...
#define MAXG 8      /* if you need a constant, #define one (or more) */
#define MAXP 32
#define MAXN 128

typedef struct {    /* struct with typedef */
    char name[MAXN], gender[MAXG];
    int iq, weight;
} person;
...

使用顯示的數據,並且name的聲明為128個字符, gender的聲明為8個字符,其余兩個成員為int類型,您可以執行類似於以下的操作:

    int rtn;                                /* fscanf return */
    size_t n = 0;                           /* number of struct filled */
    person ppl[MAXP] = {{ .name = "" }};    /* array of person */
    ...
    while (n < MAXP &&  /* protect struct array bound, and each array bound below */
            (rtn = fscanf (fp, " %127[^\n]%d%d %7[^\n]", /* validate each read */
                    ppl[n].name, &ppl[n].iq, &ppl[n].weight, ppl[n].gender)) == 4)
        n++;            /* increment array index */

具體查看格式字符串,您有:

    " %127[^\n]%d%d %7[^\n]"

其中" %127[^\n]" ,憑借前導' ' ,使用任何前導空格,然后最多讀取127個字符(不能使用變量或宏來指定field-width ),字符是任何字符在不是'\n'字符的行中(允許您讀取空格作為名稱的一部分,例如"Mickey Mouse" )。

請注意, "%[...]是一個字符串轉換,它將讀取字符列表中的任何字符[...]作為字符串。使用抑揚符'^'作為列表的第一個字符會否定匹配,從而導致"%[^\n]"將不包括'\n'的所有字符讀入字符串。

" %[^\n]"之前的空格是必需的,因為像"%c"這樣的 " "%[...]"是唯一不消耗前導空格轉換說明符,因此您可以通過在之前包含空格來提供這一點格式字符串中的轉換。 int的其他兩個轉換說明符,例如"%d"將自行消耗前導空格,從而導致總轉換:

    " %127[^\n]%d%d %7[^\n]"

總而言之,這將:

  • 使用任何前導空格(先前讀取的stdin中留下的'\n'或數組中前一個結構的gender );
  • 使用%127[^\n]將最多 127 個字符的行讀入name成員;
  • 將包含第一個 integer 值的行用%d讀取到iq中(這會消耗前導空格);
  • 使用%d將包含第二個 integer 值的行讀入weight (同上);
  • ' '消耗讀取weight剩下的'\n' 最后
  • 使用%7[^\n]將最多 7 個字符的行讀入gender成員(根據需要進行調整以保留最長的性別字符串)

使用這種方法,您可以通過一次調用fscanf()將 4 行輸入消耗到數組中的每個結構中。 您應該在循環退出時檢查rtn ,以確保在從文件中讀取所有值后,循環在EOF上退出。 一個簡單的檢查將涵蓋所需的最低驗證,例如

    if (rtn != EOF) /* if loop exited on other than EOF, issue warning */
        fputs ("warning: error in file format or array full.\n", stderr);

注意:您還可以檢查是否n == MAXP以查看循環退出的原因是否是由於單獨的數組已滿)。

總而言之,你可以這樣做:

#include <stdio.h>

#define MAXG 8      /* if you need a constant, #define one (or more) */
#define MAXP 32
#define MAXN 128

typedef struct {    /* struct with typedef */
    char name[MAXN], gender[MAXG];
    int iq, weight;
} person;

int main (int argc, char **argv) {

    int rtn;                                /* fscanf return */
    size_t n = 0;                           /* number of struct filled */
    person ppl[MAXP] = {{ .name = "" }};    /* array of person */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (n < MAXP &&  /* protect struct array bound, and each array bound below */
            (rtn = fscanf (fp, " %127[^\n]%d%d %7[^\n]", /* validate each read */
                    ppl[n].name, &ppl[n].iq, &ppl[n].weight, ppl[n].gender)) == 4)
        n++;            /* increment array index */

    if (rtn != EOF) /* if loop exited on other than EOF, issue warning */
        fputs ("warning: error in file format or array full.\n", stderr);

    for (size_t i = 0; i < n; i++)  /* output results */
        printf ("\nname   : %s\niq     : %d\nweight : %d\ngender : %s\n",
                ppl[i].name, ppl[i].iq, ppl[i].weight, ppl[i].gender);

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
}

注意:您也可以使用全局enum來定義常量)

示例輸入文件

$ cat dat/ppl.txt
person1
25
500
male
person2
128
128
female
Mickey Mouse
56
2
male
Minnie Mouse
96
1
female

示例使用/輸出

$ ./bin/readppl dat/ppl.txt

name   : person1
iq     : 25
weight : 500
gender : male

name   : person2
iq     : 128
weight : 128
gender : female

name   : Mickey Mouse
iq     : 56
weight : 2
gender : male

name   : Minnie Mouse
iq     : 96
weight : 1
gender : female

您還可以使用行計數器或多行讀取方法使用fgets()讀取每一行,但這更多是關於為工作選擇合適的工具。 使用fgets()然后多次調用sscanf()以獲取 integer 值或兩次調用strtol()進行轉換沒有任何問題,但是對於大型輸入文件,對fscanf()的 1 個函數調用與 4-對fgets()的單獨調用加上對sscanf()strtol()的 2 次單獨調用以及用於處理行計數器或多緩沖區邏輯的附加邏輯將開始累加。

如果您還有其他問題,請仔細查看並告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM