C语言中没有字符串类型,字符串通常会被放到常量字符串或者字符数组中,在程序的开发过程中要经常对字符和字符串进行处理,这就需要引入一些库函数来简化字符、字符串的处理操作,提高程序的开发效率。
strlen()
的函数原型为size_t strlen(const char *str)
,用于计算字符串长度(\0
前),\0
仅仅是字符串结束的标志,统计长度时不计算在内。
char *p = "abcdef";
int len = strlen(p);
模拟实现
int myStrlen(const char *pstr)
{
assert(pstr != NULL);
int len = 0;
while(*pstr != '\0')
{
len++;
pstr++;
}
return len;
}
int main()
{
char *p = "abcdefg";
int len = myStrlen(p);
printf("len = %d\n", len);
return 0;
}
模拟实现时使用的返回类型为int
,而库函数中的返回类型为size_t
无符号整型,将字符串的长度定义为无符号整型是很有道理的,因为字符串的长度是一个不小于零的数,但是要注意一个细节,看下面代码:
int main()
{
char *str1 = "abc";
char *str2 = "abcde";
if(strlen(str1) - strlen(str2) > 0)
{
printf("a > b");
}
else
{
printf("a <= b");
}
return 0;
}
输出结果为a > b
,这是因为当两个无符号整型数进行算数运算时,得到的结果仍然是无符号整数,所以strlen(str1) - strlen(str2)
得到的是一个值特别大的整数。
strcpy
的函数原型为char *strcpy(char *dest, const char *src)
,用于把 src
所指向的字符串复制到 dest
目标数组中,返回值类型为char *
指向最终的目标字符串 dest
的指针。源字符串必须以'\0'
结尾,在拷贝时会将'\0'
也拷贝过去。还要保证目标空间的大小要满足源字符串的大小,避免溢出。
char str[] = "abcdefg";
strcpy(str, "hehe");
printf("%s", str);
// 打印结果为hehe
模拟实现
char* myStrcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char *ret = dest;
while(*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char str[] = "abcdefg";
char *p = "hehe";
myStrcpy(str, p);
printf("%s", str);
return 0;
}
在使用strcpy
时一定要注意,目标空间不能是一个常量字符串,同时目标空间有足够大的内存大小。以下用法是错误的:
char *dest = "abcdefg";
strcpy(dest, "hehe"); // 错误
char arr[2] = {0};
strcpy(arr, "hehe"); // 错误
strcat()函数原型为char *strcat(char *dest, const char *src)
,用于把 src
所指向的字符串追加到 dest
所指向的字符串的结尾,返回值为指向最终的目标字符串 dest
的指针。使用该函数要保证以下几点:
1、源目字符串都要以\0
结尾;
2、目标空间足够大;
3、目标空间可修改。
char arr[20] = "hello";
strcat(arr, "world");
模拟实现
char* myStrcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while(*dest != '\0')
{
dest++;
}
while(*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char str[20] = "hello";
myStrcat(str, "world");
printf("%s", str);
return 0;
}
strcmp()
函数原型为int strcmp(const char *str1, const char *str2)
,用于把 str1
所指向的字符串和 str2
所指向的字符串进行比较,返回值类型为int
,当返回值小于 0,则表示 str1
小于 str2
,返回值大于 0,则表示 str1
大于 str2
,返回值等于 0,则表示 str1
等于 str2
。
char *str1 = "abcdef";
char *str2 = "ABCDEF";
int ret = strcmp(str1, str2);
printf("%d", ret);
模拟实现
int myStrcmp(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while(*str1 == *str2)
{
if(*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return *str1-*str2;
}
int main()
{
char *str1 = "aba";
char *str2 = "abcde";
int ret = myStrcmp(str1, str2);
printf("%d", ret);
return 0;
}
strncpy()
的函数原型为char *strncpy(char *dest, const char *src, size_t n)
,用于把 src
所指向的字符串复制到 dest
,最多复制 n
个字符。当 src
的长度小于 n
时,dest
的剩余部分将用空字节填充。该函数返回最终复制的字符串。
char arr[20] = {0};
strncpy(arr, "hello", 5);
printf("%s", arr);
模拟实现
char* myStrncpy(char* dest, const char* src, int sz)
{
assert(dest != NULL);
assert(src != NULL);
char *ret = dest;
while(sz && (*dest++ = *src++))
{
sz--;
}
if(sz)
{
while(--sz)
{
*dest++ = 0;
}
}
return ret;
}
int main()
{
char arr[20] = "aaaaaaaaaaaaaaa";
myStrncpy(arr, "hello", 6);
printf("%s", arr);
return 0;
}
strncat()
的函数原型为char *strncat(char *dest, const char *src, size_t n)
,用于把 src
所指向的字符串追加到 dest
所指向的字符串的结尾,直到 n
字符长度为止。该函数返回一个指向最终的目标字符串 dest
的指针。
char arr[20] = "aaa";
strncat(arr, "hello world", 7);
printf("%s", arr);
注意细节问题,向目标字符串追加时,从目标字符串的\0
开始进行覆盖,当指定的内容写入后,最后如果没有\0
,还要加上\0
以保证完成追加后,仍然是一个字符串。但是如果要写入的字符串本身包含\0
,此时程序会默认追加到\0
位置就停下。举例:
char arr[20] = "a\0aaaaaaaa";
strncat(arr, "he\0\0\0", 5);
printf("%s", arr);
// 只是为了测试,才这样写
// 此时通过监视,可以看到内存存储的数据为 ahe\0aaaaaa\0\0\0\0\0\0\0\0\0\0
可见strncat()
不同于strncpy()
,strncpy()
会在后面补\0
直到达到n
,而strncat()
遇到\0
就会停止。
模拟实现
char* myStrncat(char* dest, const char* src, int sz)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while(*dest)
{
++dest;
}
while(sz && (*dest++ = *src++))
{
--sz;
}
if(!sz)
{
*dest = '\0';
}
return ret;
}
int main()
{
char arr[20] = "aaaa";
myStrncat(arr, "hehe", 4);
printf("%s", arr);
return 0;
}
strncmp()
的函数原型为int strncmp(const char *str1, const char *str2, size_t n)
,用于把 str1
和 str2
进行比较,最多比较前 n
个字节。返回值类型为int
,当返回值小于 0,则表示 str1
小于 str2
,返回值大于 0,则表示 str1
大于 str2
,返回值等于 0,则表示 str1
等于 str2
。
strncmp("abcdef", "abcdfff", 4);
模拟实现
int myStrncmp(const char* str1, const char* str2, int sz)
{
assert(str1 != NULL);
assert(str2 != NULL);
while(sz && (*str1++ == *str2++))
{
--sz;
}
return *--str1-*--str2;
}
int main()
{
int ret = myStrncmp("abcdf", "abcd", 5);
printf("%d", ret);
return 0;
}
strstr()
的函数原型为char *strstr(const char *haystack, const char *needle)
,用于在字符串 haystack
中查找第一次出现字符串 needle
的位置,注意不包含终止符 ‘\0’。该函数的返回值为在 haystack
中第一次出现 needle
字符串的位置,如果未找到则返回 null
。
char *str1 = "abcdabcdefghi";
char *str2 = "abcdef";
char *ret = strstr(str1, str2);
printf("%s", ret);
// 打印结果 abcdefghi
模拟实现
char* myStrstr(char* str1,char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
const char *s1 = str1;
const char *s2 = str2;
const char *cp = str1;
if(!*str2)
return (char*)str1;
while(*cp)
{
s1 = cp;
s2 = str2;
while(*s1 && *s2 && *s1==*s2)
{
s1++;
s2++;
}
if(!*s2)
return (char*)cp;
cp++;
}
return NULL;
}
int main()
{
char *str1 = "abcc2ccccde";
char *str2 = "c2c";
char *ret = myStrstr(str1, str2);
printf("%s", ret);
return 0;
}
strtok()
函数原型为char *strtok(char *str, const char *delim)
,用于将字符串 str
分解为一组子字符串,delim
为分隔符。该函数返回值为被分解的第一个子字符串地址,如果没有可检索的字符串,则返回一个空指针。
使用该函数时的注意事项:
1、delim
参数是一个字符串,定义了用作分隔符的字符集合;
2、str
指定一个字符串,包含了0个或者多个被delim
字符串中一个或者多个分隔符分割的子串;
3、strtok()
函数找到str
中的下一个分割符,并将其用'\0'
替换,返回一个指向这个子串的指针;
4、strtok()
函数需要改变被操作的字符串,所以在使用strtok()
函数切分的字符串一般都是临时拷贝的内容并且可修改;
5、如果strtok()
函数的str
不为NULL
,函数将找到str
中的第一个分隔符,并且保存它在字符串中的位置;
6、如果strtok()
函数str
为NULL
,函数将在同一个字符串中被保存的位置开始,查找下一个分隔符。
7、如果字符串中不存在更多的标记,则返回NULL
指针。
char arr[] = "[email protected]";
char *p = "@.";
char *ret = NULL;
for(ret = strtok(arr,p); ret != NULL; ret = strtok(NULL, p))
{
printf("%s\n", ret);
}
strerror
包含在errno.h
头文件中,函数原型为char *strerror(int errnum)
,用于从内部数组中搜索错误号 errnum
,并返回一个指向错误消息字符串的指针。strerror
生成的错误字符串取决于开发平台和编译器。
int main ()
{
FILE *fp;
fp = fopen("file.txt","r");
if( fp == NULL )
{
printf("Error: %s\n", strerror(errno));
}
return(0);
}
全局变量errno
错误码,当使用库函数发生错误时,会将errno
置成对应的错误码,用于输出对应的错误信息。
函数 | 如果参数符合下列条件就返回为真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符,空格’ ‘,换页’\f’ ,换行’\n’,回车’\r’,制表符’\t’,垂直制表符’\v’ |
isdigit | 十进制数字0-9 |
isxdigit | 十六进制数字 |
islower | 小写字母 |
isupper | 大写字母 |
isalpha | 字母 |
isalnum | 字母数字 |
ispunct | 任何不属于数字字母的可打印图形字符 |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
以上函数包含在ctype.h
头文件中
tolower
函数原型 int tolower(int c)
toupper
函数原型 int toupper(int c)
memcpy
包含在string.h
头文件中,其函数原型为void *memcpy(void *str1, const void *str2, size_t n)
,用于从存储区 str2
复制 n
个字节到存储区 str1
,返回值为一个指向目标存储区 str1
的指针。注意此函数在处理过程中的强制类型转换,类型强制转换为 void*
指针。
int arr1[] = {1,2,3,4,5};
int arr2[20] = {0};
当数组中存放的不再是字符串时,如果使用strcpy
函数进行拷贝会导致拷贝的内容出错。
strcpy(arr2, arr1);
// 此时只拷贝了一个字节
// 原因:arr1[]在内存中存放顺序为 01 00 00 00 02 00 00 00```
// 而strcpy在遇到 \0 时会停止拷贝
所以此时就要使用memcpy
进行内存拷贝
int arr1[] = {1,2,3,4,5};
int arr2[20] = {0};
memcpy(arr2, arr1, 20);
模拟实现
void* myMemcpy(void* str1, const void* str2, int sz)
{
assert(str1 != NULL);
assert(str2 != NULL);
void *ret = str1;
char* s1 = (char*)str1;
char* s2 = (char*)str2;
while(sz--)
{
*s1++ = *s2++;
}
return ret;
}
memmove()
函数同样包含在string.h
头文件中,其原型为void *memmove(void *str1, const void *str2, size_t n)
,其作用为从 str2
复制 n
个字符到 str1
,但是在重叠内存块这方面,memmove()
是比 memcpy()
更安全的方法。如果目标区域和源区域有重叠的话,memmove()
能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy()
函数功能相同。
模拟实现
void* myMemmove(void* str1, const void* str2, int sz)
{
assert(str1 != NULL);
assert(str2 != NULL);
void *ret = str1;
char* s1 = (char*)str1;
char* s2 = (char*)str2;
if(s1 > s2)
{
while(sz--)
{
*(s1+sz) = *(s2+sz);
}
}
else if(s1 < s2)
{
while(sz--)
{
*s1++ = *s2++;
}
}
return ret;
}
memset()
函数的函数原型为void *memset(void *str, int c, size_t n)
,用于复制字符 c
(一个无符号字符)到参数 str
所指向的字符串的前 n
个字符。参数为str
指向要填充的内存块;c
是要被设置的值,该值以 int
形式传递,但是函数在填充内存块时是使用该值的无符号字符形式;n
为要被设置为该值的字节数。返回值是一个指向存储区 str
的指针。
char str[50];
memset(str,'$',7);
memcmp()
函数的原型为int memcmp(const void *str1, const void *str2, size_t n))
,用于把存储区 str1
和存储区 str2
的前 n
个字节进行比较,如果返回值 < 0
,则表示 str1
小于 str2
,如果返回值 > 0
,则表示 str2
小于 str1
,如果返回值 = 0
,则表示 str1
等于 str2
。
int arr1[] = {1,2,3,4,5};
int arr2[] = {1,2,3};
memcmp(arr1, arr2, 12);