您应该声明您的令牌缓冲区足够大。为了安全起见,最好使它们都与输入缓冲区本身一样大。看到这个这个线程如何防止scanf在C中导致缓冲区溢出?了解更多信息。
如果您使用的是 GNU 编译器,则可以使用可以代表您动态分配缓冲区的扩展。使用 scanf()查看动态分配
例子:
为扫描的令牌使用预定义的缓冲区:
请注意,所有标记的大小都与输入缓冲区相同:
/* sscanf-test.c */
#include <stdio.h>
int main(int argc, char** argv)
{
FILE *file = fopen("sample.txt", "r");
const int BufferSize=256;
char buffer[BufferSize];
char tokenOne[BufferSize];
char tokenTwo[BufferSize];
char tokenThree[BufferSize];
char tokenFour[BufferSize];
while (fgets(buffer, sizeof(buffer), file) != NULL)
{
tokenOne[0]='\0';
tokenTwo[0]='\0';
tokenThree[0]='\0';
tokenFour[0]='\0';
int tokenCount = sscanf(buffer, "%s %s %s %s", tokenOne, tokenTwo, tokenThree, tokenFour);
printf("scanned %d tokens 1:%s 2:%s 3:%s 4:%s\n", tokenCount, tokenOne, tokenTwo, tokenThree, tokenFour);
}
fclose(file);
return 0;
}
该程序产生以下输出(我稍微清理了格式以提高可读性):
gcc sscanf-test.c -o sscanf-test
./sscanf-测试
扫描 2 个令牌 1:READ 2:N 3:4:
扫描 3 个令牌 1:N: 2:DS 3:1 4:
扫描 3 个令牌 1:SUM: 2:DS 3:1 4:
扫描 4 个令牌 1:LOOP:2:MOVER 3:AREG 4:N
扫描 3 个令牌 1:ADD 2:AREG 3:N 4:
扫描 3 个令牌 1:COMP 2:AREG 3:='5' 4:
扫描 3 个令牌 1:BC 2:LE 3:LOOP 4:
扫描 3 个令牌 1:MOVEM 2:AREG 3:SUM 4:
扫描 2 个令牌 1:PRINT 2:SUM 3:4:
扫描 1 个令牌 1:STOP 2:3:4:
如果要存储扫描的令牌以供以后处理,则必须将它们复制到 while 循环中的其他位置。您可以使用该函数strlen
来获取令牌的大小(不包括尾随字符串终止符 '\0')。
为令牌使用动态内存分配:
就像我说的,你也可以让 scanf 为你动态分配缓冲区。scanf(3)手册页声明您可以使用 GNU 扩展 'a' 或 'm' 来做到这一点。具体来说,它说:
可选的“a”字符。这与字符串转换一起使用,并且使调用者无需分配相应的缓冲区来保存输入:相反,scanf() 分配了一个足够大小的缓冲区,并将该缓冲区的地址分配给相应的指针参数,这应该是一个指向 char * 变量的指针(这个变量在调用之前不需要初始化)。当不再需要此缓冲区时,调用者应随后释放(3)此缓冲区。这是一个 GNU 扩展;C99 使用 'a' 字符作为转换说明符(它也可以在 GNU 实现中使用)
我无法使用 'a' 修饰符让 scanf 工作。但是,还有 'm' 修饰符可以做同样的事情(甚至更多):
从 2.7 版开始,glibc 还提供了 m 修饰符,其目的与 a 修饰符相同。m 修饰符具有以下优点:
它也可以应用于 %c 转换说明符(例如 %3mc)。
它避免了关于 %a 浮点转换说明符的歧义(并且不受 gcc -std=c99 等的影响)
它在即将到来的 POSIX.1 标准修订版中指定。
/* sscanf-alloc.c */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE *file = fopen("sample.txt", "r");
const int BufferSize=64;
char buffer[BufferSize];
char *tokenOne = NULL;
char *tokenTwo = NULL;
char *tokenThree = NULL;
char *tokenFour = NULL;
while (fgets(buffer, sizeof(buffer), file) != NULL)
{
// note: the '&', scanf requires pointers to pointer to allocate the buffers.
int tokenCount = sscanf(buffer, "%ms %ms %ms %ms", &tokenOne, &tokenTwo, &tokenThree, &tokenFour);
printf("scanned %d tokens 1:%s 2:%s 3:%s 4:%s\n", tokenCount, tokenOne, tokenTwo, tokenThree, tokenFour);
// note: the memory has to be free'd to avoid leaks
free(tokenOne);
free(tokenTwo);
free(tokenThree);
free(tokenFour);
tokenOne = NULL;
tokenTwo = NULL;
tokenThree = NULL;
tokenFour = NULL;
}
fclose(file);
return 0;
}
gcc sscanf-alloc.c -o sscanf-alloc
./sscanf-分配
扫描 2 个令牌 1:READ 2:N 3:(null) 4:(null)
扫描 3 个令牌 1:N: 2:DS 3:1 4:(null)
扫描 3 个令牌 1:SUM: 2:DS 3:1 4:(null)
扫描 4 个令牌 1:LOOP:2:MOVER 3:AREG 4:N
扫描 3 个令牌 1:ADD 2:AREG 3:N 4:(null)
扫描了 3 个令牌 1:COMP 2:AREG 3:='5' 4:(null)
扫描 3 个令牌 1:BC 2:LE 3:LOOP 4:(null)
扫描 3 个令牌 1:MOVEM 2:AREG 3:SUM 4:(null)
扫描 2 个令牌 1:PRINT 2:SUM 3:(null) 4:(null)
扫描 1 个令牌 1:STOP 2:(null) 3:(null) 4:(null)