你几乎是对的。假设引号是强制性的,您只需将扫描集转换说明符包含在引号中:
if (sscanf(s, "%*s \"%29[^\"]\" %d", name, &date) != 2)
...oops...
注意%29[^0-9]
; 这将字符串限制为 29 个字节加上'\0'
适合您的 30 字节数组的终端 null。你可能也应该对你的外衣进行类似的检查scanf()
,或者只是使用fgets()
而不是scanf()
那里。
如果引号是可选的,您必须更加努力,将名称扫描为非数字并在扫描后删除引号:
if (sscanf(s, "%*s %29[^0-9] %d", name, &date) != 2)
...oops...
下面是一些测试代码:
#include <stdio.h>
static void scanner(const char *fmt, const char *s)
{
char name[30];
int date;
if (sscanf(s, fmt, name, &date) != 2)
printf("Scan failed {%s} and {%s}\n", fmt, s);
else
printf("{%s} and {%s} => {%s} %d\n", fmt, s, name, date);
}
int main(void)
{
char source[][30] =
{
"ins \"name in quotes\" 12345",
"ins name without quotes 12345",
};
enum { NUM_SOURCE = sizeof(source) / sizeof(source[0]) };
char format[][20] =
{
"%*s \"%29[^\"]\" %d",
"%*s %29[^0-9] %d",
};
enum { NUM_FORMAT = sizeof(format) / sizeof(format[0]) };
for (int i = 0; i < NUM_FORMAT; i++)
{
for (int j = 0; j < NUM_SOURCE; j++)
scanner(format[i], source[j]);
}
return 0;
}
样本输出:
{%*s "%29[^"]" %d} and {ins "name in quotes" 12345} => {name in quotes} 12345
Scan failed {%*s "%29[^"]" %d} and {ins name without quotes 12345}
{%*s %29[^0-9] %d} and {ins "name in quotes" 12345} => {"name in quotes" } 12345
{%*s %29[^0-9] %d} and {ins name without quotes 12345} => {name without quotes } 12345
失败的转换是意料之中的;该格式查找引号,但没有任何引号。
我还使用了稍微不同的线束和格式字符串:
"%*s %1[\"]%29[^\"]%1[\"] %d"
(传递两个 2 字符的字符串q1
和q2
, 来保存引号),但是当引号丢失时,扫描失败;引号不是可选的。
请注意,名称有尾随空格和第二种格式的引号;这些将不得不单独删除。