第9章 字符串、字符和字节

size_t strlen( char const *string );
char *strcpy( char *dst, char const *src );
char *strcat( char *dst, char const *src );
int strcmp( char const *s1, char const *s2 );
char *strncpy( char *dst, char const *src, size_t len );
char *strncat( char *dst, char const *src, size_t len );
int strncmp( char const *s1, char const *s2, size_t len );
char *strchr( char const *str, int ch );
char *strrchr( char const *str, int ch );
char *strpbrk( char const *str, char const *group );
char *strstr( char const *s1, char const *s2 );
size_t strspn( char const *str, char const *group );
size_t strcspn( char const *str, char const *group );
char *strtok( char *str, char const *sep );
char *strerror( int error_number );
int tolower( int ch );
int toupper( int ch );
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 );

学习笔记

1. 库函数strlen

size_t strlen( char const *string );

注意strlen返回一个类型为size_t的值。这个类型是在头文件stddef.h中定义的,它是一个无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。例如,下面两个表达式是不相等的,由于strlen的结果是个无符号数,所以第二条语句的结果将永远是真。

if( strlen(x) >= strlen(y) ) ...
if( strlen(x) - strlen(y) >= 0 ) ...

如果把strlen的返回值强制转换为int,就可以消除这个问题。

2. 库函数strncpy

char *strncpy( char *dst, char const *src, size_n len );

如果strlen(src)的值小于lendst数组就用额外的NUL字节填充到len长度。如果strlen(src)的值大于或等于len,那么只有len个字符被复制到dst中。注意!它的结果将不会以NUL字节结尾。
尽管strncat也是一个长度受限的函数,但它却和strncpy存在不同之处。它从src中最多复制len个字符到目标数组的后面。但是,strncat总是在结果字符串后面添加一个NUL字节,而且它不会像strncpy那样对目标数组用NUL字节进行填充。

3. 库函数strchr和strrchr

char *strchr( char const *str, int ch );
char *strrchr( char const *str, int ch );

strchr在字符串str中查找字符ch第一次出现的位置,找到后函数返回一个指向该位置的指针。如果该字符并不存在于字符串中,函数就返回一个NULL指针。strrchr的功能和strchr基本一致,只是它返回的是一个指向字符串中该字符最后一次出现的位置(最右边那个)。

4. 库函数strpbrk

char *strpbrk( char const *str, char const *group );

这个函数返回一个指向str中第一个匹配group中任何一个字符的字符位置。如果未找到匹配,函数返回一个NULL指针。

5. 库函数strstr

char *strstr( char const *s1, char const *s2 );

这个函数在s1中查找整个s2第一次出现的起始位置,并返回一个指向该位置的指针。如果s2并没有完整地出现在s1的任何地方,函数将返回一个NULL指针。如果第二个参数是一个空字符串,函数就返回s1
标准库中并不存在strrstrstrrpbrk函数。不过,如果你需要它们,它们是很容易实现的。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

/* 字符串中最后一个匹配group的位置 */
char *strrpbrk(char const *str, char const *group){
    char *last = NULL, *current = NULL;
    if(*group == '\0')
        return NULL;
    current = strpbrk(str, group);
    while(current != NULL){
        last = current;
        current = strpbrk(last + 1, group);
    }
    return last;
}

/* 字符串中最后一个匹配子字符串的位置 */
char *strrstr(char const *s1, char const *s2){
    char *current = NULL, *last = NULL;
    /* 如果s2为空则返回s1 */
    if(*s2 == '\0')
        return s1;
    current = strstr(s1, s2);
    while(current != NULL){
        last = current;
        current = strstr(last + 1, s2);
    }
    return last;
}

int main(){
    char *x = "1234523", *y = "", *res = NULL;
    res = strrpbrk(x, y);
    printf("%s\n", res);
    res = strrstr(x, y);
    printf("%s\n", res);
    return EXIT_SUCCESS;
}

6. 库函数strspn和strcspn

size_t strspn( char const *str, char const *group );
size_t strcspn( char const *str, char const *group );

group字符串指定一个或多个字符,strspn返回字符串str从第一个字符开始在字符串group中字符的数量。strcspnstrspn函数的作用正好相反。

/* ptr指向buffer第一个非空白字符 */
/*
\n - 换行
\r - 回车
\f - 换页
\t - 制表符
\v - 垂直制表符
*/
ptr = buffer + strspn(buffer, "\n\r\f\t\v");

7. 库函数strtok

char *strtok( char *str, char const *sep );

sep参数是个字符串,定义了用作分隔符的字符集合。第一个参数指定一个字符串,它包含零个或多个由sep字符串中一个或多个分隔符分隔的标记。strtok找到str的下一个标记,并将其用NUL结尾,然后返回一个指向这个标记的指针。
如果strtok函数的第一个参数不是NULL,函数将找到字符串的第一个标记。strtok同时将保存它在字符串中的位置。如果strtop函数的第一个参数是NULL,函数就在同一个字符串中从这个被保存的位置开始像前面一样查找下一个标记。strtok函数是不可再入的。

#include<stdio.h>
#include<string.h>

int main(){
    char str[] = "my name is cc.", *token;
    char const *sep = " ";
    for(token = strtok(str, sep); token != NULL; token = strtok(NULL, sep))
        printf("%s\n", token);
}

书后练习

问题4

如果从数组x复制50个字节到数组y,最简单的方法是什么?

memcpy(y, x, 50);

问题8

下面的表达式有何不同?

memchr( buffer, 0, SIZE ) - buffer
strlen( buffer )

如果缓冲区包含了一个字符串,memchr将在内存中buffer的起始位置开始查找第一个包含0的字节并返回一个指向该字节的指针。将这个指针减去buffer获得存储在这个缓冲区中的字符串的长度。strlen函数完成相同的任务,不过strlen的返回值是个无符号类型的值,而指针减法的值应该是个有符号类型。
但是,如果缓冲区内的数据并不是以NULL字节结尾,memchr函数将返回一个NULL指针。将这个值减去buffer将产生一个无意义的结果。另一方面,strlen函数在数组的后面继续查找,直到最终发现一个NUL字节。

编程练习2

编写一个名叫my_strlen的函数。它类似于strlen函数,但它能够处理由于使用strn—函数而创建的未以NUL字节结尾的字符串。你需要向函数传递一个参数,它的值就是保存了需要进行长度测试的字符串的数组的长度。

/* my_strlen */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

size_t my_strlen1(char const *str, size_t length){
    size_t len = strlen(str);
    if(len < length)
        return len;
    return length;
}

size_t my_strlen2(char const *str, int length){
    size_t len;
    for(len = 0; len < length; len++)
        if(*str++ == '\0')
            break;
    return len;
}

int main(int argc, char*argv[]){
    char *string = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    size_t length1 = 30, res;
    int length2 = 30;
    res = my_strlen1(string, length1);
    printf("%u\n", res);
    res = my_strlen2(string, length2);
    printf("%u\n", res);
    return EXIT_SUCCESS;
}

编程练习6

编写一个名叫my_strcpy_end的函数取代strcpy函数,它返回一个指向目标字符串末尾的指针(也就是说,指向NUL字节的指针),而不是返回一个指向目标字符串起始位置的指针。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

char *my_strcpy_end(char *dst, char const *src){
    char *ptr = strcpy(dst, src) + strlen(dst);
    return ptr;
}

int main(int argc, char *argv[]){
    char dst[10], *src = "123";
    char *res = my_strcpy_end(dst, src);
    printf("%s\n", res);
    return EXIT_SUCCESS;
}

编程练习11

编写一个程序,对标准输入进行扫描,并对单词“the”出现的次数进行计数。进行比较时应该区分大小写,所以“The”和“THE”并不计算在内。你可以认为各单词由一个或多个空格字符分隔,而且输入行在长度上不会超过100个字符。计数结果应该写道标准输出上。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_SIZE 100

int main(int argc, char *argv[]){
    char input_string[MAX_SIZE], *token;
    char const sep[] = " \n\r\f\t\v";
    int count = 0;
    while(gets(input_string)){
        for(token = strtok(input_string, sep);
        token != NULL;
        token = strtok(NULL, sep))
            if(strcmp(token, "the") == 0)
                count++;
    }
    printf("There are %d 'the' in the string.\n", count);
    return EXIT_SUCCESS;
}

编程练习15

标准I/O库并没有提供一种机制,在打印大整数时用逗号进行分隔。在这个练习中,你需要编写一个程序,为美元数额的打印提供这个功能。函数把一个数字字符串(代表以美分为单位的金额)转换为美元形式,如下面的例子所示:

输入输出输入输出
$0.0012345$123.45
1$0.01123456$1,234.56
12$0.121234567$12,345.67
123$1.2312345678$123,456.78
1234$12.34123456789$1,234,567.89

下面是函数的原型:

void dollars( char *dest, char const *src );

src将指向需要被格式化的字符(你可以假定它们都是数字)。函数应该像上面例子所示的那样对字符进行格式化,并把结果字符串保存到dest中。你应该保证你所创建的字符串以一个NUL字节结尾。src的值不应该被修改。你应该使用指针而不是下标。
提示:首先找到第2个参数字符串的长度。这个值有助于判断逗号应插入到什么位置。同时,小数点和最后两位数字应该是唯一的需要你进行处理的特殊情况。

#include<stdio.h>
#include<string.h>

void dollars(char *dest, char const *src){
    int len = strlen(src);
    char *ptr = dest;
    if(dest == NULL || src == NULL)
        return;
    /* 把$添加到dest中 */
    *dest++ = '$';
    /* 如果src的长度大于3 */
    if(len >= 3){
        for(int i = len - 2; i > 0; ){
            *dest++ = *src++;
            /* 判断剩下的位数是不是3的倍数 */
            if(--i > 0 && i % 3 == 0)
                *dest++ = ',';
        }
    }else
        *dest++ = '0';
    *dest++ = '.';
    *dest++ = len < 2 ? '0' : *src++;
    *dest++ = len < 1 ? '0' : *src;
    *dest = '\0';
    printf("%s\n", ptr);
}

int main(int argc, char *argv[]){
    char dest[100] = "", *src = "123456789";
    dollars(dest, src);
    return 1;
}
  • 30
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值