说句实在话,字符串相关函数大概是所有编程语言里“看着最无害、用起来最坑人”的一类函数。你平时写代码几乎每天都在碰它们,截个串、取个长度、拼接一下,好像谁都会;可一旦遇到编码问题、边界下标、精度丢失,或者数据库里那个小数点前的0莫名其妙没了,你就知道什么叫“一个字符淹死一个项目”。这篇文章算是我这么多年在字符串函数上的一笔总账,围绕最常见的字符串操作、类型转换、分割排序,以及 SQL、Excel 这些办公场景里的函数用法,把高频用法和踩坑点一起列清楚。适合刚入门的开发新手,也适合想把字符串处理这块彻底盘清楚的老人。
1. 为什么字符串函数值得单独总结:几个绕不开的基础问题
1.1 字符串的存储与不可变性
字符串的基础问题看起来很简单,但很多新人的翻车都翻在这里。
先说存储。C 语言里根本没有原生的字符串类型,所谓字符串就是一段以 \0 结尾的字符数组。你去数它的长度,用的是 strlen,它从起始地址一直数到 \0 为止;如果你用 sizeof 去测,得到的是整个数组占的字节数,这两个东西混用的后果,轻则长度算错,重则越界读写。我自己就见过不少同学写出这种代码:
c复制char buf[] = "hello";
int len = sizeof(buf); // 结果是 6,不是 5,因为带了结尾的 '\0'
而在 Python、Java、JavaScript 这类语言里,字符串是不可变对象,也就是说你调 replace、substring 这些函数,并不会修改原字符串,而是生成一个新的字符串。这个机制带来的直接后果是:在最坏情况下大量拼接会反复创建新对象,性能肉眼可见地下降。Java 里用 String + String 在循环里拼接,远不如 StringBuilder 高效,就是这个原因。
C++ 的 std::string 则是另一种典型:它对用户暴露出来的行为像一个值类型,但内部管理着动态分配的内存。因此拷贝、赋值、传参时要注意到底是深拷贝还是移动语义,否则很容易发生悬垂指针或者凭空多出来的大内存开销。
1.2 字符串函数家族的通用分类
我习惯把字符串函数按功能分成几类,这样不管换什么语言,脑子里都有一张地图:
- 度量类:长度、字节数、字符数、容量
- 查询类:查找子串、判断前缀后缀、统计出现次数
- 截取类:从中间切一段、按位置切、按长度切
- 修改类:替换、删除、插入、翻转
- 转换类:大小写转换、字符串与数字互转、编码转换
- 分割组合类:按分隔符拆分成数组、把数组拼成字符串
- 比较类:相等判断、字典序比较、忽略大小写比较
- 格式化类:模板字符串、占位符、补零、千分位、日期格式化
这张图本身没有语言绑定。你在 JavaScript 里要把一整个家族对应到 String.prototype 上,在 Python 里对应到 str 方法上,在 C 里则对应到 <string.h> 和 <stdio.h> 的一组自由函数上。但底层要解决的业务问题,永远是那十来件事。
这里顺便说一句“函数声明”的坑。现代 JavaScript 里函数有函数声明、函数表达式、箭头函数三种常见写法,它们在 this 绑定、提升机制上差别很大。用在字符串处理场景里最典型的例子是排序比较器和 replace 的回调函数:
javascript复制// 箭头函数写法
const arr = ["banana", "Apple", "cherry"];
arr.sort((a, b) => a.localeCompare(b)); // 按字典序排序
// 回调函数在 replace 中的典型应用
"2024-01-15".replace(/(\d+)-(\d+)-(\d+)/, (match, y, m, d) => `${d}/${m}/${y}`);
// 结果 "15/01/2024"
回调函数本身不是字符串函数,但字符串函数经常用它来做复杂逻辑。你只要记得:回调写对了,很多字符串处理能从“写一长串 if-else”变成“一两行声明式代码”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高频字符串操作全对比:C、C++、JavaScript、Python、Java 一次讲透
2.1 截取子串:substr与substring的天壤之别
截取子串是字符串函数里最容易被命名搞混的一组。不同语言对“从哪里开始、到哪里结束”的定义完全不同,甚至同一门语言里还同时存在两套函数。
我直接给一张对照表:
| 语言 | 函数/写法 | 参数含义 | 边界行为 |
|---|---|---|---|
| C | strncpy(dst, src+n, len) |
起始位置、长度 | 需手动补 \0 |
| C++ | str.substr(pos, len) |
起始位置、长度 | pos 越界抛 out_of_range |
| JavaScript | str.substring(start, end) |
起始、结束(不含end) | 负数会被当成0处理 |
| JavaScript | str.substr(start, length) |
起始、长度 | 容易和 substring 搞混 |
| Python | s[start:end] |
起始、结束(不含end) | 负数表示从尾部倒数 |
| Java | str.substring(start, end) |
起始、结束(不含end) | 下标越界抛异常 |
最坑的是 JavaScript 同时有 substring 和 substr 两个函数,前者参数是两个位置,后者参数是“起始位置 + 长度”。写代码的时候一时手滑,substr(1, 4) 本来想取第1到第4之间的内容,结果取到的却是从第1位开始往后的4个字符。这种 bug 不容易一眼看出来,因为大多数短字符串截出来结果都是字符串,只是内容不对。
如果你是想从 C 字符串里取连续几个字母,比如从 "hello world" 里取 "world",C 语言没有专门的高层接口,通常这样写:
c复制#include <stdio.h>
#include <
