如何用sscanf解析输入行?

时间:2014-03-24 01:10:53

标签: c arrays parsing input scanf

我有一个输入.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

我需要读取这些行并将它们解析为三个不同的类别:name(这是一个chars数组),mileage(int)和years(int)。

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

这对我来说效果不错,有什么建议吗?

5 个答案:

答案 0 :(得分:3)

问题

传递给sscanf的当前说明符的问题在于它是不正确的,即使修复它也不会做你想要的。如果您使用[^ ]作为第一个转化说明符,则sscanf会尝试在点击空格之前读取尽可能多的字符。

如果我们假设名称不能包含指定[^0123456789]的数字,则会读取正确的数据,但它也会包含名称后面的尾随空格,但是在第一个里程之前name中用空字节替换最后一个空格可以轻松解决这个问题。

要获取读入name的字符数,我们可以使用%n说明符来表示我们{d} 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

答案 1 :(得分: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);

答案 2 :(得分:0)

这可能会帮助您入门。它缺乏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
                        );
                }
        }

}

答案 3 :(得分:0)

您已经发现了*scanf永远不应该使用的三个原因之一:编写处理非平凡输入语法的格式规范几乎是不可能的,特别是如果您不得不担心从格式错误中恢复输入。但有两个更重要的原因:

  • 许多输入规范(包括您的%[...]构造)与臭名昭着的gets一样高兴地溢出缓冲区。
  • 数字溢出会引发未定义的行为 - C库被授权崩溃只是因为有人输入了太多数字。

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

答案 4 :(得分:-1)

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