对sscanf的使用缺乏了解

时间:2016-04-26 11:24:05

标签: c parsing scanf

我想解析一个特定的行。所以,我编写了下面的代码来测试逻辑,但我可能错误地理解了一些:

typedef struct vers
{
   char tu8UVersion[5];
   char tu8UCommit[32];
}tst_prg_versions;

int main(int argc, char **argv)
{
    tst_prg_versions lstVer;
    char buf1[32];
    char buf2[32];

    char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a";
    sscanf(str, "BOARD-VERS-v%5s-git+%s", lstVer.tu8UVersion, lstVer.tu8UCommit);
    printf("vers='%s'\n", lstVer.tu8UVersion);
    printf("commit='%s'\n", lstVer.tu8UCommit);

    sscanf(str, "BOARD-VERS-v%5s-git+%s", buf1, buf2);
    printf("vers='%s'\n", buf1);
    printf("commit='%s'\n", buf2);
    return 0;
}

一旦执行,它将返回:

vers='1.0.09abc12345a'
commit='9abc12345a'
vers='1.0.0'
commit='9abc12345a

为什么第一个vers等于1.0.09abc12345a而不是1.0.0

3 个答案:

答案 0 :(得分:2)

第一个实际读取1.0.0!然而问题是,tu8UVersion不是以空值终止的,因此printf(不是sscanf)打印在字段上(这样做是未定义的行为,但正如sjsam所述) - 紧接着是tu8UCommit(不一定要因此,出于对齐的原因,它们之间仍然可能存在一些填充字节!)。

您需要最多打印5个字符(printf格式字符串中为%.5s)或者留下用于终止tu8UVersion为0的位置,如评论中所建议的那样。

您的缓冲区也可能发生类似情况。你很幸运,他们似乎已经初始化为0(可能是因为编译为调试版本),这不一定必须发生。所以运气不好,你可以打印掉剩余的buf1(已被留在垃圾箱里)甚至更远。

答案 1 :(得分:2)

  

为什么第一个vers等于1.0.09abc12345a而不是1.0.0?

请记住,你有

typedef struct vers
{
   char tu8UVersion[5];
   char tu8UCommit[32];
}tst_prg_versions;

我猜,tu8UVersiontu8UCommit的内存很可能是连续的。由于您执行以下操作时非空终止 tu8UVersion

printf("vers='%s'\n", lstVer.tu8UVersion);

继续打印tu8UCommit并停止,因为tu8UCommit为空终止。

虽然sscanf在这里似乎是最明智的解决方案,但你也可以引入一些格式:

char tu8UVersion[32];
   /*  version number can't get too big.
    *  So the first step is do allocated a
    *  reasonably - but not too - big size for it.
    *  So that you can be sure there are few empty bytes at the end.
    */

然后使用函数来清理字符串:

char* sanitized(char* ptr)
{
  if(ptr[strlen(ptr)]!='\0')  // include string.h for strlen
     ptr[strlen(ptr)]='\0';
  return ptr;
}

并打印出来:

 printf("vers='%s'\n", sanitized(lstVer.tu8UVersion));

答案 2 :(得分:1)

您的问题已在评论中确定:您没有为终止空字符留出空间,并且两个字符串一起运行。

如果您想要扫描预先不知道其大小的版本,可以将字符限制为使用%[.-9]扫描到十进制数字和点,或者除了带{{1}的连字符以外的所有内容}。 (%[^-]格式与%[...]类似,不同之处在于您必须在括号中提供有效字符列表。插入符号作为首字母表示该字符串由未列出的字符组成。换句话说,%s%s

的缩写

扫描字符串时,应测试%[^ \t\n]的返回值,以确保所有项目都已正确扫描并包含有效值。

这是一个扫描最多11个字母的版本号的变体:

sscanf