fscanf用于检索号码信息的模式

时间:2016-08-28 02:30:01

标签: c text-files

我正在尝试创建一个算法来读取具有这种形状的文件:

+6.590472E-01;+2.771043E+07;+
-5.003500E-02;-8.679890E-02;-

如您所见,它有三列。其中两个是数字,最后一个是信号。

我已将该行作为char [30]并且列以分号分隔。

现在,让我们假设数字为" + 6.590472E-01"。我需要将它分成四个信息:符号(+-),点前的数字(0到9,在这种情况下为6),点和指数之间的数字( 590472)最后是指数(-01)。

如何使用fscanf检索这些信息?我必须使用哪种模式?

2 个答案:

答案 0 :(得分:2)

假设声明如下:

 char s1[2], s2[2], s3[2];
 char int1[21], int2[21], frac1[21], frac2[21];
 char exp1[6], exp2[6];

并假设您使用fgets()getline()成为字符串变量string,然后您可以使用sscanf()一次性解析字符串,如下所示:

if (sscanf(string, "%1[-+]%20[0-9].%20[0-9]%*[eE]%5[-+0-9];%1[-+]%20[0-9].%20[0-9]%*[eE]%5[-+0-9];%1[-+]",
           s1, int1, frac1, exp1, s2, int2, frac2, exp2, s3) != 9)
    …something went wrong — at least we can analyze the string…
else
    …got the information…

请注意在格式字符串中使用20,但在变量声明中使用21;这个一个接一个是很久以前(大约1979年)在标准I / O库中做出的设计决定,远在标准之前。 %*[eE]允许eE作为指数标记,并禁止分配。请注意,指数项将允许E9-8+7作为指数,并且不会坚持使用符号;除非你分两部分收集指数,否则没有一个简单的方法。

您也不能简单地告诉扫描完成的位置。您可以在最后添加%n转换规范,并将&n作为额外参数传递(使用int n;作为变量定义)。 %n未计算在内,因此条件不变。然后,您可以检查buffer[n]以查看转换停止的位置 - 是换行符还是字符串结尾,还是虚假的东西?

请注意,因为格式字符串始终使用%[…]个扫描集,所以不会消耗任何空格 - 输入中的任何空格都会触发错误。

这需要对sscanf()的规范有相当全面的了解。在接下来的一个月左右你可能需要阅读它六次才能开始掌握它,然后在明年重新读它六次,之后你可能会得到它离开年度修订版 - 它是一个复杂的函数(scanf()函数族是标准C中最复杂的函数。)

测试代码

#include <stdio.h>

int main(void)
{
    char string[] = "+6.590472E-01;+2.771043E+07;+\n";
    char s1[2], s2[2], s3[2];
    char int1[21], int2[21], frac1[21], frac2[21], exp1[6], exp2[6];
    int n;
    int rc;

    if ((rc = sscanf(string, "%1[-+]%20[0-9].%20[0-9]%*[eE]%5[-+0-9];%1[-+]%20[0-9].%20[0-9]%*[eE]%5[-+0-9];%1[-+]%n",
                s1, int1, frac1, exp1, s2, int2, frac2, exp2, s3, &n)) == 9)
    {
        printf("[%s][%s].[%s]E[%s]\n", s1, int1, frac1, exp1);
        printf("[%s][%s].[%s]E[%s]\n", s2, int2, frac2, exp2);
        printf("[%s] %d (%d = '%c')\n", s3, n, string[n], string[n]);
    }
    else
        printf("Oops (rc = %d)!\n", rc);
    return 0;
}

输出:

[+][6].[590472]E[-01]
[+][2].[771043]E[+07]
[+] 29 (10 = '
')

正如chqrliecomment所述,编写if语句的更好方法可能更像:

    if ((rc = sscanf(string, "%1[-+]%20[0-9].%20[0-9]%*[eE]%5[-+0-9];"
                             "%1[-+]%20[0-9].%20[0-9]%*[eE]%5[-+0-9];" "%1[-+]%n",
                     s1, int1, frac1, exp1,
                     s2, int2, frac2, exp2, s3, &n)) == 9)

这使用相邻的字符串连接来强调格式字符串的前两个段是相同的,然后或多或少地分割变量以匹配。有许多类似的布局也可以使用。

答案 1 :(得分:0)

如果您确定他们有这种格式,您只需使用:

int z = sscanf(var,"%g;%g;%c,&float1,&float2,&char1);

其中char1是一个char,而float1float2float。 验证z == 3以确保您已成功填充三个变量。

编辑:我看到你想要分开的部分;然后你可以使用:

int z = sscanf(var,"%c%d.%[^E]E%3c;%c%d.%[^E]E%3c;%c",...)具有适当数量的变量。 %c将第一个符号读入char,%d整数,点被丢弃,%[^E]直到&#39; E&#39 ;然后是&#39; E&#39;被丢弃,%3c读取指数,依此类推。

这些格式也适用于fscanf - 无论如何fscanf内部映射到scanf。