繁体   English   中英

如何用sscanf解析输入行?

[英]How to parse an input line with sscanf?

我有一个输入.txt文件,看起来像这样:

Robert Hill 53000 5

Amanda Trapp 89000 3

Jonathan Nguyen 93000 3

Mary Lou Gilley 17000 1 // Note that came contains of 3 parts!

Warren Rexroad 72000 7

我需要阅读这些行并将其解析为三个不同的类别:名称(这是一个字符数组),Mileage(int)和years(int)。

 sscanf(line, "%[^] %d %d ", name, &mileage, &years);

这对我来说不是很好,有什么建议吗?

问题

当前传递给sscanf说明符的问题在于,它们既格式不正确,即使被修复,也无法满足您的要求。 如果您将[^ ]用作第一个转换说明符 ,则sscanf将尝试在插入空格之前读取尽可能多的字符。

如果我们假设一个名称不能包含数字,则指定[^0123456789]将读取正确的数据,但它还将在名称之后但在第一个里程输入之前包含尾随空格。 但是,可以通过将name的最后一个空格替换为空字节来轻松解决。

为了获得读入name的字符数,我们可以使用%n说明符来表示我们将sscanf存储读取到匹配参数中的字节数。 我们以后可以使用该值正确地“修剪”缓冲区。

我们还应该指定%[^0123456789]读取的字符的最大宽度,以免引起缓冲区溢出 ,这可以通过在%之后直接指定缓冲区的大小来实现。


样品实施

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

int
main (int argc, char *argv[])
{
  char const * line = "Mary Lou Gilley 17000 1";

  char     name[255];
  int mileage, years, name_length;

  sscanf(line, "%254[^0123456789]%n %d %d ", name, &name_length, &mileage, &years);

  name[name_length-1] = '\0';

  printf ("data: '%s', %d, %d", name, mileage, years);

  return 0;
}

data: 'Mary Lou Gilley', 17000, 1

如果您有一个可以找到第一个数字的位置的函数,如下所示:

// This function returns the position of the 
// space before the first digit (assuming that
// the names dont contain digits)...
char *digitPos(char *s){
    if isdigit(*(s+1)) return s;
    else return digitPos(s+1);
}

然后,您可以通过在正确的位置插入'\\0'分隔两个变量,如下所示:

pos  = digitPos(line); // This is a pointer to the space
*pos = '\0';
strcpy(name, line);
sscanf(pos + 1, "%d %d", &mileage, &years);

这可以帮助您入门。 它缺乏BLUEPIXY解决方案的智能,该解决方案对尾随空白的处理比我的要好(或者您可以自己砍掉它)。

dan@rachel ~ $ echogcc -o t t.c
dan@rachel ~ $ echo "Dan P F 3 21" | ./t
Name:    Dan P F ,
Mileage:         3,
Years:   21.

这是代码。

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

int main(){
        char *buf;
        int mileage, years;
        while(!feof(stdin) ){
                if( fscanf( stdin, "%m[^0-9] %d %d", &buf, &mileage, &years) == 3 ){
                        fprintf(stderr, "Name:\t %s,\nMileage:\t %d,\nYears:\t %d.\n", 
                                buf, mileage, years
                        );
                }
        }

}

您已经发现*scanf永远不要使用*scanf的三个原因之一:编写处理非平凡输入语法的格式规范几乎是不可能的,尤其是在您必须担心从格式错误的输入中恢复时。 但是,还有两个更重要的原因:

  • 许多输入规格,包括您%[...]建设,也同样高兴地溢出缓冲区声名狼藉gets
  • 数值溢出会引发未定义的行为-C库被许可因有人输入太多数字而崩溃

解析这样的行的正确方法是使用strcspn("0123456789", line)while (*p && !isdigit(*p)) p++;扫描第一个数字while (*p && !isdigit(*p)) p++; ,然后使用strtoul转换strtoul的数字。

int pos;
sscanf(line, "%*[^0-9]%n", &pos);
line[--pos]=';';
sscanf(line, "%[^;]; %d %d ", name, &mileage, &years);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM