C语言--字符函数和字符串函数
C语言提供了多种字符串处理函数,主要包含在<ctype.h>和<string.h>头文件中。<string.h>包含字符串操作函数(如strlen、strcpy、strcat、strcmp等)及其安全版本(strncpy、strncat、strncmp)。文章详细介绍了这些函数的用法、注意事项,并提供了模拟实现代码,如自定义的my_strlen、my_strcp
字符分类函数
C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。 这些函数的使用都需要包含一个头文件是 <ctype.h>
下面是一些常见函数

int isalnum(int c):检查字符是否为数字或字母;(0~9,a~z,A~Z)
int isalpha(int c):检查字符是否为字母;(a~z, A~Z)
int iscntrl(int c):检查字符是否为控制字符;(八进制000~037以及177的字符)
int isdigit(int c):检查字符是否为十进制数字;(0~9)
int isgraph(int c):检查字符是否为图形表示,依赖于使用语言的环境;0~9,a~z,A~Z,以及标点符号)
int islower(int c):检查字符是否为小写的字母;(a~z)
int isprint(int c):检查字符是否为可打印的;(数字、字母、标点符号、空白字符)
int ispunct(int c):检查字符是否为标点符号;(! ” # $ % & ’ ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~等)
int isspace(int c):检查字符是否为空白字符;(TAB、换行、垂直TAB、换页、回车、空格)
int isupper(int c):检查字符是否为大写字母;(A~Z)
int isxdigit(int c):检查字符是否为十六进制数字;(0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f)
都是int类型的函数;如果满足返回非0值,不满足返回0;
来个练习
//写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变。
#include<ctype.h>
int main()
{
char arr[] = "Hello Worled !!!";
int i = 0;
int ret = 0;
//int leg = sizeof(arr)/sizeof(arr[0]);
while (arr[i] != '\0')//这里使用while是因为这是一个字符串,以'\0'结尾
{
ret = islower(arr[i]);
if (ret)
{
arr[i] -= 32;
}
i++;
}
//for (i = 0;i < leg;i++)
//{
// printf("%c", arr[i]);
//}
printf("%s\n", arr);//字符串数组用%s打印就行
return 0;
}
字符转换函数
C语言提供了2个字符转换函数:
函数原型:
int tolower ( int c ); //将参数传进去的大写字母转小写
int toupper ( int c ); //将参数传进去的小写字母转大写
头文件:<ctype.h>
strlen的使用与模拟实现
函数原型:
size_t strlen ( const char * str );
- 注意函数的返回值为 size_t,是无符号的,因为求的长度是 (>=0)的嘛。( 易错 ,如下面的例子)
- 在C语言中,当编译器遇到 strlen 函数调用时,如果没有包含 <string.h> 头文件,编译器并不知道 strlen 函数的具体声明和类型信息。在这种情况下,编译器会隐式声明 strlen 函数,按照C语言的隐式声明规则,会假设 strlen 返回 int 类型 。
例:
//1
#include<string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") < 0)//会打印 >
{
printf("<");
}
else
{
printf(">");
}
return 0;
}
//2
int main()
{
if (strlen("abc") - strlen("abcdef") < 0)//会打印 <
{
printf("<");
}
else
{
printf(">");
}
return 0;
}
模拟实现
#include<assert.h>
int my_strlen(char* p)
{
// 1.计数器法
assert(p != NULL);
int count = 0;
while (*p != '\0')
{
count++;
p++;
}
return count;
// 2.指针-指针法
assert(p != NULL);
int start = p;
while (*p != '\0')
{
p++;
}
return p - start;
// 3.递归法(没有创建临时变量^-^)
assert(p != NULL);
if (*p != '\0')
{
return 1 + my_strlen(p + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[] = "fsfusoiusw";
printf("%d\n", my_strlen(arr));
return 0;
}
strcpy的使用和模拟实现
函数原型
char* strcpy(char * destination, const char * source );
头文件:<string.h>
注:
- 源字符串必须以’\0’结束。
- 会将源字符串中的’\0’拷贝到目标空间。
- 仅用于拷贝字符串。
#include<string.h>
int main()
{
char arr[] = " {hello world }";
char* p = "xxxxxxxxxxxxxxxxxxx";//这样写 是常量字符串,不能被修改
strcpy(p, arr);
printf("%s\n", p);
return 0;
}
strcpy的模拟实现
#include<assert.h>
//strcpy函数的返回的是目标空间的起始地址(如果只是为了实现功能,也可以没有返回值)
char* my_strcpy(char* p2, const char* p1)//有返回值是为了实现链式访问,const
{
//assert(p1 != NULL && p2 != NULL);
assert(p1 && p2);//这种写法更加简洁
char* ret = p2;//目标空间的起始地址
while (*p2++ = *p1++)//判断真假时,类似c=a=b+2.注意是一个等于号
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
strcat的使用与模拟实现
函数原型
char* strcat(char* destination, const char* source);
头文件:<strinig.h>
- 目标字符串中 也 得有 \0 ,否则没办法知道追加从哪里开始。
- 目标空间必须可修改。
//strcat的使用与模拟实现
char* strcat(char* destination, const char* source);
#include<string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "free!";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
#include<assert.h>
char* my_stract(char* p1, char* p2)
{
assert(p1 && p2);
char* ret = p1;
while (*p1 != '\0')
{
p1++;
}
while (*p1++ = *p2++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello0 ";
char arr2[] = "free!";
printf("%s\n", my_stract(arr1,arr2));
return 0;
}
字符串自己给自己追加如何?不推荐!假如用以上我们自己模拟的strcat来追加,会修改自身,造成死循环。
对数组名的一些理解
先举个例子
int main()
{
char arr1[20] = "hello0 ";
char arr2[] = "free!";
arr1 = arr2;//注意,这种赋值是错误的
printf("%s", arr1);
return 0;
}
. 数组名的本质是“地址符号”而非变量
数组名不是一个可以存储数据的变量,而是一个编译期确定的地址符号:
-
变量的本质是“内存地址 + 类型 + 名字”,变量的值可以被修改(如 int a = 1; a = 2; );
-
数组名仅关联了数组的起始地址,没有对应的“存储单元”来存放新的地址,因此无法被赋值(如 arr = arr + 1; 是非法的)。
strcmp的使用与模拟实现
函数原型:
int strcmp(const char* str1, const char* str2);
头文件:<string.h>
注:
- 第一个字符串小于第二个字符串,则返回小于0的数字 。
- 第一个字符串等于第二个字符串,则返回0 。
- 第一个字符串大于第二个字符串,则返回大于0的数字 。
- 那么如何判断两个字符串? 比较两个字符串中对应位置上字符ASCII码值的大小。不是比字符串长度
在VS中为 返回 -1 0 1
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 == *str2)
{
if (*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else
{
return -1;
}
}
int main() {
char arr1[] = "abq";
char arr2[] = "abqdfg";
int ret = my_strcmp(arr1, arr2);
printf("%d\n", ret);//-1
return 0;
}
strncpy的使用
函数原型:
char * strncpy ( char * destination, const char * source, size_t num );
int main()
{
char arr1[20] = "abcdef";
char arr2[20] = "xxxxxxxxxxxxxx";
strncpy(arr2, arr1, 8);
printf("%s\n", arr2);//abcdef
return 0;
}
- 拷贝num个字符从源字符串到目标空间。
- 如果num大于源字符串的长度,则拷贝完源字符串之后,在目标的后边追加\0,直到num个。
- 如果num小于源字符串的长度,则只拷贝源字符串,不会自动在后面加\0
strncat的使用
函数原型:
char * strncat ( char * destination, const char * source, size_t num );
int main()
{
char arr1[20] = "abcdef\0yyyyyyy";
char arr2[20] = "xxxxxxxxxxxxxx";
char arr3[20] = "abcdefy";
strncat(arr1, arr2, 3);//abcdefxxx
strncat(arr3, arr3, 7);//abcdefyabcdefy
return 0;
}
- 将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个 \0 字符。
- 如果source 指向的字符串的长度小于num的时候,只会将字符串中到\0 的内容追加到destination指向的字符串末尾。
strncmp的使用
函数原型:
int strncmp ( const char * str1, const char * str2, size_t num );
int main()
{
char arr1[] = "abq";
char arr2[] = "abqdfg";
int ret = strncmp(arr1, arr2,5);//第5个不会比较,只到第四个
printf("%d\n", ret);//-1
return 0;
}
strncpy strncat strncmp函数 在VS中,VS会认为他们不安全,会报警告。
strtok的使用
函数原型:
char * strtok ( char * str, const char * sep);
- sep参数指向一个字符串,定义了用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以被strtok函数 切分 的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的**位置()**开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
int main()
{
char arr1[] = "wjiahao@yeah.net";
char buf[40] = { 0 };
strcpy(buf, arr1);
//char sep[] = "@.";
char* sep = "@.";
////模拟实现代码逻辑
//char* ret = strtok(buf, sep);//bu f只用传一次
//printf("%s\n", ret);
//ret = strtok(NULL, sep);
//printf("%s\n", ret);
//ret = strtok(NULL, sep);
//printf("%s\n", ret);
// ret = strtok(NULL, sep);//如果再继续打印时就会打印NULL
//printf("%s\n", ret);
//一种很巧妙的方法
for (char* ret = strtok(buf, sep);ret != NULL;ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
strstr的使用与模拟实现
函数原型:
char * strstr ( const char * str1, const char * str2);
功能:在str1中找str2这个字符串第一次出现的位置,
如果找到了,就返回这个第一次出现的起始地址,
如果找不到,就返回NULL。
//strstr函数的使用
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "aef";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf("不存在");
}
else
{
printf("%s\n", ret);
}
return 0;
}
//strstr函数的模拟实现
const char* my_strstr(const char* str1, const char* str2)
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* cur = str1;//作为返回地址
//特殊情况 - str2指向的是空字符串,直接返回str1;
if (*str2 == '\0')
{
return str1;
}
while (*cur)
{
s1 = cur;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)//三个判断条件
{
s1++;
s2++;
}
if (*s1 == '\0')
{
return NULL;
}
if (*s2 == '\0')
{
return cur;
}
cur++;
}
return cur;
}
int main()
{
char arr1[] = "abcdeeefabcdef";
char arr2[] = "ef";
const char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("不存在");
}
else
{
printf("%s\n", ret);
}
return 0;
}
当前我们写的strstr函数的实现不是最优的算法,去了解数据结构算法的一种KMP算法。
strerror与perror的使用
函数原型
char* strerror ( int errnum );
void peroor (const char* str);
strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。 在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在 errno.h 这个头文件中说明的,C语言程序启动的时候就会使用一个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
perror == printf + strerror
perror函数打印完参数指向的字符串后,再打印一个冒号和一个空格,再打印错误信息。
#include<errno.h>
int main()
{
//打开文件
FILE* pf = fopen("data.txt", "r");//"r" - 读,以读文件的形式打开文件,如果这个文件不存在,就打开失败返回NULL
if (pf == NULL)
{
printf("打开失败,原因是:%s\n", strerror(errno));//没有if的条件它也会输出错误
perror("打开失败,原因是");//不需要传参,默认打印error
//perror == printf + strerror
return 1;
}
else
{
printf("打开文件成功");
//....
fclose(pf);//关闭文件
pf = NULL;
}
return 0;
}
更多推荐



所有评论(0)