C语言字符串安全查找 :strchr_s、strrchr_s、strstr_s
在 C11 标准中,为了增强字符串操作的安全性,引入了一系列安全版本的字符串函数。下面详细解析 strchr_s
、strrchr_s
和 strstr_s
这三个字符串安全查找函数。
1. strchr_s - 安全字符查找
函数原型
errno_t strchr_s(const char *str,size_t strsz,int ch,char **result
);
功能说明
查找字符 ch
在字符串 str
中第一次出现的位置。
参数说明
str
:要搜索的字符串strsz
:字符串缓冲区大小ch
:要查找的字符result
:指向指针的指针,用于存储找到的位置
返回值
- 成功:返回 0
- 失败:返回非零错误码
示例代码
#include <stdio.h>
#include <string.h>int main() {const char *text = "Hello, World!";char *result = NULL;errno_t err;// 查找字符 'W'err = strchr_s(text, strlen(text) + 1, 'W', &result);if (err == 0 && result != NULL) {printf("找到字符 'W',位置:%ld\n", result - text);printf("剩余字符串:%s\n", result);} else {printf("未找到字符 'W'\n");}return 0;
}
2. strrchr_s - 安全反向字符查找
函数原型
errno_t strrchr_s(const char *str,size_t strsz,int ch,char **result
);
功能说明
查找字符 ch
在字符串 str
中最后一次出现的位置。
示例代码
#include <stdio.h>
#include <string.h>int main() {const char *filename = "document.txt.backup";char *result = NULL;errno_t err;// 查找最后一个点号err = strrchr_s(filename, strlen(filename) + 1, '.', &result);if (err == 0 && result != NULL) {printf("文件扩展名:%s\n", result + 1); // 跳过点号} else {printf("未找到文件扩展名\n");}return 0;
}
3. strstr_s - 安全子串查找
函数原型
errno_t strstr_s(const char *str,size_t strsz,const char *substr,char **result
);
功能说明
查找子串 substr
在字符串 str
中第一次出现的位置。
示例代码
#include <stdio.h>
#include <string.h>int main() {const char *text = "The quick brown fox jumps over the lazy dog";char *result = NULL;errno_t err;// 查找子串 "fox"err = strstr_s(text, strlen(text) + 1, "fox", &result);if (err == 0 && result != NULL) {printf("找到子串 'fox',位置:%ld\n", result - text);printf("从该位置开始的字符串:%s\n", result);} else {printf("未找到子串 'fox'\n");}return 0;
}
4. 完整示例程序
#include <stdio.h>
#include <string.h>
#include <errno.h>void demonstrate_strchr_s() {printf("=== strchr_s 示例 ===\n");const char *text = "Programming in C";char *result = NULL;errno_t err = strchr_s(text, strlen(text) + 1, 'g', &result);if (err == 0 && result != NULL) {printf("第一个 'g' 在位置:%ld\n", result - text);} else {printf("查找失败,错误码:%d\n", err);}
}void demonstrate_strrchr_s() {printf("\n=== strrchr_s 示例 ===\n");const char *path = "/home/user/documents/file.txt";char *result = NULL;errno_t err = strrchr_s(path, strlen(path) + 1, '/', &result);if (err == 0 && result != NULL) {printf("文件名:%s\n", result + 1);} else {printf("查找失败,错误码:%d\n", err);}
}void demonstrate_strstr_s() {printf("\n=== strstr_s 示例 ===\n");const char *sentence = "C programming is powerful and C is widely used";char *result = NULL;errno_t err = strstr_s(sentence, strlen(sentence) + 1, "C programming", &result);if (err == 0 && result != NULL) {printf("找到子串,位置:%ld\n", result - sentence);printf("内容:%s\n", result);} else {printf("未找到子串,错误码:%d\n", err);}
}int main() {demonstrate_strchr_s();demonstrate_strrchr_s();demonstrate_strstr_s();return 0;
}
5. 错误处理
这些函数在发生错误时会返回相应的错误码:
#include <stdio.h>
#include <string.h>void error_handling_example() {const char *text = "Hello";char *result = NULL;// 测试各种错误情况// 1. 空指针errno_t err = strchr_s(NULL, 10, 'H', &result);if (err != 0) {printf("错误:空指针,错误码:%d\n", err);}// 2. 字符串长度超过缓冲区大小err = strchr_s(text, 3, 'o', &result); // 实际字符串长度 > 3if (err != 0) {printf("错误:缓冲区大小无效,错误码:%d\n", err);}// 3. 正常情况err = strchr_s(text, strlen(text) + 1, 'l', &result);if (err == 0 && result != NULL) {printf("成功找到字符,位置:%ld\n", result - text);}
}
6. 与传统函数的对比
特性 | 传统函数 | 安全版本 |
---|---|---|
参数验证 | 无 | 有完整的参数检查 |
缓冲区溢出保护 | 无 | 通过 strsz 参数提供 |
返回值 | 直接返回指针 | 通过错误码和输出参数 |
空指针处理 | 未定义行为 | 明确返回错误码 |
字符串安全查找函数对比表
特性 | strchr_s | strrchr_s | strstr_s |
---|---|---|---|
函数原型 | errno_t strchr_s(const char *str, size_t strsz, int ch, char **result) | errno_t strrchr_s(const char *str, size_t strsz, int ch, char **result) | errno_t strstr_s(const char *str, size_t strsz, const char *substr, char **result) |
查找方向 | 正向查找(从左到右) | 反向查找(从右到左) | 正向查找(从左到右) |
查找目标 | 单个字符 | 单个字符 | 字符串(子串) |
查找内容 | 字符 ch | 字符 ch | 子串 substr |
返回位置 | 第一次出现的位置 | 最后一次出现的位置 | 第一次出现的位置 |
主要用途 | 查找字符首次出现 | 查找字符最后出现 | 查找子串首次出现 |
典型应用场景 | 解析字符串前缀、查找分隔符 | 文件扩展名提取、路径解析 | 关键词搜索、模式匹配 |
参数类型 | int ch (字符) | int ch (字符) | const char *substr (字符串) |
时间复杂度 | O(n) | O(n) | O(n×m) 最坏情况 |
返回值机制 | 通过 result 输出参数返回位置 | 通过 result 输出参数返回位置 | 通过 result 输出参数返回位置 |
错误返回值 | 非零错误码 | 非零错误码 | 非零错误码 |
详细功能对比
功能细节 | strchr_s | strrchr_s | strstr_s |
---|---|---|---|
查找范围 | 整个字符串 | 整个字符串 | 整个字符串 |
匹配方式 | 精确字符匹配 | 精确字符匹配 | 子串完全匹配 |
空字符处理 | 会找到字符串结束符 \0 | 会找到字符串结束符 \0 | 不会匹配空字符串 |
大小写敏感 | 是 | 是 | 是 |
内存安全 | 是(通过strsz参数) | 是(通过strsz参数) | 是(通过strsz参数) |
标准版本 | C11 | C11 | C11 |
使用示例对比
使用场景 | strchr_s | strrchr_s | strstr_s |
---|---|---|---|
示例输入 | "hello@example.com" | "/path/to/file.txt" | "C programming is fun" |
查找目标 | '@' | '/' | "programming" |
查找结果 | "@example.com" | "/file.txt" | "programming is fun" |
实际应用 | 邮箱域名提取 | 文件名提取 | 关键词定位 |
错误处理对比
错误情况 | strchr_s | strrchr_s | strstr_s |
---|---|---|---|
str为NULL | 返回错误 | 返回错误 | 返回错误 |
result为NULL | 返回错误 | 返回错误 | 返回错误 |
strsz为0 | 返回错误 | 返回错误 | 返回错误 |
strsz超过实际大小 | 可能返回错误 | 可能返回错误 | 可能返回错误 |
未找到目标 | result设为NULL,返回0 | result设为NULL,返回0 | result设为NULL,返回0 |
substr为NULL | 不适用 | 不适用 | 返回错误 |
性能特点对比
性能指标 | strchr_s | strrchr_s | strstr_s |
---|---|---|---|
最佳情况 | O(1) - 目标在开头 | O(n) - 必须扫描整个字符串 | O(n) - 目标在开头 |
最坏情况 | O(n) - 目标在末尾或不存在 | O(n) - 必须扫描整个字符串 | O(n×m) - 需要回溯匹配 |
平均情况 | O(n/2) | O(n) | O(n+m) |
内存访问 | 顺序访问 | 顺序访问(但需要记录最后位置) | 顺序访问,可能回溯 |
适用场景总结
场景类型 | 推荐函数 | 理由 |
---|---|---|
查找分隔符 | strchr_s / strrchr_s | 单字符查找效率高 |
提取文件扩展名 | strrchr_s | 需要找到最后一个点号 |
搜索关键词 | strstr_s | 需要匹配整个子串 |
解析路径 | strrchr_s | 提取文件名或最后一级目录 |
验证字符串格式 | strchr_s | 检查特定字符是否存在 |
文本内容搜索 | strstr_s | 在长文本中查找短语 |
这个对比表清晰地展示了三个安全字符串查找函数在功能、性能和适用场景上的差异,帮助开发者根据具体需求选择合适的函数。
总结
安全版本的字符串查找函数提供了以下优势:
- 参数验证:检查空指针和无效参数
- 边界检查:通过
strsz
参数防止缓冲区溢出 - 明确的错误处理:通过返回值提供详细的错误信息
- 一致性:统一的错误返回机制
在使用这些函数时,务必:
- 正确计算字符串缓冲区大小(通常为
strlen(str) + 1
) - 检查返回值以确保操作成功
- 正确处理错误情况
这些安全函数特别适用于对安全性要求较高的应用程序,如系统软件、网络服务等。