从C中的文件读取一行并提取输入数

问题描述

我有一个文件 input.dat 。在此文件中,共有3行:

1 2 3
5 7 10 12
8 9 14 13 15 17

我将使用C读取三行之一,并返回元素的数量。 例如,我想将第二行5 7 10 12读入内存,并返回第二行中的值数4。我的代码在下面...

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

#define STRING_SIZE 2000

int main() {
    FILE *fp = fopen("in.dat","r");
    char line[STRING_SIZE];
    int lcount = 0,nline = 1,sum = 0,number;

    if (fp != NULL) {
        while (fgets(line,STRING_SIZE,fp) != NULL) {
            if (lcount == nline) {
                while (sscanf(line,"%d ",&number)) {
                    sum++;
                }
                break;
            } else {
                lcount++;
            }
        }
        fclose(fp);
    }
    exit(0);
}

运行此代码时,它永远不会像死循环一样停止。这是什么问题?

解决方法

循环while (sscanf(line,"%d ",&number))继续解析该行中的第一个数字。

您应该改用strtol

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

#define STRING_SIZE 2000

int main() {
    FILE *fp = fopen("in.dat","r");
    char line[STRING_SIZE];
    int lcount = 0,nline = 1;

    if (fp != NULL) {
        while (fgets(line,STRING_SIZE,fp) != NULL) {
            if (lcount == nline) {
                char *p = line,*q;
                int count = 0;
                for (;;) {
                    long val = strtol(p,&q,0);    // parse an integer
                    if (q == p) {
                        // end of string or not a number
                        break;
                    }
                    // value was read into val. You can use it for whatever purpose
                    count++;
                    p = q;
                }
                printf("%d\n",count);
                break;
            } else {
                lcount++;
            }
        }
        fclose(fp);
    }
    return 0;
}
,

chqrlie 答案的较干净版本。以字符串开头,这就是fgets()之后的问题所在。

sscanf()不会单步执行字符串,而是始终从头开始读取。

strtol()在字符串的开头查找long int,忽略初始空格。返回停止扫描的地址。

strtol()的手册中说,应该检查errno是否存在任何转换错误。

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

#define STRING_SIZE 2000

int main(void)
{
    char line[STRING_SIZE] = "5 7 10 12";

    char* start = line;
    char* end;

    int count = 0;

    while(1)
    {
        /**
         * strtol() look for long int in beginning of the string
         * Ignores beginning whitespace
         * 
         * start: where to strtol() start looking for long int
         * end: where strtol() stops scanning for long int
         */
        errno = 0; // As strol() manual says

        strtol(start,&end,0);

        if (errno != 0)
        {
            printf("Error in strtol() conversion.\n");
            exit(0);
        }

        if (start == end) break; // Quit loop

        start = end;
        count++;
    }
    

    printf("count: %d\n",count);

    return 0;
}
,

您正在使用sscanf()来思考正确的方法,唯一缺少的难题是如何对line应用偏移量,以便您读取{与您下一次致电sscanf()的通话保持一致。您可以通过使用sscanf()转换跟踪每次调用"%n"所消耗的字符数来完成此操作(它不会添加到sscanf()返回的转换计数中),例如读取行从打开的文件流fp中,您可以执行以下操作:

#define MAXC  1024      /* if you need a constant,#define one (or more) */
...
    char line[MAXC] = "";   /* buffer to hold each line */
    ...
    while (fgets (line,MAXC,fp)) {    /* reach each line in file */
        int offset = 0,/* offset in line for next sscanf() read */
            nchr = 0,/* number of char consumed by last read */
            val,/* integer value read with sscanf() */
            nval = 0;                   /* number of values read in line */
        /* conververt each integer at line + offset,saving no. of chars consumed */
        while (sscanf (line + offset,"%d%n",&val,&nchr) == 1) {
            printf (" %d",val);        /* output value read */
            offset += nchr;             /* update offset with no. chars consumend */
            nval++;                     /* increment value count */
        }
        printf ("  -  %d values\n",nval);  /* output no. values in line */
    }

注意strtol()比转换失败的sscanf()提供更好的错误报告)

如果将其与一个示例结合在一起,该示例从提供给程序的第一个参数的文件名中读取(如果没有给出参数,则默认从stdin读取),您可以这样做:

#include <stdio.h>

#define MAXC  1024      /* if you need a constant,#define one (or more) */

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

    char line[MAXC] = "";   /* buffer to hold each line */
    /* 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 (fgets (line,nval);  /* output no. values in line */
    }

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

使用/输出示例

使用文件名dat/nvals.txt中显示的数据,您将得到:

$ ./bin/fgetsnvals dat/nvals.txt
 1 2 3  -  3 values
 5 7 10 12  -  4 values
 8 9 14 13 15 17  -  6 values

仔细检查一下,如果还有其他问题,请告诉我。