IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C/C++常用字符串处理函数总结 及 手动替代实现 -> 正文阅读

[C++知识库]C/C++常用字符串处理函数总结 及 手动替代实现

本篇将介绍C/C++中非常常用的字符串处理函数,以及用C++手动替代实现函数功能相关代码(末尾)。

本篇中处理对象为字符数组。

以下是函数列表及简单文字介绍:(详细文字介绍以及C++替代实现将在下面给出)

常用字符串处理函数(索引)
strlen ( const char s[ ] )计算字符串长度
strcat ( char dst[ ] , const char src[ ] )拼接字符串
strncat (?char dst[ ] , const char src[ ] , const unsigned int length )拼接部分字符串
strcpy (?char dst[ ] , const char src[ ] )复制字符串
strncpy (?char dst[ ] , const char src[ ]?, const unsigned int length )复制部分字符串
strcmp (?const char s1[ ] ,?const char s2[ ] )比较字符串
strncmp (?const char s1[ ] ,?const char s2[ ] ,?const unsigned int length )比较字符串的部分内容

目录

strlen 长度计算函数

strcat 连接函数

strncat 部分连接函数

strcpy 复制函数

strncpy 部分复制函数

strcmp 比较函数

strncmp 部分比较函数

总结

C++代码手动实现

strlen

strcat

strncat

strcpy

strncpy

strcmp

strncmp


下面是每个函数使用方法的详细讲解:

strlen 长度计算函数

标准函数语句:strlen ( const char s[ ] )

输入存放字符串的字符数组,返回值为一个整型值,表示字符串的长度。

需要注意的是,求得的是第一个尾零 ' \0 ' 前的字符串长度,不包括尾零。strlen 这与 sizeof 不同,后者读的是所占字节数,尾零存放在数组中标志着字符串的结束,当然占有一定字节,所以 sizeof 是读取尾零的。可以用下面的程序加以验证:

const char str[]="I am from Tongji";
cout<<sizeof(str)<<endl; //输出17(16+1尾零)
cout<<strlen(str)<<endl; //输出16

当字符串中有多个尾零时,strlen 只读到第一个尾零,第一个尾零后面的所有部分 strlen 忽略不计。sizeof 因为一律读取尾零计算字节,所以不受限制。

const char str[]="Tongji\0Univer\0sity";
cout<<sizeof(str)<<endl; //输出19
cout<<strlen(str)<<endl; //输出6(读到最左侧尾零结束,不读入该尾零)

我们可以看到,在上面的数组中,有的可以直接看到有尾零字符 ' \0 ' 在其中,这就是显式尾零。无论是否有显式尾零,最后一定有一个隐式的尾零(看不见但的确存在,标志字符串的结束)。

strcat 连接函数

标准函数语句:strcat ( char dst[ ] , const char src[ ] )

首先这个函数和猫咪应该没啥关系(虽然有cat),不要乱想。函数输入两个参数,分别是:

  • 存放字符串 dst 的字符数组 dst
  • 存放字符串 src 的字符数组 src(只读!因为不需要进行写入操作)

它的功能是将只读字符串 src 连接到字符串 dst 的尾部。返回值是加长后的字符数组 dst。

需要注意的是,首先在连接时,src 的尾零也会依附在 src 一起被连接到 dst 上,形成新 dst 的结束标志;其次,字符数组dst一定要有足够长的空间,最小大小为 strlen(src)+strlen(dst初)+1。最后的加1,显而易见又是尾零;最后,如果你使用的编译器时 Visual Studio 2019/22,务必加上头文件:

#define _CRT_SECURE_NO_WARNINGS

当然如果你不加,编译器会报错(甚至不运行)。这时你也可以使用 strcat_s 函数,但是不建议,比如用洛谷的编译器就不能识别这个函数。关于strXXX_s 系列的函数,是在 vs 2005 以后引进的安全函数系列,有效防止数据溢出问题。

strcat 函数的具体使用,可以参考以下代码:

char dst[30]="Shanghai";
char src[]=" Yangpu";
/* 特别注意:dst数组大小不能不写(缺省),因为需要比缺省更大的空间 */
cout<<strcat(dst,src)<<'*'<<endl;

//输出为 Shanghai Yangpu*  (这里的*作用是检查是否末尾有赘余不可见部分被连接)

dst 数组大小千万不能使用缺省(默认),在上例中,如果缺省那大小就是9,但是后面还要连接src字符串,导致数组越界。当然运行时 vs 会弹窗报错,不予通过。

strncat 部分连接函数

标准函数语句:strncat (?char dst[ ] , const char src[ ] , const unsigned int length )

strncat 建立在 strcat 基础上,选取 src 的一部分连接到 dst 上去。至于选取的“量”有多少,取决于参数 length。同样这个函数返回值是增长后的数组 dst ,并且为了防止数组越界,要求 dst 长度不小于 strlen(dst初)+n+1。具体使用代码如下:

char dst1[100]="Siping ";
char dst2[100]="Siping ";
char src[]="Road 1239";
cout<<strncat(dst1,src,4)<<'*'<<endl;   //输出 Siping Road*
cout<<strncat(dst2,src,100)<<'*'<<endl; //输出 Siping Road 1239*

可以看到,如果选择的长度 length 大于了原 src 的长度,那么连接的就是整个 src 字符串(此时 strncat 等价于 strcat )。同样,dst 长度不能缺省,否则弹窗报错。

strcpy 复制函数

标准函数语句:strcpy (?char dst[ ] , const char src[ ] )

上一个函数讲的是将一个字符串连接到另一个字符串上,形成一个新的增长的字符串。但是如果想达到“覆盖”而非在尾部“连接”的效果,便引入了 strcpy 复制(覆盖)函数,可以将字符串 src 复制到 dst 中,并覆盖原来的 dst 字符串。

同时我们也要注意 dst 串的长度问题。当然这里要求就没有 strcat 函数那么高,只需要满足大于等于 strlen(src)+1 即可。头文件 _CRT_SECURE_NO_WARNINGS 也需要添加。使用参考代码如下:

char dst[]="hello world";
char src[]="goodbye";
strcpy(dst,src);
cout<<dst<<'*'<<endl; 
//输出为 goodbye*

这里可以看到,无论原来的 dst 有多长,无论 src 有多短,覆盖后输出只有了 src 的部分。这和广泛认知里的“覆盖”有所不同(普遍认为被覆盖物长的部分还会露出来)。其实这是因为在复制 src 时,同时复制了尾零。而输出时只要遇到第一个尾零就停止输出。所以后面的部分并非不存在,而是因为前有尾零限制输出。如果想验证,可以把 dst 内每一项强转成 int 型后输出:

char dst[]="student";
char src[]="hello";
strcpy(dst,src);

int i;
for(i=0;i<8;i++){
    cout<<int(dst[i])<<' ';
//输出 104 101 108 108 111 0 116 0

可以看到输出结果 104~111 对应的就是 hello 的 ASCII码,遇到第一个尾零(ASCII 0)后面不在输出但存在。

strncpy 部分复制函数

标准函数语句:strncpy (?char dst[ ] , const char src[ ]?, const unsigned int length )

原理和 strcpy相似,通过 length 控制复制的长度。同理如果 length 超过了 src 的长度,那就复制整个 src 即可。所以这里要求字符数组 dst 的长度不小于 min{ strlen(src) , n }+1。

char dst[]="hello world";
char src[]="goodbye";
strncpy(dst,src,3);
cout<<dst<<'*'<<endl; 
//输出为 goolo world*

这里可以清晰的看到 strncpy 的“覆盖”时普遍认知的覆盖。有了上一个函数的铺垫不难得出,这是因为 strncpy 不会复制尾零(复制不到末尾也没有尾零给他复制)。

这里我们还对 strncpy 函数做一个拓展。如果想从 src 串的某个特定位置(而不是开头)开始复制应该怎么做呢?我们不能想到可以用地址:

char dst[]="hello world";
char src[]="goodbye";
strncpy(dst,&src[2],3);
cout<<dst<<'*'<<endl; 
//输出为 odblo world*

上例中 “&” 为取地址符,这样使用就可以直接转到 src[2] 处开始复制操作。加之拓展,可以实现从一个字符串中任意位置取出任意片段。

strcmp 比较函数

标准函数语句:strcmp (?const char s1[ ] ,?const char s2[ ] )

这个函数通过两个字符串一位一位进行比较,输入参数为两个存放只读字符串的字符数组,返回值是一个整型值,这个整型值有三种数据范围情况:

=0:两串相等

>0:串1大于串2

<0:串1小于串2

使用样例代码如下:

char s1_1[]="abcd";
char s1_2[]="abca";

char s2_1[]="efg";
char s2_2[]="efgh";

char s3_1[]="tju";
char s3_2[]="tju";
char s3_3[]="tju\0sjtu";

cout<<strcmp(s1_1,s1_2)<<endl; //输出1
cout<<strcmp(s2_1,s2_2)<<endl; //输出-1
cout<<strcmp(s3_1,s3_2)<<endl; //输出0
cout<<strcmp(s3_1,s3_3)<<endl; //输出0

通过第三个和第四个 strcmp 可以看出,两个字符串“相等”的条件是:在第一个(显式/隐式)尾零前字符数量相等且每个字符对应一致。换言之,第一个尾零后面的strcmp是不会管的。

关于字符串比较,有一个常犯的错误,代码如下:

char s1="abcd";
char s2="abca";

int k=s1<s2;
cout<<k<<endl;  //输出1

这个程序编译没有任何问题,但是不能达到预期效果。这是因为这里比较的是两个字符数组的首地址,而不是字符数组本身。因此如果将s1和s2内容互换,结果也不变。

strncmp 部分比较函数

标准函数语句:strncmp (?const char s1[ ] ,?const char s2[ ] ,?const unsigned int length )

使用方法没有特别之处(参考 strncpy/strncat 等部分处理理解),返回值同strcmp处理,此处不展开细说。

总结

关于这些函数,我们可以发现一些有意思的相同点:

  1. 当字符串中有多个尾零,一般只处理到第一个尾零(strlen / strcat / strcpy / strcmp / ...)
  2. 部分处理函数中,n超过截取串的总串长,就取整串(strncat / strncpy / ...)
  3. 返回值:strlen / strcmp 系列为整型数;strcpy / strcat 系列为字符数组 (包括部分处理的衍生函数)

C++代码手动实现

接下来就是非常有趣的事情了,我们要用C++代码取手动实现这些复杂的函数:

strlen

int henry_strlen(const char *str)
{
    if (str != NULL) {
        const char* src = str;
        for (; *str != '\0'; str++)
            ;
        return str - src;
    }
    else
        return 0;
}

strcat

char *henry_strcat(char *s1, const char *s2)
{
    if (s1 != NULL && s2 != NULL) {
        int i = tj_strlen(s1);
        int j = tj_strlen(s2);  //被添加
        int cal;
        char* s_all = s1 + i;
        for (cal = 1; cal <= j+1; cal++, s2++, s_all++) {
            *s_all = *s2;
        }
    }
    return s1;
}

strncat

char *henry_strncat(char *s1, const char *s2, const int len)
{
    if (s1 != NULL && s2 != NULL) {
        int i = tj_strlen(s1);
        int j = tj_strlen(s2);
        int cal;
        char* s_all = s1 + i;
        if (len >= j) {
            for (cal = 1; cal <= j + 1; cal++, s2++, s_all++) {
                *s_all = *s2;
            }
        }
        else {
            for (cal = 1; cal <= len; cal++, s2++, s_all++) {
                *s_all = *s2;
            }
            *s_all = '\0';
        }
    }
    return s1;
}

strcpy

char *henry_strcpy(char *s1, const char *s2)
{
    if (s1 != NULL && s2 != NULL) {
        int j = tj_strlen(s2);
        int cal;
        char* s_all = s1; 
        for (cal = 1; cal <= j + 1; cal++, s_all++, s2++) {
            *s_all = *s2;
        }
    }
    else if (s1 != NULL && s2 == NULL) {
        int i = tj_strlen(s1);
        for (i; i >=1; i--, s1++)
            *s1 = '\0';
    }
    return s1;    
}

strncpy

char *henry_strncpy(char *s1, const char *s2, const int len)
{
    int j = tj_strlen(s2);
    int cal;
    if (s1 != NULL && s2 != NULL) {
        char* s_all = s1;
        if (len <= j) {
            for (cal = 1; cal <= len; cal++) {
                if (*s2 == '\0')
                    continue;
                else
                    *s_all++ = *s2++;
            }
        }
        else {
            for (cal = 1; cal <= j; cal++) {
                if (*s2 == '\0')
                    continue;
                else
                    *s_all++ = *s2++;
            }
        }
    }
    return s1;
}

strcmp

int henry_strcmp(const char *s1, const char *s2)
{
    if (s1 != NULL && s2 != NULL) {
        int a1, a2;
        int i = tj_strlen(s1);
        int j = tj_strlen(s2);
        int num = i > j ? i : j;
        int cal;
        for (cal = 1; cal <= num; cal++) {
            a1 = int(*s1++);
            a2 = int(*s2++);
            if (a1 != a2)
                return a1 - a2;
            else
                continue;
        }
        return 0;
    }
    else if (s1 == NULL && s2 != NULL)
        return -1;
    else if (s1 != NULL && s2 == NULL)
        return 1;
    else
        return 0;
}

strncmp

int henry_strncmp(const char *s1, const char *s2, const int len)
{
    if (s1 != NULL && s2 != NULL) {
        int a1, a2;
        int i = tj_strlen(s1);
        int j = tj_strlen(s2);
        int min = i < j ? i : j;
        int num = min < len ? min + 1 : len;
        int cal;
        for (cal = 1; cal <= num ; cal++) {
            a1 = int(*s1++);
            a2 = int(*s2++);
            if (a1 != a2)
                return a1 - a2;
            else
                continue;
        }
        return 0;
    }
    else if (s1 == NULL && s2 != NULL)
        return -1;
    else if (s1 != NULL && s2 == NULL)
        return 1;
    else
        return 0;
}

本篇文章的内容就到这里了,后续还会跟进更多C++有关内容。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-09 20:30:33  更:2022-02-09 20:30:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:14:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码