C语言(Head First C)-3:字符串

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

 3 字符串:字符串原理

 

 字符串不只是用来读取的:

 C中的字符串其实就是char数组;那字符串能用来干嘛?

 

 该String.h出场了:

 String.h是C标准库中的一员;负责处理字符串;如果想要连接、比较或复制字符串,String.h中的函数就可以派上用处;

 

 先看一个目标:

 -创建字符串数组,并使用strstr()函数搜索字符串;

 

 创建数组的数组:

 字符串本身是字符数组,所以用数据记录多个字符串,就是说需要创建数组的数组;

 如:

 char tracks[][12] = {"abc","123"};

 第一对方括号用来访问有所有字符串组成的数组;(编译器可以识别有两个字符串,所以不用指定长度)

 第二对方括号用来访问每个单独的字符串;(假设每个字符串最大长度为12)

 这就是一个数组的数组;

 

 可以像这样访问:

 tracks[1] -> 读取某个字符串;

 tracks[1][2] -> 读取某个字符串的某个字符;

 

 (Code3_1)

/*
 * 字符串数组
 */

#include <stdio.h>

int main() {
    
    char tracks[][12] = {
        "I love you1",
        "I love you2",
        "I love you3",
        "I love you4",
        "I love you5",
        "you love I1",
        "you love I2",
        "you love I3",
        "you love I4",
        "you love I5",
    };
    printf("Tracks number:%lu\n",sizeof(tracks));
    
    for (int i = 0 ; i < sizeof(tracks)/sizeof(tracks[0]); i++) {
        
        printf("Tracks[%i] %s number:%lu\n",i,tracks[i],sizeof(tracks[i]));
        
        for (int j = 0; j < sizeof(tracks[i]); j++) {
//            printf("%c",tracks[i][j]);
        }
    }
    
    return 0;
}


 log:

 Tracks number:120

 Tracks[0] I love you1 number:12

 Tracks[1] I love you2 number:12

 Tracks[2] I love you3 number:12

 Tracks[3] I love you4 number:12

 Tracks[4] I love you5 number:12

 Tracks[5] you love I1 number:12

 Tracks[6] you love I2 number:12

 Tracks[7] you love I3 number:12

 Tracks[8] you love I4 number:12

 Tracks[9] you love I5 number:12

 

 这里实践了访问数组的方式,还有如何计算数组长度;

 

 找到包含搜索文本的字符串:

 输入指定字符串,然后遍历数组,包含指定字符串的话,就将遍历到的字符串打印出来;

 

 那么如何判断字符串中包含某段文本呢?

 

 使用string.h:

 安装C编译器时可以免费得到一批很有用的代码——C标准库;

 可以做很多事:打开文件、做算术、管理存储器;标准库分了好几个部分,每个部分都有一个头文件,列出这部分标准库中的所有函数;

 

 目前用到的stdio.h,提供了标准输入/输出函数,如printf和scanf;

 

 string.h就是标准库中处理字符串的代码;稳定可靠,速度也很快;比如 比较、复制、搜索、切割字符串等;

 首先你需要,#include <string.h>

 再看看常用的一些函数:

 -strchr()  ->  在字符串中查找字符

 -strcmp()  ->  比较字符串

 -strstr()  ->  在字符串中查找字符

 -strcpy()  ->  复制字符串

 -strlen()  ->  返回字符串长度

 -strcat()  ->  连接字符串

 

 使用strstr()函数:

 例如:strstr("abcdef","abc");

 strstr()函数会在第一个字符串中查找第二个字符串;

 如果找到,返回第二个字符串在存储器中的位置;

 如果找不到,就会返回0;(C中0相当于假)

 

 (Code3_2)

/*
 * String.h
 */

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

char tracks[][12] = {
    "I love you1",
    "I love you2",
    "I hate you3",
    "I love you4",
    "I love you5",
    "you love I1",
    "you love I2",
    "you love I3",
    "you love I4",
    "you love I5",
};

void track_str(char * str){
    for (int i = 0; i < sizeof(tracks) / sizeof(tracks[0]); i++) {
        if (strstr(tracks[i],str)) {
            printf("在数组第%i的位置的%s中找到了%s。\n",i,tracks[i],str);
        }
    }
}

int main() {
    
    char * str1 = "abcdefg";
    printf("字符串长度-%lu\n",strlen(str1));
    
    char * str2 = "de";
    printf("str2 子串 在 str1中,从 %p 位置开始\n",strstr(str1,str2));
    if(strstr(str1,str2)){
        printf("在str1中找到了str2字符串!\n");
    }
    
    char cur[12];
    puts("有了上面的测试,接下来让我们输入一个字符串在给定的字符串数组中进行查找:");
    fgets(cur,12,stdin);
    
    printf("字符串-%s\n",cur);
    for (int i = 0 ; i < strlen(cur); i++) {
        printf("字符-%c\n",cur[i]);
    }
    printf("字符串长度-%lu\n",strlen(cur));
    
    printf("设置字符串最后一位为\\0");
    
    cur[strlen(cur) - 1] = '\0';
    
    printf("字符串--%s\n",cur);
    for (int i = 0 ; i < strlen(cur); i++) {
        printf("字符--%c\n",cur[i]);
    }
    printf("字符串长度--%lu\n",strlen(cur));
    
    track_str(cur);
    
    return 0;
}


 log:

 str2 子串 在 str1中,从 0x1022f5e62 位置开始

 在str1中找到了str2字符串!

 有了上面的测试,接下来让我们输入一个字符串在给定的字符串数组中进行查找:

 hate

 字符串-hate

 

 字符-h

 字符-a

 字符-t

 字符-e

 字符-

 

 字符串长度-5

 设置字符串最后一位为\0字符串--hate

 字符--h

 字符--a

 字符--t

 字符--e

 字符串长度--4

 在数组第2的位置的I hate you3中找到了hate。

 

 这是一个完整的例子;其中有很多值得注意的地方:

 1)关于fgets:

 指定的是可接受字符串的最大长度,包括了存储\0的位置,之所以cur[strlen(cur) - 1] = '\0';这样做是为了为fgets接收的字符串指定哨兵字符,以标示字符串的结尾;(哨兵字符并不占字符串的长度,但却需要占字符串对应的字符数组的一个位置);否则的话fgets直接接收的字符串会比录入的多一个长度,是给\0预留的(由于没有\0的录入,可能会影响字符串,因为你无法标示这个字符串何时结束);

scanf()不会出现这种情况,这里举个例子说明下:

(1)char ex[20]; scanf("%19s",ex); printf("%lu",strlen(ex));

log:

fff

3


(2)char ex[20]; fgets(ex,20,stdin); printf("%lu",strlen(ex));

log:

fff

4


 2)代码的排列顺序:

 顶部包含头文件,这样编译器就能在编译代码之前把所有的函数都准备好;

 在开始写函数之前,定义tracks,这叫把tracks数组放在全局区;全局变量位于任何函数之外,所有函数都可以调用它们;

 最后是两个函数:track_str()和main();前者必须赶在main()函数调用之前出现;

 

 在Mac或Linux的计算机,可以通过手册查看string.h中每个函数的介绍,如man strstr;

 

 为什么不写成track[][];//每个字符串都不一样长的话,为了放下最长的,编译器需要分配足够大的空间;

 如果是track[m][n]的话,track数组一共占了m*n个字符;

 

 计算机总会先运行main()函数,之所以把track_str()函数放在前面,是因为在调用函数前,编译器需要知道函数接收什么参数以及函数的返回类型;

 

 要点:

 -可以用char strings[...][...]来创建数组的数组;

 -第一组方括号用来访问外层数组;

 -第二组方括号用来访问每个内层数组中的元素;

 -有了string.h头文件,就可以使用C标准库中的字符串处理函数;

 -一个C程序可以有多个函数,但是计算机总是先运行main();

 


再看一个示例:反转字符串中的字符,输出并返回;

 (Code3_1)

 

/*
 * reverse string
 */

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

char * reverse(char * s){
    size_t len = strlen(s);//size_t相当于整数,用来保存字符串的长度
    
    char chars[len];
    
    char * t = s + len - 1;
    while (t >= s) {
        printf("%c",*t);
        chars[len - 1 - (t-s)] = *t;
        t = t - 1;
    }
    puts("\n");
    return chars;
}

int main() {
    
    char * chars = "flower";
    char * charreverse = reverse(chars);
    printf("%s\n",charreverse);
    return 0;
}

log:

rewolf


rewolf


 数组的数组 和 指针的数组:

 我们已经见过如何用数组的数组保存多个字符串;还有一种方法是只用指针的数组;

 指针的数组就是保存存储器地址的数组;

 如果想要快速创建字符串字面值列表,指针的数组很有用:

 char * names[] = {"ab","cd","ef"};//一个字符串字面值配一个指针

 我们可以像访问数组的数组那样访问指针的数组;

 (Code3_2)

 

/*
 * 填字游戏举例
 */

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

void reverse(char * s){
    size_t len = strlen(s);//size_t相当于整数,用来保存字符串的长度
    
    char chars[len];//按照字符串长度声明一个字符数组
    
    char * t = s + len - 1;//获取指向字符数组最后一位字符的指针
    while (t >= s) {
        printf("%c",*t);//打印指针存储的字符
        chars[len - 1 - (t-s)] = *t;//将指针指向的字符逐个赋到数组的指定位置(地址)
        t = t - 1;//进行指针的运算
    }
    puts("\n");
}

int main() {
    //横
    puts("横");
    
    char * juices[] = {
        "dragonfruit","waterberry","sharonfruit","uglifruit",
        "rumberry","kiwifruit","mulberry","strawberry",
        "blueberry","blackberry","starfruit"
    };
    
    char * a;
    puts(juices[6]);
    reverse(juices[7]);
    a = juices[2];
    juices[2] = juices[8];
    juices[8] = a;
    puts(juices[8]);
    reverse(juices[(18+7)/5]);
    
    //纵
    puts("纵");
    puts(juices[2]);
    reverse(juices[9]);
    juices[1] = juices[3];
    puts(juices[10]);
    reverse(juices[1]);
    
    return 0;
}

log:

mulberry

yrrebwarts


sharonfruit

tiurfiwik


blueberry

yrrebkcalb


starfruit

tiurfilgu


 这段代码其实在对指针的数组的操作上其实没做什么,和我们之前用的差不多,但在字符串反转的处理上却很有意思(仔细看下注释);

 

 工具箱:

 -string.h头文件包含了有用的字符串处理函数;

 -strstr(a,b)可以返回字符串b在字符串a中的地址;

 -strcpy()可以复制字符串;

 -strcmp()可以比较字符串;

 -strcat()可以连接字符串;

 -字符串数组是数组的数组;

 -可以用char strings[...][...]创建数组的数组;

 -strchr()用来在字符串中查找某个字符的位置;

 -strlen()可以得到字符串的长度;


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值