我应该分解长扫描以使其可维护吗?

时间:2018-04-12 19:26:00

标签: c scanf

我有一个固定宽度列的文件,它是2到4位整数,一些浮点数和字符串的混合。全部混合在一起。结果是一个在页面上运行的scanf格式......

fscanf(file, "%2d%2d%2d%2d%4d%4d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s%1d%2d%1s%1d%2d%1s%1d%4d%1s%1d%4d%1s%1d%3d%1s%1d%4d%1s%1d%3d%1s%1d%3d%1s%1d%4d%1s%1d%5ld%1s%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%3d%1s%1d%3d%1s%1d%3d%1s%1d%2d%1s%1d",

这甚至不包括变量列表!

有关如何使其可维护的建议?我应该连续几次scanf吗?或者在这种情况下人们使用的是规范解决方案吗?

更新:我在第一篇文章中包含了原始数据,但在编辑过程中显然已将其删除。有问题的数据称为TMY2,您可以找到定义和示例格式字符串here。还有一种新的格式TMY3,它使用CVS,所以我有很多很好的例子来解析它。

3 个答案:

答案 0 :(得分:3)

  

具有固定宽度列的混合文件....   如何使这个可维护?

步骤1,将读入fgets()的宽大缓冲区。请勿使用fscanf() @Andrew Henle

#define MY_BUF_N 400
char buf[MY_BUF_N * 2];  // make buffer 2x aticipated max size
if (fgets(buf, sizeof buf, file) == NULL) {
   Handle_EOF_or_Error();
}

根据重复模式识别群组:

#define FMT_PRE "%2d%2d%2d%2d%4d%4d"
#define FMT_5   "%4ld%1s%1d"
#define FMT_4   "%4d%1s%1d"
#define FMT_3   "%4d%1s%1d"
#define FMT_2   "%4d%1s%1d"
#define FMT_MID "%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d"

使用字符串文字连接。 @jxh

#define FMT_ALL FMT_PRE \
        FMT_4 FMT_4 FMT_4 FMT_4 FMT_4 FMT_4 \
        FMT_2 FMT_2 FMT_4 FMT_4 FMT_3 FMT_4 \
        FMT_4 FMT_4 FMT_3 FMT_3 FMT_4 FMT_5 \
        FMT_MID \
        FMT_3 FMT_3 FMT_3 FMT_2 

使用" %n"测试整个扫描是否成功

int n = 0;
sscanf(FMT_ALL " %n", ...
  ...
  ...  /*  variable list laid out like the format statements. */
  ...
  &n);

测试n

// Did scan reach the end?
if (n == 0) {
  Handle_incomplete_scan();
}
// Was there any remaining junk? 
if (buf[n] != '\0') {
  Handle_junk_at_the_end();
}

添加其他测试以验证对象是否在范围内。

一般来说,我会重写。 @PeterJ_01在使用fgets()的第1步之后,使用strtol()等的帮助函数按顺序解析数据。

size_t i = 0;  // Index in buffer
if (parse_int(&object1, buf, &i, min_value, max_value)) error();
if (parse_int(&object1, buf, &i, min_value, max_value)) error();
...
if (parse_string(object7, sizeof object7, buf, &i)) error();
....
if (parse_long(&object12, buf, &i, min_value, max_value)) error();
...

答案 1 :(得分:1)

最好的选择是:

  1. 对每个变量使用多个scanfs,这样就可以更容易地读取代码,并且您可以确定它不会崩溃(问题:很多行只适用于scanfs,不是很优雅);
  2. 将所有这些东西作为main的参数传递,然后你只需要使用argv,更容易使用,更优雅(问题:用户需要事先知道程序需要的一切,不是非常用户友好);
  3. 希望有所帮助:)

答案 2 :(得分:0)

鉴于您正在http://rredc.nrel.gov/solar/pubs/tmy2/tab3-2.html处理文档中的指定数据,您根本无法安全使用RR.test();

因为"规范"被打破。厉害。它没有规定如何在每个字段中放置数据。数据可以用尾随空格左对齐,用前导空格右对齐,用前导零右对齐。

所有这些都适合"规范"。并且没有办法编写一个可以处理所有这些可能性的scanf()格式字符串。

直言不讳,无论是谁写了#34;规范"是无能的。

如果使用类似scanf()和C格式字符串的方式编写数据:

sprintf()

无法使用(%2d%2d%2d%2d%4d%4d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s%1d%4d%1s %1d%4d%1s%1d%2d%1s%1d%2d%1s%1d%4d%1s%1d%4d%1s%1d%3d%1s%1d%4d%1s%1d%3d %1s%1d%3d%1s%1d%4d%1s%1d%5ld%1s%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%3d%1s %1d%3d%1s%1d%3d%1s%1d%2d%1s%1d) 安全地阅读。例如,此代码:

scanf()

生成此输出:

#include <stdio.h>

int main( int argc, char **argv )
{
    int a = 1;
    int b = 99;
    int c = 34;

    char buffer[ 128 ];

    sprintf( buffer, "%2d%2d%2d", a, b, c );

    printf( "a: %d\nb: %d\nc: %d\n", a, b, c );

    printf( "String: '%s'\n", buffer );

    sscanf( buffer, "%2d%2d%2d", &a, &b, &c );

    printf( "a: %d\nb: %d\nc: %d\n", a, b, c );

    return( 0 );
}

请注意,这些值不匹配。

再次说明:您无法对该数据使用a: 1 b: 99 c: 34 String: ' 19934' a: 19 b: 93 c: 4 。完全没有。