C语言进阶 10. 字符串
文章目录
- C语言进阶 10. 字符串
- 10.1. 字符串
- 10.2. 字符串变量
- 10.3. 字符串输入输出
- 10.4. 字符串数组
- 10.5. 单字符输入输出
- 10.6. 字符串函数strlen()
- 10.7. 字符串函数strc()
- 10.8. 字符串函数strcpy()
- 10.9. 字符串搜索函数
- 10.10. PAT
- 10-0. 说反话 (20)
- 10-1. 在字符串中查找指定字符(15)
- 10-2. 删除字符串中的子串(20)
- 10-3. 字符串逆序(15)
- 10-4. 字符串循环左移(20)
10.1. 字符串
-
字符数组:
- char word[] = { ‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’ };
- 这不是C语言的字符串, 因为不能用字符串的方式做计算
-
字符串:
-
char word[] = { ‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’, ‘\0’};
-
区别在于这个数组的初始化的过程中, 在字符数组的最后加入了一个特殊的东西’\0’, 它使得字符数组可以使用字符串的运算方式去进行运算
-
字符串:
- 以0结尾的一串字符, 0或’\0’是一样的, 但是和’0’不同
- 0标志字符串的结束, 但是他不是字符串的一部分, 计算字符串长度的时候不包含这个0
- 字符串以数组的形式存在, 以数组或者指针的形式访问, 更多的是以指针的形式
- string.h里面有很多处理字符串的函数
-
-
字符串变量:
- char* str = “Hello”; //指针str指向了字符数组"Hello"
- char c[] = “Hello”; //数组c中存放了"Hello"
- char line[10] = “Hello”;//数组line共10字节的大小, "Hello"占据了6字节的空间
-
字符串常量:
- “Hello”
- "Hello"会被编译器编程一个字符数组放在某处, 这个数组的长度是6, 结尾还有表示结束的0, 它也占据空间
- 两个相邻的字符串常量会被自动连接起来
printf("hello" " world");
-
字符串:
- C语言的字符串是以数组的形式存在的
- 不能用运算符对字符串做运算
- 通过数组的方式可以遍历字符串
- 唯一特殊的地方是字符串字面量可以用来初始化字符数组
- 以及标准库提供了一系列的字符串函数
- C语言的字符串是以数组的形式存在的
#include <stdio.h>int main(void) {//char word[] = { 'H', 'e', 'l', 'l', 'o','!' };char word[] = { 'H', 'e', 'l', 'l', 'o','!', '\0'};for (int i = 0; i < sizeof(word) / sizeof(word[0]); i++) {printf("word[%d] = %c\n", i, word[i]);}char* str = "Hello"; //指针str指向了字符数组"Hello"char c[] = "Hello"; //数组c中存放了"Hello"char line[10] = "Hello";//数组line共10字节的大小, "Hello"占据了6字节的空间char c1[] = "stephen""curry";char c2[] = "curry";for (int i = 0; i < sizeof(c1) / sizeof(c1[0]); i++) {printf("%c", c1[i]);}printf("\n");printf("hello"" world");return 0;
}
10.2. 字符串变量
-
字符串常量:
- char* s = “Hello world!”;
- 指针s要指向某个地方的字符串
- s是一个指针, 初始化为指向一个字符串常量
-
由于这个常量所在的地方, 所以实际上s是const char* s, 但是由于历史的原因, 编译器接受不带const的写法
-
程序当中有两处相同的字符串常量, 而它们又分别赋值给了两个不同的指针, 那么这两个指针都是会指向一个相同的地方的
char* s = "Hello world!";char* s1 = "Hello world!";printf("s = %p\n", s);//s = 00007FF7F2699C28printf("s1 = %p\n", s1);//s1 = 00007FF7F2699C28"Hello world!"位于程序的代码段, 它是只读的, 试图对s所指的字符串做写入会导致严重的后果s[0] = 'B';printf("s = %p\n", s);//s = 00007FF7F2699C28
-
char s2[] = “Hello world!”;
它指的是"Hello world!"这个字符串就在s2这里
-
- char* s = “Hello world!”;
-
选择指针还是数组?
-
char s2[] = “Hello world!”;
-
char* s = “Hello world!”;
-
数组: 这个字符串在这里
- 作为本地变量空间自动被回收
-
字符串: 这个字符串不知道在哪里
- 处理函数参数
- 动态分配空间(malloc)
-
如果要构造一个字符串 -> 数组
-
如果要处理一个字符串 -> 指针
-
-
char* 是字符串?
- 字符串可以表达为char的形式, 但是char不一定是字符串, 它本意是指向字符串的指针, 可能指向的是字符的数组(就像int*一样), 只有它所指的字符数组有结尾的0, 才能说它所指的是字符串
#include <stdio.h>int main(void) {int i, j;char* s = "Hello world!";char* s1 = "Hello world!";printf("&i = %p\n", &i);//&i = 0000003AB915F934printf("&j = %p\n", &j);//&j = 0000003AB915F954printf("s = %p\n", s);//s = 00007FF7F2699C28printf("s1 = %p\n", s1);//s1 = 00007FF7F2699C28char s2[] = "Hello world!";printf("s2 = %p\n", s2);s2[0] = 'B';printf("Here!s2[0] = %c\n", s2[0]);return 0;
}
10.3. 字符串输入输出
-
字符串赋值:
char* t = "title";char* s;s = t;
- 并没有产生新的字符串, 只是让指针s指向了t所指的字符串, 对s的任何操作就是对t做的
-
字符串输入输出:
-
%s: 字符串格式
-
char string[8];
-
scanf(“%s”, string);
-
printf(“string = %s\n”, string);
-
scanf中的%s读入一个单词(到空格, tab或者回车为止)
-
这里的scanf是不安全的, 因为不知道要读入的内容的长度
-
-
安全的输入:
-
char string[8];
-
scanf(“%7s”, string);
-
在%和s之间的数字表示最多允许读入的字符的数量, 这个数字应该比数组的大小小1
- 下一次scanf从哪里开始?
- 交给下一个%s
- 下一次scanf从哪里开始?
-
-
常见的错误:
- 误以为char*是字符串类型, 定义了一个字符串类型的变量string就可以直接使用了
- 由于没有对string初始化为0, 所以不一定每次运行都出错
- 误以为char*是字符串类型, 定义了一个字符串类型的变量string就可以直接使用了
-
空字符串:
- char buffer[100] = “”;
- 这是一个空字符串, buffer[0] == ‘\0’
- char buffer[100] = “”;
#include <stdio.h>int main(void) {char* t = "title";char* s;s = t;char string[8];scanf("%7s", string);printf("string = %s\n", string);return 0;
}
10.4. 字符串数组
-
字符串数组:
-
char** a;
- a是一个指针, 指向另一个指针, 那个指针指向一个字符(串)
-
char a[][10];
-
char* a[];
-
-
程序参数:
- int main(int argc, char const* argv[])
- 其实main()函数的参数表里面是有东西的, 前面有一个整数, 后面有一个字符串数组
- 整数是告诉我们后面的数组到底有多少个字符串
- argc[0]是命令本身
- 当使用Linux的符号链接时, 反应符号链接的名字, 当然, 没学过Linux系统的肯定很难理解
#include <stdio.h>int main(void) {//a[0] -> char [10]/*char a[][10] = {"Hello","asdfsgdhjll",};*///a[0] -> char*/*char* a[] = {"Hello","asdfsgdhjll",};*///月份转换, 使用数组代替switch case语句//分析: 数组的每一个元素代表月份, 每一个下标代表月份数字char* arr[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December",};int month;printf("请输入月份: ");scanf("%d", &month);printf("月份为%s\n", arr[month - 1]);return 0;
}
#include <stdio.h>int main(int argc, char const* argv[]) {for (int i = 0; i < argc; i++) {printf("%d : %s\n", i, argv[i]);}return 0;
}
10.5. 单字符输入输出
-
putchar:
- 是int而不是char, 但是它的int只能接收一个字符
- int putchar(int c);
- 向标准输出写一个字符
- 返回写了几个字符, 宏EOF(end of file文件结束)(-1)表示写失败
-
getchar:
- int getchar(void);
- 从标准输入读入一个字符
- 返回类型是int是为了返回EOF(-1)
- Windows -> ctrl+z
- Unix -> ctrl+d
-
shell:
-
getchar只读一个字符, 但是为什么输入了那么多的字符, 都没有反应, 直到我们敲下回车才会有反应?
- shell负责运行程序, 在键盘上敲出来的东西, shell会帮你进行翻译. 在敲下123之后, 123会停留在shell当中, 直到敲下回车, shell才会将他们放入缓冲区, 如果程序使用getchar, 就会一个一个字符去读, 直到遇到EOF(也就是在shell当中敲下ctrl+z, shell会把它翻译成EOF), 程序才会结束, 而ctrl+c的作用是把程序直接关闭, 也就不会有返回结果了.
-
#include <stdio.h>int main(void) {int c;while ((c = getchar()) != EOF) {putchar(c);}printf("EOF\n");return 0;
}
10.6. 字符串函数strlen()
-
string.h:
- 对于字符串, C语言提供了很多函数来帮我处理它们, 这是在C的标准库当中的函数, 这些函数的原型在一个叫做string.h的头文件当中, 所以#include <string.h>
- 其中包括strlen, strcmp, strcpy, strcat, strchr, strstr, 它们都是以str开头的
-
strlen:
- StringLength
- size_t strlen(const char* s);
- 返回s的字符串长度(不包括结尾的0)
#include <stdio.h>
#include <string.h>//mylen==strlen, mylen是自己写的函数来实现strlen的功能的
int mylen(const char* s);int main(int argc, char const* argv[]) {char line[] = "Hello";printf("strlen = %d\n", strlen(line));//5printf("sizeof = %d\n", sizeof(line));//6printf("mylen = %d\n", mylen(line));//5return 0;
}int mylen(const char* s) {int count = 0;while (s[count] != '\0') {count++;}return count;
}
10.7. 字符串函数strc()
- strcmp:
-
StringCompare
-
int strcmp(const char* s1, const char* s2);
-
比较两个字符串, 返回:
- 0:s1==s2
- 1:s1>s2
- -1:s1<s2
-
不同的编译器, 会有不同的功能, VS上面只能返回这三种值, 而在翁恺老师的课当中的编译器则是另一个功能, 它会将两个字符串遍历, 相同的字符接着遍历, 直到出现不同的, 然后返回两个字符的ASCII码的差值
-
#include <stdio.h>
#include <string.h>指针实现翁恺老师代码
int mycmp(const char* s1, const char* s2) {while (*s1 == *s2 && *s1 != '\0') {*s1++;*s2++;}return *s1 - *s2;
}数组遍历实现翁恺老师代码
int mycmp(const char* s1, const char* s2) {int index = 0;while (s1[index] == s2[index] && s1[index] != '\0') {index++;}return s1[index] - s2[index];
}//VS编译器上面strcmp的功能实现
int mycmp(const char* s1, const char* s2) {int index = 0;int ret = 1;while (s1[index] == s2[index] && s1 != '\0') {index++;ret = 0;}if (s1[index] < s2[index]) {ret = -1;}return ret;
}int main(void) {char s1[] = "abc";char s2[] = " bc";printf("%d\n", strcmp(s1, s2));printf("%d\n", mycmp(s1, s2));return 0;
}
10.8. 字符串函数strcpy()
-
strcpy:
- StringCopy
- char* strcpy(char* restrict dst, const char* restrict src);
- 把src的字符串拷贝到dst
- restrict表明src和dst不重叠(C99)
- 当计算机是多核时, 计算机会把这个字符串分成好几段, 分配给这些核, 这时就需要两个字符串之间不能重叠, 如果重叠则会拷贝冲突
- restrict表明src和dst不重叠(C99)
- 返回dst
- 为了能链起代码来
-
复制一个字符串:
-
char* s2 = (char*)malloc(strlen(s1) + 1);
-
strcpy(s2, s1);
-
从函数的参数得到了一个字符串, 但是实际上我们得到的是一个指针, 它指向外面的某个地方的一个字符串, 那个字符串不是我这里的, 我不能保证那个字符串始终存在, 所以我需要把它复制到这里. 于是就要使用malloc去动态的分配一块空间(因为在程序静态的时候, 不知道这块内存有多大), 然后使用strlen来得到那个字符串的长度, 并加上字符串后面的’\0’也就是+1
-
#include <stdio.h>
#include <string.h>
#include <stdlib.h>指针版本mycpy
char* mycpy(char* dst, const char* src) {char* ret = dst;while (*dst++ = *src++);*dst = '\0';return ret;
}//数组版本mycpy
char* mycpy(char* dst, const char* src) {int index = 0;while (src[index]) {dst[index] = src[index];index++;}dst[index] = '\0';return dst;
}int main(int argc, char const* argv[]) {char s1[] = "curry";char* s2 = (char*)malloc(strlen(s1) + 1);strcpy(s2, s1);printf("%s\n", s2);printf("%s\n", s1);free(s2);//mycpy(s2, s1);//printf("%s\n", s2);//printf("%s\n", s1);return 0;
}
10.9. 字符串搜索函数
- 字符串中找字符:
-
char* strchr(const char* s, int c);//从左向右找
-
char* strrchr(const char* s, int c);//从右向左找
-
不管从左还是从右, 找到之后会返回该字符以及该字符右边的字符, 把它们当成一个字符串返回
-
返回NULL表示没有找到
-
如何寻找字符串中相同字符的第二个字符?
char s[] = "hello"; char* p = strchr(s, 'l');p = strchr(p + 1, 'l');printf("%s\n", p);
-
找到’l’之后将返回结果复制到另一个字符串
char* t = (char*)malloc(strlen(p) + 1);strcpy(t, p);printf("%s\n", t);
-
如果找到一个字符, 想要返回它左边的那些字符, 把它们当作一个字符串返回
char c = *p;*p = '\0';char* t = (char*)malloc(strlen(s) + 1);strcpy(t, s);printf("%s\n", t);
-
- 字符串中找字符串:
- char* strstr(const char* s1, const char* s2);
- char* strcasestr(const char* s1, const char* s2);//忽略大小写进行查找
#include <stdio.h>
#include <string.h>
#include <stdlib.h>如何寻找字符串中相同字符的第二个字符?
int main(int argc, char const* argv[]) {char s[] = "hello";char* p = strchr(s, 'l');p = strchr(p + 1, 'l');printf("%s\n", p);return 0;
}找到'l'之后将返回结果复制到另一个字符串
int main(void) {char s[] = "hello";char* p = strchr(s, 'l');char* t = (char*)malloc(strlen(p) + 1);strcpy(t, p); printf("%s\n", t);free(t);return 0;
}//如果找到一个字符, 想要返回它左边的那些字符, 把它们当作一个字符串返回
int main(void) {char s[] = "hello";char* p = strchr(s, 'l');char c = *p;*p = '\0';char* t = (char*)malloc(strlen(s) + 1);strcpy(t, s);printf("%s\n", t);free(t);//实现功能后再将s复原*p = c;printf("%s\n", s);printf("%s\n", p);return 0;
}字符串中找字符串
int main(void) {char* s1 = "hello";char* s2 = strstr(s1, "ll");printf("%s\n", s2);return 0;
}
10.10. PAT
-
10-0. 说反话 (20)
-
给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。
-
输入格式:
测试输入包含一个测试用例,在一行内给出总长度不超过80的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有区分)组成的字符串,单词之间用1个空格分开,输入保证句子末尾没有多余的空格。 -
输出格式:
每个测试用例的输出占一行,输出倒序后的句子。 -
输入样例:
Hello World Here I Come -
输出样例:
Come I Here World Hello -
分析:
- 1.输入
- 2.取出最后一个单词
- 3.处理最后一个单词开头的空格
- 4.缩小字符串
- 5.循环上面的动作
- 6.如果找不到空格了, 则把原字符串中剩余的最后单词打印
- 7.可以找到就接着把取出来的单词打印并缩小字符串
-
#include <stdio.h>
#include <string.h>int main(void) {//输入char s1[] = "Hello World Here I Come";gets(s1);//取出最后一个单词char* s2 = strrchr(s1, ' ');//处理最后一个单词开头的空格for (int i = 1; i < strlen(s2); i++) {printf("%c", s2[i]);}//缩小字符串*s2 = '\0';//循环上面的动作while (s2 != NULL) {s2 = strrchr(s1, ' ');//如果找不到空格了, 则把原字符串中剩余的最后单词打印if (s2 == NULL) {printf(" %s", s1);}//可以找到就接着把取出来的单词打印并缩小字符串else {printf("%s", s2);*s2 = '\0';}}return 0;
}
-
10-1. 在字符串中查找指定字符(15)
-
输入一个字符串S,再输入一个字符c,要求在字符串S中查找字符c。如果找不到则输出“Not found”;若找到则输出字符串S中从c开始的所有字符。
-
输入格式:
输入在第1行中给出一个不超过80个字符长度的、以回车结束的非空字符串;在第2行中给出一个字符。 -
输出格式:
在一行中按照题目要求输出结果。 -
输入样例1:
It is a black box
b -
输出样例1:
black box -
输入样例2:
It is a black box
B -
输出样例2:
Not found -
分析:
- 1.输入
- 2.查找strchr
- 3.输出
- 4.判断, 如果为Null, 则输出not found
-
#include <stdio.h>
#include <string.h>int main(void) {//输入char s1[] = "It is a black box";gets(s1);char c = 'b';scanf("%c", &c);//查找char* s2 = strchr(s1, c);//判断if (s2 != NULL) {//输出printf("%s", s2);}else {printf("Not found");}return 0;
}
-
10-2. 删除字符串中的子串(20)
-
输入2个字符串S1和S2,要求删除字符串S1中出现的所有子串S2,即结果字符串中不能包含S2。
-
输入格式:
输入在2行中分别给出不超过80个字符长度的、以回车结束的2个非空字符串,对应S1和S2。 -
输出格式:
在一行中输出删除字符串S1中出现的所有子串S2后的结果字符串。 -
输入样例:
Tomcat is a male ccatat
cat -
输出样例:
Tom is a male -
分析:
- 1.输入
- 2.strstr查找子串
- 3.找到后, 将空字符串填入
- 4.循环查找, 判断条件为当查询结果不等于Null时
- 5.输出
-
-
看着答案写的
#include <stdio.h>
#include <string.h>
#include <stdlib.h>char s1[81], s2[81];int main(void) {/*char s1[] = "Tomcat is a male ccatat";char s2[] = "cat";*/gets(s1);gets(s2);int i;char* p = strstr(s1, s2);while (1) {char* p = strstr(s1, s2);if (p) {for (i = 0; i < strlen(p) - strlen(s2); i++) {p[i] = p[strlen(s2) + i];}p[i] = '\0';}else {printf("%s", s1);break;}}return 0;
}
-
10-3. 字符串逆序(15)
-
输入一个字符串,对该字符串进行逆序,输出逆序后的字符串。
-
输入格式:
输入在一行中给出一个不超过80个字符长度的、以回车结束的非空字符串。 -
输出格式:
在一行中输出逆序后的字符串。 -
输入样例:
Hello World! -
输出样例:
!dlroW olleH -
分析:
- 1.输入s1
- 2.字符串下标index初始化为strlen(s1)
- 3.遍历循环字符串
- 4.s1[index] > 0, index–
- 5.输出
-
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(void) {//输入char s1[] = "Hello World!";gets(s1);//下标初始化int index = (int)strlen(s1);//遍历字符串, 下标递减for (; index >= 0; index--) {//输出printf("%c", s1[index]);}return 0;
}
-
10-4. 字符串循环左移(20)
-
输入一个字符串和一个非负整数N,要求将字符串循环左移N次。
-
输入格式:
输入在第1行中给出一个不超过100个字符长度的、以回车结束的非空字符串;第2行给出非负整数N。 -
输出格式:
在一行中输出循环左移N次后的字符串。 -
输入样例:
Hello World!
2 -
输出样例:
llo World!He -
分析:
- 1.输入s1
- 2.循环n次
- 3.下标i初始化为0, 判断条件i < n, i++
- 4.char* s2 = s1[i]
- 5.s1[i] = “”
- 6.输出
-
-
看答案写的
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(void) {char s1[103];int i, k, n;gets(s1);k = strlen(s1);scanf("%d", &n);if (n % k == 0) {printf("%s", s1);}else {for (i = n % k; i < k; i++) {printf("%c", s1[i]);}for (i = 0; i < n % k; i++) {printf("%c", s1[i]);}}return 0;
}