- List item
前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中
字符串常量适用于那些对它不做修改的字符串函数
1.求字符串长度
1.1strlen
其返回值是一个size_t的无符号给整型
字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )
参数指向的字符串必须要以 ‘\0’ 结束
注意函数的返回值为size_t,是无符号的( 易错 )
学会strlen函数的模拟实现
strlen函数介绍
strlen的基本形式
size_t strlen ( const char * str );
这里有一行代码
#include<stdio.h>
#include <string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("大于\n");
}
else
{
printf("小于等于\n");
}
return 0;
}
相信很多人都认为这行代码输出的结果肯定为大于等于
起始输出的恰好相反是大于
那这是为什么呢?
第一个strlen的值为3,第二个strlen的值为6,看似3-6的值为-3,但是strlen的返回值是无符号
两个无符号数相减返回的结果也会被当作无符号数来处理
-3被当作无符号数是多少呢?
-3的原码为10000000000000000000000000000011
-3的反码为111111111111111111111111111111111100
-3的补码为111111111111111111111111111111111101
无符号数的符号位不管是0还是1都表示整数
所以-3的补码表示的数字是非常大的
所以我们这样写代码并不能达到我们的目的
所以我们联想一下将size_t无符号数强制类型转化为int整型就能达到我们想要的结果
现在我们来模拟实现一下strlen
#include<stdio.h>
#include <string.h>
size_t my_strlen(const char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
size_t sz = my_strlen("abc");
printf("%u\n", sz);
return 0;
}
my_strlen求一个长度“abc”,我拿一个sz来接收,我们可以模拟库函数strlen的返回类型来设计sz的返回类型为size_t,这时我们函数调用,my_strlen的函数参数也要设计成和库函数strlen一样的方便我们理解,当我们遇到\0的时候,我们就停下来,返回count的值,那么为什么设计库函数strlen的人会将strlen的返回类型设计成无符号数size_t呢?
为了解决这个疑惑,我们先来想一想字符串有没有可能返回一个负数?显然不可能,所以我如果将库函数strlen的返回类型设计称为int,int既能表示整数也能表示负数,感觉上并不合理,所以我们直接写成size_t,无符号数表示整数
当然了size_t也可以写成unsigned int:无符号整型
2 长度不受限制的字符串函数
2.1strcpy
Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point)
源字符串必须以 ‘\0’ 结束
会将源字符串中的 ‘\0’ 拷贝到目标空间
目标空间必须足够大,以确保能存放源字符串
目标空间必须可变
学会模拟实现
#include<stdio.h>
#include <string.h>
int main()
{
//char arr2[] = "hello bit";
char* arr1 = "xxxxxxxxxx";
char arr2[6] = { 'a', 'b', 'c', 'd', 'e' , '\0'};
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
这里的\0是正确的代码,如果没有\0程序会报错,不知道什么时候停下来
所以没有\0是绝对不行的
#include<stdio.h>
#include <string.h>
int main()
{
//char arr1[3] = "";
//char arr2[] = "hello bit";
char* arr1 = "xxxxxxxxxx";
char arr2[6] = { 'a', 'b', 'c', 'd', 'e' , '\0'};
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
对于arr1所开辟的空间只有三个字节,arr2里面的字符串并不能完全放得下
所以目标空间应该足够大
字符指针char*arr1是一个常量字符串,不能被修改,正确的写法是在char星前面加上const
那么我们仿照库函数strcpy来模拟实现一下
strcpy函数
strcpy的代码形式
char* strcpy(char * destination, const char * source );
先直接来看代码
#include<stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
return ret;
}
int main()
{
char arr1[20] = "hello world";
char arr2[] = "xxxxx";
my_strcpy(arr1 + 6, arr2);
printf("%s\n", arr1);
return 0;
}
自定义函数my_strcpy的返回类型参照库函数strcpy的返回类型char*//
最后的src中的\0也会赋给星dest(destnation)
自定义函数my_strcpy的目标函数dest的返回类型返回的是char星首元素的地址
这里最好的写法是在char* src前面+上一个const,因为它是一个常量字符串
我们前面学习过assert函数—断言
这里我们给指针变量进行解引用操作,我们必须保证指针的有效性,指针万一有问题怎么办呢?为NULL—空指针怎么办呢?
所以我们这里进行断言判断指针不为NULL
提醒一下使用assert断言必须包含头文件#include<assert.h>
这个代码有一点冗余
我们尝试着将while循环稍作修改
#include<stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello world";
char arr2[] = "xxxxx";
my_strcpy(arr1 + 6, arr2);
printf("%s\n", arr1);
return 0;
}
while语句中的分号不执行任何语句,是不是看起来很舒服
这行代码照样能达到我们的预期效果
这里补充一个例子
看代码
#include<stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello world";
char arr2[] = "xxxxx";
my_strcpy(arr1 + 6, arr2);
printf("%s\n", arr1);
return 0;
}
如果我只想改变world这段字符串,只有arr数组名首元素的地址加6然后再调用函数依次进行遍历就可以了
2.2strcat
Appends a copy of the source string to the destination string. The terminating null character
in destination is overwritten by the first character of source, and a null-character is included
at the end of the new string formed by the concatenation of both in destination.
源字符串必须以 ‘\0’ 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
字符串自己给自己追加,如何?
strcat函数
库函数strcat代码形式
char * strcat ( char * destination, const char * source );
先直接看代码
#include<stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
这个函数需要注意几点
1:目标空间字符串必须有\0以便我能够找到在何处进行追加
2:源头字符串也需要有\0以便追加完后停止
3:目标空间要足够大能够放下追加完后的字符串
我们来模拟实现以下库函数strcat
先直接看代码
#include<stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//1. 找目标空间中的\0
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
星号dest遇到\0就停下来,结束第一个while循环
第二个while循环就是将源头字符串追加到了目标字符串之中
这里还是需要断言,这里就不做过多解释了
返回值还是返回目标字符串首元素的地址,通过首元素的地址按顺序打印字符串
2.3strcmp
This function starts comparing the first character of each string. If they are equal to each
other, it continues with the following pairs until the characters differ or until a terminating
null-character is reached.
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串?
strcmp函数介绍
库函数strcmp代码形式
int strcmp ( const char * str1, const char * str2 );
我们直接来模拟实现库函数strcmp
#include<stdio.h>
#include <string.h>
#include <assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return (*str1 - *str2);
}
//在VS
//> 1
//= 0
//< -1
//
int main()
{
int ret = my_strcmp("bbq", "bcq");
if (ret>0)
printf(">\n");
printf("%d\n", ret);
return 0;
}
在VS编译器环境下,大于0就返回1
小于0就返回-1
等于0就返回0
我可以直接写成
return (*str1 - *str2);
C语言规定返回一个正负数即可,不一定要返回1,-1,0
但是在VS环境下则返回-1,0,1
库函数strcmp的返回类型为int,所以我们定义一个整型ret
调用函数返回类型为int
进入while循环,如果我们的星号str1为\0则表示我们的两个字符串相等则返回\0
第二个if是判断大小号的,理解也很容易,这里不过多解释
3 长度受限制的字符串函数介绍
3.1strncpy
库函数strncpy介绍
Copies the first num characters of source to destination. If the end of the source C string
(which is signaled by a null-character) is found before num characters have been copied,
destination is padded with zeros until a total of num characters have been written to it.
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
#include<stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "xxx";
strncpy(arr1, arr2, 5);
return 0;
}
库函数strncpy代码形式
char * strncpy ( char * destination, const char * source, size_t num );
3.2strncat
Appends the first num characters of source to destination, plus a terminating null-character.
If the length of the C string in source is less than num, only the content up to the terminating
null-character is copied.
库函数strncat介绍
代码形式
char * strncat ( char * destination, const char * source, size_t num );
实现代码
//int main()
//{
// char arr1[20] = "abcdef\0yyyyyyyy";
// char arr2[] = "xxxxxxxxx";
// strncat(arr1, arr2, 3);
//
// return 0;
//}
/* strncat example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
puts (str1);
return 0;
}
3.3strncmp
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完
代码形式
int strncmp ( const char * str1, const char * str2, size_t num );
实现代码
//int main()
//{
// char arr1[] = "abcqwertyuiop";
// char arr2[] = "abcdef";
// printf("%d\n", strncmp(arr1, arr2, 4));
//
// return 0;
//}
//
/* strncmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts ("Looking for R2 astromech droids...");
for (n=0 ; n<3 ; n++)
if (strncmp (str[n],"R2xx",2) == 0)
{
printf ("found %s\n",str[n]);
}
return 0;
}
4.字符串查找
4.1strstr
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
库函数strstr介绍
/* strstr example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");
strncpy (pch,"sample",6);
puts (str);
return 0;
}
代码形式
char * strstr ( const char *str1, const char * str2);
我们先直接看代码
#include<stdio.h>
#include <string.h>
#include <assert.h>
char* my_strstr(char *str1, char* str2)
{
char* cp = str1;
char* s1 = cp;
char* s2 = str2;
if (*str2 == '\0')
return str1;
while (*cp)
{
//开始匹配
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return cp;
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "abbbcdef";
char arr2[] = "bbc";
char* ret = my_strstr(arr1, arr2);
if (ret != NULL)
printf("%s\n", ret);
else
printf("找不到\n");
return 0;
}
函数调用的返回类型为char返回字符串首元素的地址
星s1是a不是\0,星s2是b不是\0,他们不相等,不相等就退出循环,然后cp++得到b
依次循环往复,当找完了字符数组中的字符串之后就停止下来返回一个char类型的首元素地址然后进行一次判断星号s2等于\0就返回char*类型的指针然后从第一个地址开始打印字符串
4.2strtok
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在更多的标记,则返回 NULL 指针
库函数strtok介绍
代码形式
char * strtok ( char * str, const char * sep
库函数strtok基本实现形式
/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
模拟实现库函数strtok
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char arr[] = "zpengwei@yeah.net@666#777";
char copy[30];
strcpy(copy, arr);
char sep[] = "@.#";
char* ret = NULL;
for (ret = strtok(copy, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
第一个库函数strtok的第一个参数应该传copy
第二个库函数strtok的第一个参数应该是NULL
第三个库函数strtok同理----NULL
对于for循环知道走完条件部分也就是最后一个分号部分,如果分割出来的字符串不是空指针就继续循环打印,如果为假则跳出循环
5 错误信息报告
5.1strerror
返回错误码,所对应的错误信息
代码形式
char * strerror ( int errnum );
库函数strerror介绍
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d: %s\n", i, strerror(i));//
}
return 0;
}
我们知道C语言中有一个文件操作的知识
第一步打开文件
第二步可以读或者写文件
第三步----关闭文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
//C语言中可以操作文件
//操作文件的步骤
//1. 打开文件
//2. 读/写
//3. 关闭文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("fopen: %s\n", strerror(errno));
perror("fopen");
//fopen: xxxxxx
return 1;
}
//读文件
//...
//关闭文件
fclose(pf)
return 0;
}
我们可以直接在我们当前的这个VS路径上面创建一个.txt文件,但是你如果关闭了文件属性显示,又加了一个.txt后缀你打印的结果还是错误的,当你打开文件属性的时候就发现文件本身自带了一个.txt
具体操作如下,打开查看点击显示点击文件扩展名就有后缀了,大家一定要注意细节
6 字符操作
6.1字符分类函数
具体操作如下
//代码1
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
int main()
{
printf("%d\n", isupper('a'));
printf("%d\n", isdigit('2'));
printf("%c\n", tolower('A'));
printf("%c\n", tolower('s'));
char arr[20] = { 0 };
gets(arr);//遇到空格继续读
char* p = arr;
while (*p)
{
if (isupper(*p))// *p>='A' && *p<='Z'
{
*p = tolower(*p);//*p = *p+32;
}
p++;
}
printf("%s\n", arr);
return 0;
}
//代码2
/* isupper example */
#include <stdio.h>
#include <ctype.h>
int main ()
{
int i=0;
char str[]="Test String.\n";
char c;
while (str[i])
{
c=str[i];
if (isupper(c))
c=tolower(c);
putchar (c);
i++;
}
return 0;