参考: 里科《C和指针》
长度strlen
库函数strlen返回的是size_t(stddef.h中定义,是一个无符号整数)。如果需要跟int做比较、加减,需要加类型转换,否则结果可能是错的
if( strlen(x) >= strlen(y) )
if( strlen(x) - strlen(y) >= 0 ) // size_t是无符号数,相减结果也是size_t,肯定是永远非负
if( strlen(x) - 10 >= 0 ) // 这个也会永远true
#include <string.h>
#include <typeinfo>
char s[] = "test";
cout << typeid(strlen(s) - 10).name() << endl; // unsigned __int64
cout << strlen(s) - 10 << endl; // 18446744073709551610
cout << typeid(strlen(s)).name() << endl; // unsigned __int64
字符串复制strcpy
复制字符串使用的strcpy,要求目标字符数组的空间必须足够容纳待复制的字符串。strcpy不会检查目标字符数组的长度,所以复制过长的字符串时,会越界改写,报错。
char *strcpy(char *dst, char const *src);
字符串连接strcat
就是将src拷贝到dst末尾。同样需要程序员保证空间充足
char *strcat(char *dst, char const *src);
strcpy/strcat返回的是第一个参数的拷贝,即指向目标字符数组的指针。不过返回值一般都被忽略。
字符串比较strcmp
逐个字符比较,直到发现不匹配,此时如果那个字符比较小(字符集中的序数),则认为那个字符串比较小。使用strcmp,如果s1<s2,返回负值,大于返回正值,等于返回0。
int strcmp(char const *s1, char const *s2);
限定复制、连接和比较的长度strnXXX
strncpy、strncat和strncmp多加一个形参size_t len,用来限定操作范围。
strncpy:如果src的长度小于len,dst会用NUL填充到len长度;否则只复制len个,此时需要手动加\0,因为不会自动以NUL结尾
strncat:添加完后自动添加NUL
字符串查找单个字符
// 以下两个函数区分大小写
// 查找第一次出现的位置,找到后返回指针;找不到返回NULL
// char *ans = strchr(string, 'h');
char *strchr(char const *str, int ch);
// 从右边开始查找
char *strrchr(char const *str, int ch);
查找一组字符
只要其中一个字符出现即返回指针。区分大小写
char *strpbrk(char const *str, char const *group);
char string[20] = "Hello there";
char *ans = strpbrk(string, "aeiou");
// ans = string + 1,因为e出现了
查找一个子串
返回指针,指向s2在s1中第一次出现的位置。如果没有返回NULL。如果s2是空串,返回s1
char *strstr(char const *s1, char const *s2);
查找一个字符串前缀
size_t strspn(char const *str, char const *group);
// 这个c指的complement,指遇到匹配group的就停止
size_t strcspn(char const *str, char const *group);
int len1, len2;
char buffer[] = "25,142,330,Smith,J,239-4123";
len1 = strspn(buffer, "0123456789"); // 2,因为buffer[2]=',',不匹配了
len2 = strspn(buffer, ",0123456789"); // 11,因为buffer[11]='S',不再匹配
查找标记token
// 会修改原字符串,因为找到下一个标记时会给它以NUL结尾,再返回指向这个标记的指针
// 如果str是NULL, 就从上一次查找位置之后继续查找
char *strtok(char *str, char const *sep);
void
print_tokens(char* line)
{
static char whitespace[] = " \t\f\r\v\n";
char* token;
for (token = strtok(line, whitespace); token != NULL;
token = strtok(NULL, whitespace))
printf("Next is %s\n", token);
}
int main()
{
char s[] = " tes\tt";
print_tokens(s);
// Next is tes
// Next is t
return 0;
}
如果vs报错:‘strtok’: This function or variable may be unsafe. Consider using strtok_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS
去调试器的属性页-C/C+±预处理器-预处理器定义,编辑,增加_CRT_SECURE_NO_WARNINGS
同时,因为strtok保存了局部状态信息,所以不能同时解析两个字符串,因此,如果for循环的循环体中调用了一个在内部调用strtok的函数,上面的代码会失效
字符分类
iscntrl // 任何控制字符
isspace // 空格 换页\f 换行\n 回车\r 制表\t 垂直制表\v
isdigit // 0-9,十进制
isxdigit // 0-9+a-f+A-F十六进制
islower //
isupper // 最好不要用if(ch >='A' && ch <='Z')判断大小写,因为有的字符集不正确
isalpha // a~z+A~Z
isalnum // 字母+数字
ispunct //标点符号,任何不属于数字和字母的图形字符(可打印符号)
isgraph // 任何图形字符
isprint // 任何可打印字符,包括图形字符和空白字符
字符转换
int tolower(int ch);
int toupper(int ch);
内存操作
因为字符串函数处理不了内部有NUL的情况,但是下面可以。其中Length指的是字节,所以如果不是单字节的数据,需要使用sizeof
// 复制,但是dst和src重叠的结果未知
void *memcpy(void *dst, void const *src, size_t length);
// 如果是复制整型数组,可以把length写成sizeof(src)
// 如果只是复制部分,length=count * sizeof(src[0])
// 复制,dst和src可以重叠,比memcpy慢
void *memmove(void *dst, void const *src, size_t length);
void *memcmp(void const *a, void const *b, size_t length);
// 查找ch第一次出现的位置,共查找length个字节
void *memchr(void const *a, int ch, size_t length);
// 从a开始的length个字节设置为ch
void *memset(void *a, int ch, size_t length);