☰
15. sscanf 函数详解
2026/10/8 6:54:51 网站建设 项目流程

头文件

#include <stdio.h>

函数原型

int sscanf(const char *str, const char *format, ...);

作用:从字符串str中,按照format格式提取数据,存入后续变量。
对比:scanf是从标准输入(键盘)读取;sscanf是从内存字符串读取。

返回值(非常关键,你代码里漏掉了判断)

返回成功匹配并赋值的变量个数。

  • 成功匹配几项,就返回几;
  • 如果一开始就匹配失败,返回0;
  • 如果读到字符串结束(EOF),返回EOF(一般是-1)。

示例:

char buf[] = "GET /index.html HTTP/1.1"; char method[16], path[256], proto[16]; int ret = sscanf(buf, "%s%s%s", method, path, proto); // 匹配成功3个,ret = 3

常用格式符

格式说明
%s读取字符串,遇到空白字符(空格、换行、tab)自动停止,不会读取空白;自动追加\0
%d读取整数
%f读取浮点数
%[abc]读取字符,只接受括号内的字符,遇到不在集合里的字符停止
%[^abc]读取字符,直到遇到括号里任意字符停止,非常适合按分隔符截取字符串
%*s/%*d*表示:匹配但是不保存结果,跳过这部分数据

⚠️%s巨大坑点:不限制长度,极易缓冲区溢出!
不安全写法:sscanf(str, "%s", buf);
安全写法:限定最大读取长度(数组大小-1,留位置放\0)

char buf[16]; sscanf(str, "%15s", buf); //最多读15个字符,末尾自动补'\0'

几个典型例子

例1:分割HTTP请求行(就是你代码场景)

char req[] = "GET /test HTTP/1.1"; char method[16], path[256], proto[16]; int ret = sscanf(req, "%15s %255s %15s", method, path, proto); if(ret == 3) { printf("method=%s, path=%s, proto=%s\n", method, path, proto); }

输出:method=GET, path=/test, proto=HTTP/1.1

例2:按分隔符截取,%[^]常用技巧

截取到换行符为止(提取一行)

char line[] = "Host: 127.0.0.1\r\n"; char host[128]; sscanf(line, "Host: %[^\r]", host); // %[^\r]:一直读,直到碰到 \r 就停止

例3:跳过不需要的字段%*s

char str[] = "GET /index.html HTTP/1.1"; char path[256]; // 跳过第一个字符串,跳过第二个空格,读取path sscanf(str, "%*s %255s %*s", path); printf("%s", path); //输出 /index.html

重点注意事项

  1. %s以空白作为分隔符,不能用来读取带空格的字符串;想读带空格的字符串要用%[^分隔符]
  2. 必须校验返回值,否则非法输入会让变量变成脏数据。你的原始代码没有判断返回值,是严重bug。
  3. 缓冲区溢出:%s不加长度限制属于高危漏洞,和gets()一样危险。
  4. 参数传递:接收字符串的变量直接传数组名(地址),不要加&;普通变量如int才需要&。
char s[16]; int a; sscanf("123 abc", "%d %s", &a, s);
  1. sscanf只做简单正则风格匹配,能力弱,复杂文本解析不要依赖它。HTTP完整解析不能只靠sscanf。

和你这段HTTP解析代码关联总结

你的代码:

sscanf(req, "%s%s%s", hreq->method, hreq->path, hreq->protocol);

优点:一行代码拆分请求行3个字段。
缺点:

  • 没有长度限制,超长输入会内存越界;
  • 没有判断返回值,无法识别非法请求;
  • 只能分割空格分隔的3段,不能解析请求头多行数据。

2. sscanf (req, “% s% s% s”, hreq->method, hreq->path, hreq->protocol); 没明白这是怎么解析字符串的,这种只能解析 字符串之间只有一个空白字符(空格、换行、tab)的是吗,遇到一个,解析出来,保存到第一个 % s 里是吗,如果是连续多个空白字符(空格、换行、tab)呢

核心结论先说

%s的规则:

自动跳过开头所有连续空白(空格 、\t、\n、\r),然后持续读取非空白字符,直到碰到下一个空白就停止,并且自动在末尾加\0。

所以:

  1. 多个连续空白,和单个空白效果完全一样,%s会一次性把连续空白全部跳过,不会把空白当成内容。
  2. 不是“遇到一个空白就保存当前%s”,逻辑顺序是反过来的。

拆解sscanf(req, "%s%s%s", m, p, proto)的执行流程

以例子:

char req[] = "GET /index.html HTTP/1.1"; // 中间是3个空格,连续空白 char method[16], path[256], proto[16]; sscanf(req, "%s%s%s", method, path, proto);

执行步骤:

  1. 处理第1个%s:
    • 指针从开头G开始,前面没有空白,直接读非空白字符:G E T;
    • 读到后面的空格,停止读取;
    • 把GET存入method,追加\0;
    • 字符串指针停在第一个空格位置。
  2. 处理第2个%s:
    • %s规则:先跳过所有连续空白(这里3个空格全部跳过);
    • 指针跳到/,读取非空白字符直到下一组空格:/index.html;
    • 存入path,追加\0;指针停在后面空格。
  3. 处理第3个%s:
    • 再次跳过全部连续空白;读取HTTP/1.1存入protocol。

✅ 最终结果和只有单个空格:GET /index.html HTTP/1.1完全相同。

空白字符集合:' '(空格)、\t制表符、\n换行、\r回车。%s全部视为分隔符,多个连续空白等价于一个分隔符。

举个极端例子(混合换行+空格)

char req[] = "GET\n\r\t /demo HTTP/1.1";

\n\r\t加空格一堆连续空白,sscanf("%s%s%s")照样正常拆出三段。

重点误区澄清

❌ 错误理解:遇到一个空白字符,就结束当前%s。
✅ 正确理解:

  1. 每次执行%s:第一步先吃掉所有连续空白;
  2. 第二步读取连续非空白字符;
  3. 直到再次碰到空白字符,停止本次%s读取。空白本身不会存入字符串。

空白字符永远不会被%s捕获到结果里。

那什么情况下会解析失败?

只有非空白字符不够3段的时候,返回值小于3:
例:GET /index只有两段,sscanf返回2,第三个变量protocol不会被写入。

和HTTP场景关联

HTTP标准请求行:METHOD PATH HTTP/VERSION,规定用空格分隔,允许一个或多个空格,所以用%s%s%s刚好可以兼容标准写法。
但是!path里面不能带空格(HTTP路径本身不允许空格,空格要编码成%20),所以这个写法在解析请求行是可行的Demo方案。

对比:%[^\n]和%s的区别(容易搞混)

  • %s:自动跳过前置空白,读到空白停止,不保留空白
  • %[^\n]:不会跳过前置空白,读到\n停止,可以读取包含空格的字符串。

小测试代码,你可以直接复制运行验证

#include <stdio.h> int main() { char req[] = "GET /index.html HTTP/1.1"; char method[16], path[256], proto[16]; int ret = sscanf(req, "%s%s%s", method, path, proto); printf("ret=%d\n", ret); printf("method=[%s]\n", method); printf("path=[%s]\n", path); printf("proto=[%s]\n", proto); return 0; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询