为什么scanf忽略了最后一个值?

时间:2016-02-29 17:55:00

标签: c string parsing csv scanf

我想从命令行读取一行CSV。用户以age, HH:MM:SS, number

的形式输入

我写了以下内容:

int age;
char timestamp[8];
int number;

scanf("%d,%8[^,],%d", &age, timestamp, &number;
printf("%d, %s, %d", age, timestamp, number);

当我输入23, abcd, 9时,它会打印23, abcd, 9
当我输入23, abcdefghijklmno, 9时,它会打印23, abcdefgh, 0

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:3)

scanf()和UB

中使用的格式较弱

%8[^,](阅读不是char的8 ',')将" HH:MM:S"读入太小@Weather Vane的空间。因此,如果将char和空字符存储到timestamp[8]中的未定义行为不会导致代码被删除,则尝试阅读第二个S并与','匹配将会停止扫描@Marc B。不检查返回值(一个很大的禁忌),进一步隐藏了number未写入的事实。

char timestamp[8];
//     12345678
// age, HH:MM:SS, number
scanf("%d,%8[^,],%d", &age, timestamp, &number;`

相反

char buf[100];
fgets(buf, sizeof buf, stdin);

// 1 bigger for the null character
char timestamp[8+1];

//                  v-- add space              check result --v
if (sscanf(buf, "%d, %8[^,],%d", &age, timestamp, &number) == 3) {
  printf("%d, %s, %d", age, timestamp, number);
} else {
  puts("Input data scan failed");
}

scanf()是邪恶的。节省时间,避免头痛。使用fgets()
此广告由“实践安全输入”委员会提供给您。

答案 1 :(得分:1)

检查出来。 宣称小尺寸.. timestamp[8]

而是像timestamp[16]这样的东西。在这种情况下,名称长度最多为16。

%8最多消耗8个字符。这是另一个原因。

答案 2 :(得分:1)

如果您不确定逗号之间的字符串长度,可以扫描整数和逗号。使用%n说明符捕获扫描的字符数。最多可扫描8个字符串。然后使用strpbrk查找下一个逗号并扫描该逗号和最后一个整数。

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


int main()
{
    char *comma = NULL;
    char csv1[] = "23, abcdefghijklmnop, 9";
    char timestamp[9]= "";
    int age = 0;
    int number = 0;
    int used = 0;

    if ( ( sscanf ( csv1, "%d ,%n %8[^,]", &age, &used, timestamp)) == 2) {
        if ( ( comma = strpbrk ( csv1 + used, ","))) {
            if ( ( sscanf ( comma, ",%d", &number)) == 1) {
                printf ( "%d, %s, %d\n", age, timestamp, number);
            }
        }
    }
    return 0;
}