【问题标题】:C Reading a file of digits separated by commasC读取以逗号分隔的数字文件
【发布时间】:2019-03-22 14:09:09
【问题描述】:

我正在尝试读取包含逗号操作的数字的文件,并将它们存储在不存在逗号的数组中。

例如:processes.txt 包含

0,1,3
1,0,5
2,9,8
3,10,6

一个叫做 numbers 的数组应该是这样的:

0 1 3 1 0 5 2 9 8 3 10 6

到目前为止我的代码是:

FILE *fp1;
char c; //declaration of characters

fp1=fopen(argv[1],"r"); //opening the file



int list[300];


c=fgetc(fp1); //taking character from fp1 pointer or file
int i=0,number,num=0;

while(c!=EOF){ //iterate until end of file
    if (isdigit(c)){ //if it is digit
        sscanf(&c,"%d",&number); //changing character to number (c)
        num=(num*10)+number;

    }
    else if (c==',' || c=='\n') { //if it is new line or ,then it will store the number in list
        list[i]=num;
        num=0;
        i++;

    }

    c=fgetc(fp1);

}

但如果是两位数,就会出现问题。有没有人有更好的解决方案?谢谢!

【问题讨论】:

  • 请注意fgetc返回一个int。这实际上对您的EOF 支票非常重要。
  • c 更改为int,否则while(c!=EOF) 将不起作用。 sscanf(&c,"%d",&number); 应该是 number = c - '0';
  • 另外请注意&c 不是以空值结尾的字符串。您不能这样使用它(例如作为sscanf 中的来源)。
  • 一个非常常见的错误。它必须是一个 int,否则 EOF 必须在 0 到 255 的范围内,这些都是有效的 char 值。

标签: c


【解决方案1】:

对于逗号前没有空格显示的数据,您可以简单地使用:

while (fscanf(fp1, "%d,", &num) == 1 && i < 300)
    list[i++] = num;

如果有,则会读取数字后面的逗号,如果没有,则默认忽略。如果数据中的逗号前可能有空格,请在格式字符串中的逗号前添加一个空格。 i 上的测试可防止您在 list 数组的范围之外进行写入。 ++ 运算符在这里独树一帜。

【讨论】:

    【解决方案2】:

    首先,fgetc 返回一个int,所以c 需要是一个int

    除此之外,我会使用稍微不同的方法。我承认这有点过于复杂了。但是,如果您有几种不同类型的字段需要不同的操作(例如解析器),则此方法可能是可用的。对于您的具体问题,我推荐 Johathan Leffler 的回答。

    int c=fgetc(f);
    
    while(c!=EOF && i<300) {
        if(isdigit(c)) {
            fseek(f, -1, SEEK_CUR);
            if(fscanf(f, "%d", &list[i++]) != 1) {
                // Handle error
            }
        }
        c=fgetc(f);
    }
    

    这里我不关心逗号和换行符。我将除数字以外的任何内容作为分隔符。我所做的基本上是这样的:

    read next byte
    if byte is digit:
         back one byte in the file
         read number, irregardless of length
    else continue
    

    添加条件i&lt;300 是出于安全原因。如果您真的只想检查逗号和换行符(我没有得到您认为这很重要的印象),您可以轻松地添加 else if (c == ... 来处理错误。

    请注意,您应该始终检查sscanffscanfscanf 等函数的返回值。实际上,您也应该为fseek 执行此操作。在这种情况下,它并不那么重要,因为此代码不太可能因此而失败,因此为了便于阅读,我将其省略了。但是在生产代码中你应该检查它。

    【讨论】:

    • 如果argv[1]/dev/tty,则搜索将不起作用。您可以可靠地使用ungetc()。我不相信“一次一个角色”的方法是明智的,因为它可以用fscanf() 如此简洁地完成——就像我的回答一样。
    【解决方案3】:

    我的解决方案是先读取整行,然后用strtok_r 解析它,并用逗号作为分隔符。如果你想要可移植的代码,你应该改用strtok

    一个简单的 readline 实现应该是这样的:

    static char *readline(FILE *file)
    {
        char *line = malloc(sizeof(char));
        int index = 0;
        int c = fgetc(file);
        if (c == EOF) {
            free(line);
            return NULL;
        }
        while (c != EOF && c != '\n') {
            line[index++] = c;
            char *l = realloc(line, (index + 1) * sizeof(char));
            if (l == NULL) {
                free(line);
                return NULL;
            }
            line = l;
            c = fgetc(file);
        }
        line[index] = '\0';
        return line;
    }
    

    然后你只需要用strtok_r解析整行,所以你会以这样的方式结束:

    int main(int argc, char **argv)
    {
        FILE *file = fopen(argv[1], "re");
        int list[300];
        if (file == NULL) {
            return 1;
        }
        char *line;
        int numc = 0;
        while((line = readline(file)) != NULL) {
            char *saveptr;
            // Get the first token
            char *tok = strtok_r(line, ",", &saveptr);
            // Now start parsing the whole line
            while (tok != NULL) {
                // Convert the token to a long if possible
                long num = strtol(tok, NULL, 0);
                if (errno != 0) {
                    // Handle no value conversion
                    // ...
                    // ...
                }
                list[numc++] = (int) num;
                // Get next token
                tok = strtok_r(NULL, ",", &saveptr);
            }
            free(line);
        }
        fclose(file);
        return 0;
    }
    

    要打印整个列表,只需使用 for 循环:

    for (int i = 0; i < numc; i++) {
        printf("%d ", list[i]);
    }
    printf("\n");
    

    【讨论】:

    • 在 Windows 上,你会使用 strtok_s() 而不是 strtok_r() — 除了名称,界面和效果是一样的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多