
头文件#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留位置放\0char 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); }输出methodGET, path/test, protoHTTP/1.1例2按分隔符截取%[^]常用技巧截取到换行符为止提取一行char line[] Host: 127.0.0.1\r\n; char host[128]; sscanf(line, Host: %[^\r], host); // %[^\r]一直读直到碰到 \r 就停止例3跳过不需要的字段%*schar str[] GET /index.html HTTP/1.1; char path[256]; // 跳过第一个字符串跳过第二个空格读取path sscanf(str, %*s %255s %*s, path); printf(%s, path); //输出 /index.html重点注意事项%s以空白作为分隔符不能用来读取带空格的字符串想读带空格的字符串要用%[^分隔符]必须校验返回值否则非法输入会让变量变成脏数据。你的原始代码没有判断返回值是严重bug。缓冲区溢出%s不加长度限制属于高危漏洞和gets()一样危险。参数传递接收字符串的变量直接传数组名地址不要加普通变量如int才需要。char s[16]; int a; sscanf(123 abc, %d %s, a, s);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。所以多个连续空白和单个空白效果完全一样%s会一次性把连续空白全部跳过不会把空白当成内容。不是“遇到一个空白就保存当前%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个%s指针从开头G开始前面没有空白直接读非空白字符G E T读到后面的空格停止读取把GET存入method追加\0字符串指针停在第一个空格位置。处理第2个%s%s规则先跳过所有连续空白这里3个空格全部跳过指针跳到/读取非空白字符直到下一组空格/index.html存入path追加\0指针停在后面空格。处理第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。✅ 正确理解每次执行%s第一步先吃掉所有连续空白第二步读取连续非空白字符直到再次碰到空白字符停止本次%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; }