
你在做竞赛题、读传感器数据或者解析混合文本时,一定碰到过这种情况:拿到了一串像 "abc123def456" 这样的字符,真正有用的数字却夹在字母中间。C语言提取数字,最直白的做法是用 sscanf 按格式读,要一段段抠就用指针遍历,要处理负号小数就得自己写判断。
本文基于 C99 标准验证,覆盖 sscanf、指针遍历、strtol 三种做法,并附上横向对比和踩坑清单。看完你不仅能从混合串里把数字抠出来,还能理解它们各自的适用边界,下次解析文本可以自己选型。今天这篇文章,编程狮就把这块讲透。
一、先搞清楚要提取的是哪种数字
所谓 C语言提取数字,指的是从一个包含字母、符号的混合字符串里,把其中的数值部分识别并取出来。它可能是一段(如 "123"),也可能是多段(如 "123" 和 "456"),还可能带负号或小数点。
C 语言没有 Java、Python 那样现成的「提取所有数字」函数,所以思路分两类:一类是按已知格式用 sscanf 读,另一类是自己遍历字符逐个拼。这种 字符判断 是字符串处理的基本功。
⚠️ 注意:C 语言字符串以
\0结尾,手动遍历时一定要判断'\0'终止,否则会越界读到乱码甚至崩溃。
1.1 C 和 C++ 在这里怎么选
如果你的项目是 C++,可以直接用 std::string 配合更方便的函数;纯 C 环境则只能用字符数组和指针。C 语言教程 打好的基础,在 C++ 里同样用得上,两者字符串处理方式很接近,学透一个另一个很容易上手。
二、方法一:sscanf 按格式读取(最省事)
如果数字的位置和格式相对固定,比如 "id=123" 这种「前缀 + 数字」,用 sscanf 按格式符 %d 直接读最方便。它适合「格式已知、只取其中几段」的场景。
#include <stdio.h>
int main() {
char s[] = "id=123";
int n;
sscanf(s, "id=%d", &n); // n 得到 123
printf("提取到的数字是 %d\n", n);
return 0;
}
上面这段做的是:用 "id=%d" 这个格式串,跳过前面的 "id=",把后面的整数读进 n。前提是格式要对得上,否则读不到。如果字符串里数字前面还有别的内容,比如 "user:123" 想取 123,就得把 user: 也写进格式串跳过去,或者先用别的方式定位到数字起点再读。
💡 小提示:
sscanf的返回值等于成功读入的变量个数,读之前最好判断一下,避免读到的其实是垃圾值。
三、方法二:指针遍历逐个拼(灵活)
当数字散落在多处、格式不固定时,用指针从头扫到尾,遇到数字字符就拼进缓冲区,遇到非数字就当一个数字结束。这种写法完全自己掌控,能处理任意散落的数字段。这种纯 C 的写法在 C++ 里也完全成立,想系统学 C++ 可以看 C++ 教程。
#include <stdio.h>
#include <ctype.h>
int main() {
char s[] = "abc123def456";
int i = 0, num = 0, started = 0;
while (s[i] != '\0') {
if (isdigit((unsigned char)s[i])) {
num = num * 10 + (s[i] - '0');
started = 1;
} else if (started) {
printf("提取到 %d\n", num);
num = 0;
started = 0;
}
i++;
}
if (started) printf("提取到 %d\n", num);
return 0;
}
上面这段做的是:逐字符判断是不是数字,是就累加进 num,遇到字母就输出当前数字并清零。运行后会依次打印 123 和 456。
⚠️ 注意:
isdigit来自ctype.h,判断字符是不是 0-9。直接写s[i] &= '0' && s[i] <= '9'也行,效果一样,还省一次函数调用,但可读性稍差。
四、方法三:strtol 按位置转换(带符号)
strtol 能把字符串开头一段合法的数字(支持正负号、进制)转成 long,并返回「下一个没转换的字符位置」,非常适合一段段提取,而且天然支持负号。它比 atoi 强大得多:atoi 只能转十进制整数且出错时一声不吭,而 strtol 能明确告诉你转换停在了字符串的哪个位置,便于继续往下解析。
#include <stdio.h>
#include <stdlib.h>
int main() {
char s[] = "abc-123def456";
char *p = s;
long n;
while (*p) {
n = strtol(p, &p, 10);
if (*p == '\0') break;
printf("提取到 %ld\n", n);
p++; // 跳过非数字字符
}
return 0;
}
上面这段做的是:从 p 指向的位置尝试转换数字,strtol 自动跳过前导非数字、转换 -123,并把 p 移到数字之后。循环往复就能把所有数字段抠出来。和字符串处理相对,如果你要做的是把内容从磁盘读进来再解析,可以看这篇 C 语言怎么从文件里读取内容。
顺带说一下 strtol 的第三个参数 base:传 10 按十进制、16 可以解析十六进制(如 "0x1f"),传 0 则会自动识别 0x 前缀。解析进制混合的文本时,这个参数特别好用。
| 方法 | 适合场景 | 能否多段 | 支持符号 |
|---|---|---|---|
sscanf |
格式固定 | 有限 | 能 |
| 指针遍历 | 格式自由 | 能 | 需自写 |
strtol |
按位置转换 | 能 | 能 |
表:三种提取方法横向对比。格式固定用 sscanf,格式自由用指针遍历或 strtol,需要支持符号和进制用 strtol。

五、踩坑清单
把读者最容易卡住的三个点列出来:
- 越界遍历:手动遍历忘了判断
'\0',会一直读到字符串外面。 - 数字溢出:
num = num * 10 + ...累加大数可能超过int上限,超长数字用long long或字符串存。 - sscanf 格式对不上:格式串和实际字符串不一致时读到的会是垃圾值,务必检查返回值。
- strtol 忽略前导空白:
strtol会跳过开头的空格再转换,如果输入可能以空白开头,要先把首尾空白清理掉,否则容易误读。
总结
C语言提取数字,格式固定用 sscanf 最省事,格式自由用指针遍历或 strtol 更灵活。新手建议先把字符遍历的逻辑想清楚,再选工具。
要点带走:
- 遍历字符串一定要判断
'\0'终止; - 累加大数注意
int溢出,改long long; sscanf格式要对得上,否则读到的不可信。
把 C语言基础 打牢,尤其是 字符判断 这类基本功,你处理文本会更顺手。下一步建议把 C 语言字符串处理的基础打牢,再去了解 C++ 里更现代的 std::string 写法,处理文本会轻松很多。
一句话选型:格式已知、只要其中几段用 sscanf 最省事;数字散落、格式自由用指针遍历或 strtol 更灵活;需要支持负号、进制或一段段连续提取时,strtol 是首选。
延伸学习
想把 C 语言文本解析这块系统补齐,可以按这个顺序来:
常见问题
Q:sscanf 一次能提取多个数字吗?
A:能。格式串里写多个 %d 并传入多个地址即可,例如 sscanf(s, "%d,%d", &a, &b) 可同时读两个用逗号分隔的数字。
Q:怎么处理带小数点的数字?
A:sscanf 用 %f 读小数;指针遍历则需要自己判断 '.' 并分别拼整数部分和小数部分,逻辑更繁琐。
Q:strtol 和 atoi 有什么区别?
A:atoi 只能转十进制整数且出错时不报错;strtol 能指定进制、能检测转换到哪里、出错时有更明确的返回,安全性和可控性都更好。
Q:提取出的数字想存成字符串而不是整数怎么办?
A:用指针遍历时,把数字字符直接追加进一个字符数组(记得留 \0 结尾)即可;用 sprintf(buf, "%d", n) 也能把整数重新格式化进字符串。

免费 AI IDE



