fscanf格式说明符 - 以或不带注释字符串结尾的列式数字数据

时间:2016-11-16 04:01:59

标签: c format scanf

我有一个数据文件,其中包含3个包含浮点数的列(假设数字列现在是固定的),但最后一列是包含空格(但在一行中)的字符串。

面临的问题不是所有文本行文件都包含最后一个注释字符串,否则使用

fscanf(<file pointer>, %f %f %f %[^\n]%*c,&var1,&var2,&var3,temp)有效

如下:(对于3行数据)

FILE *fp1=NULL;
printf("HELLO\n");
double value1 = 0.00,value2 = 0.00,value3=0.00;
int i=0;
char temp[200];

fp1 = fopen(textfile,"r");
rewind(fp1);
if(fp1 == 0)    { 
    perror("ERROR OPENING FILE ");
    return ;
}
char format[] = "%lf %lf %lf %[^\n]s%*c";
i=3;
while(i--)
{
    fscanf(fp1,format,&value1,&value2,&value3,temp) ;
    printf("%lf,%lf,%lf - ",value1,value2,value3);
    printf("%s\n",temp);
}
fclose(fp1);

上面的代码适用于每一行是否包含一个注释作为最后一列,但如果注释不在行的末尾,那么行将被合并。

例如,对于包含以下数据的文件,它失败:

1.00 1.1 1.4 //this is first line 
2.00 2.1 2.4 
4.00 4.1 4.4 //this is fourth line
3.00 3.1 3.4
5.00 5.1 5.4 //this is fifth line

并输出为:

HELLO
1.000000,1.100000,1.400000 - //this is first line
2.000000,2.100000,2.400000 - 4.00 4.1 4.4 //this is fourth line
3.000000,3.100000,3.400000 - 5.00 5.1 5.4 //this is fifth line

希望我的问题很明确。

2 个答案:

答案 0 :(得分:1)

嗯,当然它失败了,因为你明确要求用空格分隔三个双打,然后是另一个空格,然后是一些任意长的文本,然后是另一个字符(换行符)。

您需要将评论设为可选。使用fscanf()fgets()的组合:

char format[] = "%lf %lf %lf";

fscanf(fp1, format, &value1, &value2, &value3);

if (fgets(temp, 200, fp1)) {
    temp[strcspn(temp, "\n")] = '\0';
}

答案 1 :(得分:0)

问题很微妙。在scanf() - 系列格式字符串中,空格表示0或更多空格字符,表示空格,制表符或换行符。此外,除%c%[…]%n之外的所有格式说明符都跳过可选的前导空格。并且函数不关心白色空间中的换行(换行仅在扫描集中起作用)。如果您需要面向行的输入,那么您应该阅读fgets()或POSIX的getline()行,然后使用sscanf()处理字符串。

您的格式字符串是:

char format[] = "%lf %lf %lf %[^\n]s%*c";

当代码到达数据行时:

2.00 2.1 2.4 
4.00 4.1 4.4 //this is fourth line

格式会读取三个数字2.002.12.4;然后它扫描一些空格,读取换行符,找到4 4.00,然后将扫描设置处理到换行符,然后用{{1}读取并丢弃换行符}。

如果您想坚持使用%*cscanf(),则部分修复方法是在扫描集之前删除空格。您还需要或多或少地分析fscanf()的返回值,如下所示。请注意,当没有注释时,换行符将保留在输入缓冲区中,但下一个fscanf()将在下一个数字之前跳过换行符。

但是,您最好转向基于行的输入。使用fscanf()而不是直接文件扫描功能时,可以将空格保留为格式字符串。部分修复是检查已成功扫描的值的数量。为了便于理解输出,我用格式字符串中的方括号括起注释材料。

将代码提取为类似于MCVE(Minimal, Complete, Verifiable Example)的内容并从标准输入而不是您打开的文件流中读取,您最终可以使用以下代码:

sscanf()

示例运行:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char line[4096];

    while (fgets(line, sizeof(line), stdin) != 0)
    {
        char temp[200];
        double value1 = 0.00, value2 = 0.00, value3 = 0.00;
        char format[] = "%lf %lf %lf %[^\n]s%*c";
        int nf = sscanf(line, format, &value1, &value2, &value3, temp);
        if (nf == 4)
            printf("Comment: %lf,%lf,%lf - [%s]\n", value1, value2, value3, temp);
        else if (nf == 3)
            printf("Plain:   %lf,%lf,%lf\n", value1, value2, value3);
        else
        {
            line[strcspn(line, "\n")] = '\0';
            printf("Invalid: [%s]\n", line);
        }
    }
    return 0;
}

有趣的是,从问题复制的数据在第二行末尾有一个空白。早期版本的代码在格式字符串中省略了扫描集之前的空格。第二行然后显示为注释行,而不是简单的行,并且所有注释都包括前导空格。注意:Comment: 1.000000,1.100000,1.400000 - [//this is first line ] Plain: 2.000000,2.100000,2.400000 Comment: 4.000000,4.100000,4.400000 - [//this is fourth line] Plain: 3.000000,3.100000,3.400000 Comment: 5.000000,5.100000,5.400000 - [//this is fifth line] 格式字符串难以掌握。