06
—
高级字符串查找
接下来的一组函数简化了从一个字符串中查找和抽取一个子串的过程。
1.查找一个字符串前缀
strspn和strcspn函数用于在字符串的起始位置对字符计数。它们的原型如下:
size_t strspn(char const *str, char const *group);size_t strcspn(char const *str, char const *group);
group字符串指定一个或多个字符。strspn返回str第一个不在字符串 group中出现的字符下标。例如,如果group包含了空格、制表符等空白字符。
下面例子,
int len1, len2;char buffer[] = "25.142.330,Smith,J,239-4123";len1 = strspn(buffer, "0123456789");len2 = strspn(buffr, ".0123456789");
变量len1将被置为2,变量len2将被置为11。下面的代码将计算第一个指向字符串中第一个非空白字符的指针。
ptr = buffer + strspn(buffer, "\n\r\f\t");
strcspn函数和strspn函数正好相反,它将返回第一个在group中的字符的下标。strcspn这个词中的字母c来源于对一组字符求补这个概念。
2.查找标记
一个字符串常常包含了几个有意义的部分,它们被分隔符分隔开来。每次为了处理这些部分,你首先必须把它们从字符串中抽取出来。
这个任务正是strtok函数所实现的功能。它从字符串中隔离各个单独的称为标记(token)的部分,并丢弃分隔符。它的原型如下:
char *strtok(char *str, char const *sep);
sep参数是个字符串,定义了用作分隔符的字符集合。第1参数指定一个字符串,它包含零个或多个由sep字符串中一个或多个分隔符分隔的标记。strtok找到str的下一个标记,并将其用NUL结尾,然后返回一个指向这个标记的指针。若没有可检索的字符串,则返回一个空指针。
下面是一个简短的例子,
char str[80] = "This is - cxsjcrmdjt - official account";const char sep[2] = "-";char *token;/* 获取第一个子字符串 */token = strtok(str, sep);/* 继续获取其他子字符串 */while(token != NULL){ printf("%s\n", token); token = strtok(NULL, s);}
编译并运行上面程序,这将产生以下结果:
This is
cxsjcrmdjt
official account
如果你愿意,你可以在每次调用strtok函数时使用不同的分隔符集合。当一个字符串的不同部分由不同的字符集合分隔的时候,这个技巧很管用。
警告:
由于strtok函数保存它处理的函数的局部状态信息,所以你不能用它同时解析两个字符串,因此,如果while循环体内调用了一个在内部调用strtok函数的函数,上述代码将会失败。
07
—
错误信息
当你调用一些函数,请求操作系统执行一些功能如打开文件时,如果出现错误,操作系统是通过设置一个外部的整型变量errno进行错误代码报告的。strerror函数把其中一个错误代码作为参数并返回一个指向用于描述错误的字符串的指针。这个函数的原型如下:
char *strerror(int error_number);
08
—
字符操作
标准库包含了两组函数,用于操作单独的字符,它们的原型位于头文件ctype.h。第一组函数用于对字符分类,而第二组用于转换字符。
1.字符分类
每个分类函数接受一个包含字符值的整型参数。函数测试这个字符并返回一个整型值,表示真或假。下表列出了这些分类函数以及它们每个所执行的测试。
2.字符串转换
转换函数可将大小写字母相互转换。
int tolower(int ch);int toupper(int ch);
如其函数名一样,toupper函数返回对应的大写形式,tolower函数返回对应的小写形式。如果函数的参数并不是一个可以大小写转换的字符,函数将不修改参数直接返回。
提示:
或许你会认为这些函数的实现简单,可以很轻易地被自己的实现替代,但是直接操控字符将会降低程序的可移植性,考虑以下例子,它试图测试ch是否是一个大写字符。
if (ch >= 'A' && ch <= 'Z')
这条语句在使用ASCII字符集的机器上能够运行,但在使用EBCDIC字符集的机器上将会失败。若使用下面这条语句:
if (isupper(ch))
无论机器使用哪种字符集,它都能顺利执行。
09
—
内存操作
根据定义,字符串由一个NUL字节结尾,所以字符串内部不能包含任何NUL字符。但是,非字符串数据内部包含零值的情况并不罕见,你无法使用字符串函数来处理这种类型的数据,因为当它们遇到第一个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);
每个原型都包含一个显式的参数说明需要处理的字节数。但和strn开头的函数不同,它们在遇到NUL字节时并不会停止操作。
memcpy从src的起始位置复制length个字节到dst的内存起始位置。你可以用这种方法复制任何类型的值,第三个参数指定复制值的长度(以字节计)。如果src和dst以任何形式出现了重叠,它的结果是未定义的。
例如,
char temp[SIZE], values[SIZE];...memcpy(temp, values, SIZE);
它从数组values复制SIZE个字节到数组temp。
但是,如果两个数组都是整型数组该怎么办?下面的语句可以完成这项任务:
memcpy(temp, values, sizeof(values));
前两个参数并不需要使用强制类型转换,因为在函数的原型中,参数的类型是void*型指针,而任何类型的指针都可以转换为void*型指针。
如果数组只有部分内容需要被复制,那么需要复制的数量必须在第三个参数中指明。对于长度大于一个字节的数据,要确保把数量和数据类型的长度相乘,如:
memcpy(dst, array, count * sizoef(array[0]));
你也可以使用这种技巧复制结构体或者结构体数组。
memmove函数的行为和memcpy差不多,只是它的源和目的操作数可以重叠。虽然它不需要以下面这种方式实现,不过memmove的过程和这种方法的过程相同:把源操作数复制到一个临时位置,这个临时位置不会与源或目标操作数重叠,然后再把它从这个临时位置复制到目标操作数。memmove通常无法使用某些机器所提供的特殊的字节-字符串处理指令来实现,所以它可能比memcpy慢一些。但是,如果源和目标参数真的可能存在重叠,就应该使用memmove。
memcmp对两端内存的内容进行比较,这两端内存分别起始于a和b,共比较length个字节。这些值按照无符号字符逐字节进行比较,函数的返回类型和strcmp函数一样----负值表示a小于b,正值表示a大于b,零表示a等于b。由于这些值是根据一串无符号字节进行比较的,所以如果memcmp函数用于比较不是单字节的数据如整数或浮点数时可能会出现不可预料的结果。
memchr从a的起始位置开始查找字符ch第一次出现的位置,并返回一个指向该位置的指针,它共查找length个字节。如果在这length个字节中未找到该字符,函数就返回一个NULL指针。
memset函数把从a开始的length个字节都设置为值ch。如:
memset(buffer, 0, SIZE);
把buffer的前SIZE个字节都初始化为0。
10
—
总结
字符串就是零个或多个字符的序列。该序列以一个NUL字节结尾,字符串的长度就是它所包含的字符的数目。标准库提供了处理字符串的函数,它们的原型位于头文件string.h中。
strlen函数用于计算一个字符串的长度,它的返回值是一个无符号整数,所以把它用于表达式时应该小心。strcpy函数把一个字符串从一个位置复制到另一个位置,而strcat函数把一个字符串的一份拷贝添加到另一个字符串的后面。这两个函数都假定它们的参数是有效的字符串,而且如果源字符串和目标字符串出现重叠,函数的结果是未定义的。strcmp对两个字符串进行词典序的比较。它的返回值提示第1个字符串是大于、小1还是等于第2个字符串。
长度受限的函数strncpy, strncat和 strncmp都类似它们对应的不受限制版本,区别在于这些函数还接受一个长度参数。在strncpy中,长度指定了多少个字符将被写入到目标字符数组中。如果源字符串比指定长度更长,结果字符串将不会以NUL字节结尾。strncat函数的长度参数指定从源字符串复制过来的字符的最大数目,但它的结果始终以一个NUL字节结尾, strcmp函数的K度参数用于限定字符比较的数目。如果两个字符串在指定的数日里不存在区别,它们便被认为是相等的。
用于查找字符串的函数有好几个. strchr函数查找一个字符申中某个字符第1次出现的位置。strrchr函数查找一个字符串中某个字符最后-次出现的位置。strpbrk在一个字符串4查找一个指定字符集中任意字符第1次出现的位置。strstr函数在一个字符串中查找另一个字符申第1次出现的位置。
标准库还提供了了一些更高级的字符串查找函数。strspn函数计算一个字符串中第一个不在指定字符集出现的字符下标。strtok函数把一个字符串分隔成几个部分,每次调用它都返回一个指向下一个标记位置的指针。
sterror把一个错误码作为它的参数。它返回一个指向字符串的指针,该字符串用于描述这个错误。
标准库还提供了检验和转换字符的函数。具体查表。
memxxx函数提供了内存操作的功能。