字符串
# 1 字符串长度
字符串是一串零个或多个字符,并且以一个位模式为0的NUL字节结尾。NUL字节是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度并不包括NUL字节。
size_t strlen(char const *string);
//size_t是一个无符号整数类型。
#include <stddef.h>
size_t strlen(char const *string)
{
int length;
for(length = 0; *string++ != '\0'; )
length += 1;
return length;
}
# 2 长度不受限制的字符串函数
“不受限制”是指,通过寻找字符串参数结尾的NUL字节来判断它的长度。
# 2.1 复制字符串
char *strcpy(char *dst, char const *src);
// 由于dst参数将进行修改,所以它必须是个字符数组或者是一个指向动态分配内存的数组的指针,不能使用字符串常量。
// 需要保证目标字符数组空间足以容纳源字符串
// 如果源字符串比目标字符串短,由于新字符串是以NUL字节结尾,老字符串后面的字符将被删除。
# 2.2 连接字符串
char *strcat(char *dst, char const *src);
// 把一个字符串连接到另一个字符串的后面,找到dst字符串的末尾,并把src字符串的一份拷贝添加到这个位置。
提示
strcpy和strcat 都返回它们第一个参数的一份拷贝,就是一个指向目标字符数组的指针。
# 2.3 字符串比较
int strcmp(char const *s1, char const *s2);
// 如果s1 小于 s2,strcmp函数返回小于零的值;如果s1 大于 s2,函数返回一个大于零的值;如果两个字符串相等,函数返回零。
# 3 长度受限制的字符串函数
限定字符数
char *strncpy(char *dst, char const *src, size_t len);
// 如果strlen(src)值小于len,dst数组就用额外的NUL字节填充到len长度。如果大于或等于len,那么只有len个字符被复制到dst中。 注意:结尾将不会以NUL字节结尾,需要自己添加NUL字符。
char *strncat(char *dst, char const *src, size_t len);
// strncat总是在结果字符串后面添加一个NUL字节
char strcmp(char const *s1, char const *s2, size_t len);
//如何证明strncpy()拷贝不会自动加字符串结束符'\0'
char dst[] = "aaaaaaaaaaaaaaa";
strncpy(dst, "123", 3);
printf("dst = %s\n", dst); //dst = "123aaaaaaaaaaaa"
# 4 字符串查找基础
# 4.1 查找一个字符
char *strchr(char const *str, int ch);
char *strrchr(char const *str, int ch);
// strchr 在字符串str中查找字符ch第1此出现的位置,返回一个指向该位置的指针;不存在返回NULL
// strrchr 功能和strchr基本一致,返回一个指向字符串中该字符最后一次出现的位置。
# 4.2 查找任何几个字符
char *strpbrk(char const *src, char const *group);
// 返回一个指向str中第1个匹配group中任何一个字符的字符位置。
# 4.3 查找一个子串
char *strstr(char const *s1, char const *s2);
// 在s1中查找整个s2第1次出现的起始位置,并返回一个指向该位置的指针。
# 5 错误信息
当调用一些函数,请求操作系统执行一些功能出现错误时,操作系统时通过设置一个外部的整型变量errno进行错误代码报告的。strerror函数把其中一个错误代码作为参数并返回一个指向用于描述错误的字符串的指针。
char *strerror(int error_number);
# 6 字符操作
头文件ctype.h
# 6.1 字符分类
序号 | 函数 & 描述 |
---|---|
1 | int isalnum(int c) 该函数检查所传的字符是否是字母和数字。 |
2 | int isalpha(int c) 该函数检查所传的字符是否是字母。 |
3 | int iscntrl(int c) 该函数检查所传的字符是否是控制字符。 |
4 | int isdigit(int c) 该函数检查所传的字符是否是十进制数字。 |
5 | int isgraph(int c) 该函数检查所传的字符是否有图形表示法。 |
6 | int islower(int c) 该函数检查所传的字符是否是小写字母。 |
7 | int isprint(int c) 该函数检查所传的字符是否是可打印的。 |
8 | int ispunct(int c) 该函数检查所传的字符是否是标点符号字符。 |
9 | int isspace(int c) 该函数检查所传的字符是否是空白字符。 |
10 | int isupper(int c) 该函数检查所传的字符是否是大写字母。 |
11 | int isxdigit(int c) 该函数检查所传的字符是否是十六进制数字。 |
# 6.2 字符转换
int tolower(int c); // 该函数把大写字母转换为小写字母。
int toupper(int c); // 该函数把小写字母转换为大写字母。
# 7 内存操作
字符串操作函数只能处理字符串数据,并且遇到第1个NUL字节停止,内存操作函数可以处理任意类型的序列,因为参数的类型是 void* 型指针,而任何类型的指针都可以转换为 void* 型指针,可以处理包括NUL字节在内的任意字节。
void *memcpy(void *dst, void const *src, size_t length);
void *memmove(void *dst, void const *src, size_t length);
void *memcmp(void const *a, void const *b, size_t length);
void *memchr(void const *a, int ch, size_t length);
void *memset(void *a, int ch, size_t length);
// memset函数把a开始的length个字节都设置为字符值ch,memset可以方便的清空一个结构类型的变量或数组。
//memset(&stTest,0,sizeof(struct sample_struct));
//如果是数组:
struct sample_struct TEST[10];
memset(TEST,0,sizeof(struct sample_struct)*10);
# 8 注意点
# 8.1 strcpy和memcpy的区别
char *strcpy(char *dest,const char *src) // 实现src到dest的复制
{
if((src == NULL) || (dest == NULL)) //判断参数src和dest的有效性
return NULL;
char *strSrc = src;
char *strDest = dest; //保存目标字符串的首地址
while((*strDest++ = *strSrc++) != '\0'); //把src字符串的内容复制到dest下
return dest;
}
void *memcpy(void *memTo,const void *memFrom,size_t size)
{
if((memTo == NULL) || (memFrom == NULL)) //memTo和memFrom必须有效
return NULL;
char *tempFrom = (char *)memFrom; //保存memFrom首地址
char *tempTo = (char *)memTo; //保存memTo首地址
while(size-- > 0) //循环size次,复制memFrom的值到memTo中
*tempTo++ = *tempFrom++ ;
return memTo;
}
strcpy和memcpy主要有以下3方面的区别:
- 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
- 复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度,与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束
- 用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
- memmove()与memcpy()一样都是用来拷贝src所指的内存内容前n个字节到dest所指的地址上。不同的是,当src和dest所指的内存区域重叠时,memmove()仍然可以正确的处理,不过执行效率上会比使用memcpy()略慢些。
#include <string.h>
#include <stdio.h>
int main()
{
char a[30] = "string (a)";
char b[30] = "hi\0testword";
int i;
strcpy(a, b); //a[30] = "hi\0ing (a)"
printf("strcpy():");
for(i = 0; i < 30; i++)
printf("%c", a[i]); //hi ing (a)
memcpy(a, b, 30); //a[30] = "hi\0testword"
printf("\nmemcpy():");
for(i = 0; i < 30; i++)
printf("%c", a[i]); //hi testword
printf("\n i = %d\n", i); //30
}
# 8.2 sizeof和strlen的区别
- sizeof为一个操作符,执行sizeof的结果,在编译期间就已经确定;strlen是一个函数,是在程序执行的时候才确定结果。
- sizeof和strlen对于求字符串来讲,sizeof() 字符串类型的大小,包括’\0’;strlen() 字符串的长度不包括‘\0’(数字 0 和字符‘\0’等价)。
# 9 应用
# 9.1 利用strstr标准库函数找出一个字符串中substr出现的个数
char *p = "11abcd111122abcd333abcd3322abcd3333322qqq";
int n = 0;
do {
p = strstr(p, "abcd");
if (p != NULL)
{
n++; //累计个数
//重新设置查找的起点
p = p + strlen("abcd");
}
else //如果没有匹配的字符串,跳出循环
{
break;
}
} while (*p != 0); //如果没有到结尾
# 9.2 两头堵模型-去掉头尾空格
char *p = " abcddsgadsgefg ";
int begin = 0;
int end = strlen(p) - 1;
int n = 0;
if(end < 0){
return;
}
//从左往右移动,如果当前字符为空,而且没有结束
while (p[begin] == ' ' && p[begin] != 0)
{
begin++; //位置从右移动一位
}
//从右往左移动,如果当前字符为空
while (p[end] == ' '&& p[end] != 0)
{
end--; //往左移动
}
n = end - begin + 1; //非空元素个数
strncpy(buf, p + begin, n);
buf[n] = 0;