在编程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列库函数。
1 字符函数
1.1 字符分类函数
C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。这些函数的使用都需要包含一个头文件ctype.h。
例如islower、isupper等,以islower为例:
int islower (int c);
islower能够判断参数部分的c是否是小写字母。通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。关于(int c),要注意到字符的本质是一个ASCII码值,这样的写法并没有问题。
1.2 字符转换函数
在之前,要一个字符大小写转换打印,用的是字符+/-32来实现的,例如:大写转小写就加上32、小写转大写就减去32(参见ASCII码相关知识)。现在,我们可以用以下库函数来更简洁的解决。
C语言提供了2个字符转换函数:
int tolower (int c);//将参数传进去的大写字母转小写
int toupper (int c);//将参数传进去的小写字母转大写
举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <ctype.h>
int main()
{
char i = 'a';
//i = i - 32;
i = toupper(i);
printf("%c\n", i);
return 0;
}
2 字符串函数
1 strlen的使用和模拟实现
size_t strlen ( const char * str );
- 字符串以
'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包
含'\0')。
- 参数指向的字符串必须要以
'\0'结束。 - 函数的返回值为
size_t,是无符号的(⭐)。 strlen的使用需要包含头文件string.h。
strlen的模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <string.h>
#include <stdio.h>
#include <assert.h>
//模拟实现strlen
size_t my_strlen1(const char* str)
{
assert(str != NULL);
size_t n = 0;
while (*str != '\0')
{
n++;
str++;
}
return n;
}
size_t my_strlen2(const char* str)
{
assert(str != NULL);
char* p = str;
while (*p)
{
p++;
}
return p - str;
}
size_t my_strlen3(const char* str)
{
assert(str != NULL);
if (*str)
{
return 1 + my_strlen3(str + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[] = { "abcdef" };
size_t r = strlen(arr);
size_t r1 = my_strlen1(arr);
size_t r2 = my_strlen2(arr);
size_t r3 = my_strlen3(arr);
}
2 strcpy的使用和模拟实现
char * strcpy ( char * destination, const char * source );
- 字符串复制。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'拷贝到目标空间。 - 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可修改。
strcpy的模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <string.h>
#include <stdio.h>
#include <assert.h>
//模拟实现strcpy
//char* my_strcpy(char* des, const char* src)
//{
// assert(des != NULL && src != NULL);
// char* ret = des;
// while (*src)
// {
// *des = *src;
// des++;
// src++;
// }
// *des = '\0';
// return ret;
//}
char* my_strcpy(char* des, const char* src)
{
assert(des != NULL && src != NULL);
char* ret = des;
while (*des++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = { "abcdef" };
char arr2[20] = {"xxxxxxxxxxxxxxxx"};
my_strcpy(arr2, arr1);
}
3 strcat的使用和模拟实现
char * strcat ( char * destination, const char * source );
- 字符串拼接。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'结束。 - 目标字符串中也得有
'\0',否则没办法知道追加从哪里开始。 - 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 将该函数用于字符串自己给自己追加是不合适的,其结果未定义。
strcat模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcat(char* des, const char* src)
{
assert(des && src);
char* ret = des;
while (*des != '\0')
{
des++;
}
while (*des++ = *src++)
{
;
}
return ret;
}
int main()
{
char a[20] = { "Hello \0xxxxxxxxxxx" };//目标字符串要有足够位置
char b[10] = { "world" };
//strcat(a, b);
my_strcat(a, b);
printf("%s\n", a);
}
4 strcmp的使用和模拟实现
int strcmp ( const char * str1, const char * str2 );
- 字符串比较。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.
- 判断两个字符串的大小:比较两个字符串中对应位置上字符ASCII码值的大小。
- 标准规定的返回值要求如下:
strcmp模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_strcmp1(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return (*str1 - *str2);
}
int my_strcmp2(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
else
return -1;
}
int main()
{
char a1[] = { "abcdef" };
char a2[] = { "abq" };
int r = strcmp(a1, a2);
int r1 = my_strcmp1(a1, a2);
int r2 = my_strcmp2(a1, a2);
return 0;
}
strcpy、strcat、strcmp三个函数有对应的更安全的版本(要多传一个参数),它们分别是strncpy、strncat和strncmp。
5 strncpy函数
char * strncpy ( char * destination, const char * source, size_t num );
- 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个。
6 strncat函数
char * strncat ( char * destination, const char * source, size_t num );
- Appends the first num characters of source to destination, plus a terminating null-character.(将
source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个\0字符)。 - If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.(如果
source指向的字符串的长度小于num的时候,只会将字符串中到\0的内容追加到destination指向的字符串末尾)。
7 strncmp函数
int strncmp ( const char * str1, const char * str2, size_t num );
- Compares up to num characters of the C string str1 to those of the C string str2.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, until a terminating null-character is reached, or until num characters match in both strings, whichever happens first.
- 比较
str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束。返回值标准参照strcmp函数。
8 strstr的使用和模拟实现
const char * strstr ( const char * str1, const char * str2 );
- Returns a pointer to the first occurrence of str2 in strl, ora null pointer if str2 is not part of strl.(函数返回字符串
str2在字符串str1中第一次出现的位置)。 - The matching process does not include the terminating null-characters, but it stops there.(字符串的比较匹配不包含
\0字符,以\0作为结束标志)。 - 返回值规定:A pointer to the first occurrence in str1 of the entire sequence of characters specified in str2, or a null pointer if the sequence is not present in str1.
strstr模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* cur = str1;//记录比较的起始位置
const char* s1 = NULL;//用s1在str1中遍历
const char* s2 = NULL;//用s2在str2中遍历
//特殊情况:str2是一个空字符串
if (*str2 == '\0')
return str1;
while (*cur)
{
s1 = cur;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return (char*)cur;
cur++;
}
return NULL;//找不到
}
int main()
{
char a[] = { "abbbcdef" };
char b[] = { "bbc" };
//char* r = strstr(a, b);
char* r = my_strstr(a, b);
printf("%s\n", r);
}
示意图:
str1、str2分别记录字符串a、b的起始位置;cur记录比较的起始位置;s1、s2用于遍历,完成一次比较后分别靠cur、str2复位。
另,字符串匹配算法——KMP算法。
9 strtok函数的使用
char * strtok ( char * str, const char * sep );
sep参数指向一个字符串,定义了用作分隔符的字符集合。- 第一个参数指定一个字符串,它包含了0个或者多个由
sep字符串中一个或者多个分隔符分割的标记。 strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。- 如果字符串中不存在更多的标记,则返回
NULL指针。
举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "zpw@bitedu.net.hehe";
char buf[20] = {0};//可以修改
strcpy(buf, arr);
//zpw\0bitedu\0net
const char* p = "@.";
char* r = NULL;
for (r = strtok(buf, p); r != NULL; r = strtok(NULL, p))
{
printf("%s\n", r);
}
//这样用是不合适的
//char* r1 = strtok(buf, p);
//printf("%s\n", r1);//zpw
//r1 = strtok(NULL, p);
//printf("%s\n", r1);//bitedu
//r1 = strtok(NULL, p);
//printf("%s\n", r1);//net
return 0;
}
10 strerror与perror函数的使用
strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
char * strerror ( int errnum );
在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在errno.h这个头文件中说明的,C语言程序启动的时候就会使用一个全局变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
除此之外,还有一个函数perror,它不需要传入错误码,会直接将错误信息打印出来。
void perror ( const char * str );
perror函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。
举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//以读的形式打开test.txt文件,如果文件不存在,则打开失败。
if (pf == NULL)
{
//打开文件失败了
perror("错误信息是");//: xxxx
printf("错误信息是: %s\n", strerror(errno));
return 1;
}
//往下走了
//...
return 0;
}