202111051401字符串.mp4

大纲要求

•【2】字符数组与字符串的关系

•【2】字符数组的综合应用

•【2】string类定义、相关函数引用

•【3】string类的综合应用


字符类型和字符数组

  1. // 字符类型
  2. char c;
  3. int a[N];
  4. double a[N];
  5. bool a[N];
  6. // 字符数组
  7. char s[N];
  8. char s[N][N];
  9. /*
  10. .#...#
  11. ...#..
  12. #.....
  13. */
  14. // 界定符
  15. 'a' 字符 ' '
  16. "a" 字符串 "abc" "" " "
  17. a[0] = '\n';
  18. cout << a[0];
  19. #include <bits/stdc++.h>
  20. using namespace std;
  21. int main()
  22. {
  23. cout << sizeof ('a') << '\n';
  24. cout << sizeof ("a") << '\n';
  25. return 0;
  26. }
  27. /*
  28. 1
  29. 2
  30. */
  31. 字符占一个字节,“a”会增加一个字节,用来存放字符串结束符'\0',所以占2字节
  1. // 字符串读入
  2. #include <cstdio>
  3. using namespace std;
  4. char s[110];
  5. int main()
  6. {
  7. scanf("%s", s);
  8. printf("%s\n", s);
  9. int len = strlen(s);
  10. printf("%d\n", len);
  11. return 0;
  12. }
  1. // string类
  2. #include <iostream>
  3. using namespace std;
  4. int main()
  5. {
  6. string s;
  7. cin >> s;
  8. cout << s << '\n';
  9. return 0;
  10. }
  1. // 读入一整行
  2. // ab cd ed
  3. #include <iostream>
  4. using namespace std;
  5. int main()
  6. {
  7. string s;
  8. getline(cin, s);
  9. cout << s << '\n';
  10. return 0;
  11. }
  1. // string类的字符串处理函数
  2. #include <iostream>
  3. #include <cstring>
  4. using namespace std;
  5. int main()
  6. {
  7. string s;
  8. s = "abcdef";
  9. cout << s << '\n';
  10. cout << s.size() << '\n'; //输出字符串大小
  11. cout << s.substr(2) << '\n'; //截取子串,是位置2开始往后所有
  12. cout << s.substr(2, 3) << '\n'; //从位置2开始,截取三位
  13. return 0;
  14. }
  1. // 字符串长度
  2. char s[110];
  3. scanf("%s", s);
  4. int len = strlen(s); // 字符数组,对应 strlen
  5. string s;
  6. cin >> s;
  7. int len = s.size(); // string,对应 .size()
  1. // string::operator+=
  2. #include <iostream>
  3. #include <string>
  4. int main ()
  5. {
  6. std::string name ("John");
  7. std::string family ("Smith");
  8. name += " K. "; // c-string
  9. name += family; // string
  10. name += '\n'; // character
  11. std::cout << name;
  12. return 0;
  13. }
  1. // string::substr
  2. #include <iostream>
  3. #include <string>
  4. int main ()
  5. {
  6. std::string str="We think in generalities, but we live in details.";
  7. // (quoting Alfred N. Whitehead)
  8. std::string str2 = str.substr (3,5); // "think"
  9. std::size_t pos = str.find("live"); // position of "live" in str
  10. std::string str3 = str.substr (pos); // get from "live" to the end
  11. std::cout << str2 << ' ' << str3 << '\n';
  12. return 0;
  13. }
  1. // insert
  2. #include<iostream>
  3. using namespace std;
  4. int main(){
  5. string s="to be question";
  6. string s2="the ";
  7. s.insert(6, s2); // to be (the )question
  8. cout << s << '\n';
  9. return 0;
  10. }
  1. // c_str(),string 转 char*
  2. #include<iostream>
  3. using namespace std;
  4. int main(){
  5. string s("Please split this sentence into tokens");
  6. char *cstr = new char[s.size()+1];
  7. strcpy (cstr, s.c_str());
  8. printf("%s\n", cstr);
  9. return 0;
  10. }
  1. // 二维字符数组,如何有效读入的问题
  2. #include <iostream>
  3. #include <cstdio>
  4. using namespace std;
  5. const int N = 110;
  6. char s[N][N];
  7. int n, m;
  8. int main()
  9. {
  10. cin >> n >> m;
  11. for (int i = 0; i < n; i++) scanf("%s", s[i]);
  12. for (int i = 0; i < n; i++) printf("%s\n", s[i]);
  13. return 0;
  14. }
  15. /*
  16. .....#
  17. ####.#
  18. ###..#
  19. ##.#..
  20. */
  21. // 如何是下面的写法,是容易被输入数据坑的
  22. for (int i = 0; i < n; i++)
  23. for (int j = 0; j < n; j++)
  24. scanf("%c", &s[i][j]);
  25. // 虽然我们提前知道了每一行每一列有多少个数字,但是这种操作经常被坑
  26. // 原因是,测试数据,可能每一行的后面有一个空格,一个我们看不见的空格
  27. // 你的代码里,会把这个空格当成一个字符读入。然后,自然读入的数据就不准确了
  28. for (int i = 0; i < n; i++)
  29. for (int j = 0; j < n; j++)
  30. cin >> s[i][j];
  31. // 这样倒是可以解决问题,但这并不如上面一层循环更靠谱,尤其当数据量特别大的时候

image.png

【扩展阅读】C++中字符串的结尾标志 \0

  1. \0C++中字符串的结尾标志,存储在字符串的结尾,它虽然不计入串长,但要占一个字节的内存空间。在百度百科中查看\0词条,会有这样一句话:c/c++中规定字符串的结尾标志为'\0'。有人可能认为,在C语言里(C++会不同),'\0'并不是字符型,而是int型。在这里,我们姑且和百度词条作者保持一致,认为\0'\0'是等价的。由于不同处理器的位数不同,'\0'并不一定是8位的00000000。实际上,由于不同处理器的位数不同,sizeof(int)返回的结果也都不同,而sizeof(char)返回结果一般是1,对8位机来说,一个字节由8位组成,16位机一个字节由16位组成,我们通常用的电脑通常是32位的,即一个字节由32位组成,现在已经是64位机了。CPU一般是以字节为单元进行读取的。但是一般情况下大家还是认同1Byte等于8bit的说法,因为这是构成的最小位数。
  2. '\0'是转义字符,意思是告诉程序,这不是数字0'\0'0两者基本上可以通用,例如: string[i] != '\0'string[i] != 0是一样的。不过'\0'的类型是char型,而0int类型,所以在大多数计算机上,sizeof(0) = 4sizeof('\0') = 1,这在特殊情况下不可通用。另外扩展一下,'\0''0'也是不同的,他们都是字符,但是他们的ASCII码是不同的:'\0' ASCII码值为0'0' 也可以写成'\0x30' ASCII码值为48
  3. C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。字符串总是 '\0'作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符 '\0'存入数组,并以此作为该字符串是否结束的标志。有了'\0'标志后, 就不必再用字符数组的长度来判断字符串的长度了。
  4. '\0'就是 字符串结束标志。比如说,把一个字符串赋值给数组:char str1[] = {"Welcome!"}。实际上数组str1在内存中的实际存放情况为: 'W' 'e' 'l' 'c' 'o' 'm' 'e' '!' '\0'。这后面的'\0'是由C编译系统自动加上的。所以在用字符串赋初值时一般无须指定数组的长度, 而由系统自行处理。 把字符数组str1中的字符串拷贝到字符数组str2中。串结束标志'\0'也一同拷贝。
  1. // ASCII码相关的东西,可以百度
  2. // 观察大小写的规律,会相互转化
  3. // 会数字1和字符'1'的转化
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. const int N = 110;
  7. char s[N];
  8. int cnt[30];
  9. int main(){
  10. scanf("%s", s); // they are lowercase
  11. for (int i = 0; s[i]; i++)
  12. cnt[s[i] - 'a']++;
  13. for (int i = 0; i < 26; i++) cout << cnt[i] << ' ';
  14. puts("");
  15. return 0;
  16. }

image.png


sscanf从字符串格式化输入

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 30;
  4. char s[] = "Saturday March 25 1989";
  5. char month[N], weekday[N];
  6. int year, day;
  7. int main(){
  8. sscanf(s, "%s %s %d %d", weekday, month, &day, &year);
  9. printf("%d %s %d\n", day, month, year);
  10. return 0;
  11. }
  1. // CSP-J2021 T3网络连接
  2. // 读入数据用sscanf就相当的妙
  3. // 而我赛场上,只会用字符串,一点一点拆。。 差距显然
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. int a, b, c, d, e;
  7. char s[] = "192.168.1.1:8080";
  8. int main(){
  9. sscanf(s, "%d.%d.%d.%d:%d", &a, &b, &c, &d, &e);
  10. printf("%d.%d.%d.%d:%d\n", a, b, c, d, e);
  11. return 0;
  12. }

例题, 找第一个只出现一次的字符

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e5 + 10;
  4. char s[N];
  5. int vis[30];
  6. int main()
  7. {
  8. scanf("%s", s);
  9. int len = strlen(s);
  10. for (int i = 0; i < len; i++)
  11. vis[s[i] - 'a']++;
  12. bool flag = false;
  13. for (int i = 0; i < len; i++)
  14. {
  15. if (vis[s[i] - 'a'] == 1)
  16. {
  17. flag = true;
  18. printf("%c\n", s[i]);
  19. break;
  20. }
  21. }
  22. if (!flag) printf("no\n");
  23. return 0;
  24. }

例题,验证子串

  1. //string s;
  2. //s.find()
  3. #include <bits/stdc++.h>
  4. using namespace std;
  5. int main()
  6. {
  7. string s1, s2;
  8. cin >> s1 >> s2;
  9. int ans = 0;
  10. if (s1.find(s2) != -1) ans = 1;
  11. else if (s2.find(s1) != -1) ans = -1;
  12. if (ans == 1) cout << s2 << " is substring of " << s1 << endl;
  13. else if (ans == -1) cout << s1 << " is substring of " << s2 << endl;
  14. else cout << "No substring" << endl;
  15. return 0;
  16. }

例题,删除单词后缀

  1. // string s;
  2. // s.erase(len - 2)
  3. // 使用char s[40];
  4. // 匹配后缀成功后,利用s[len-2] = '\0'进行截断

例题,单词的长度

  1. // 第一种方法,直接手搓
  2. // getline(cin, s);
  3. // 然后遍历,碰到空格算一个单词
  4. // 要注意输出的格式
  5. #include <bits/stdc++.h>
  6. using namespace std;
  7. int main()
  8. {
  9. string s;
  10. getline(cin, s);
  11. bool flag = false;
  12. for (int i = 0, len = s.size(); i < len; i++)
  13. {
  14. while (s[i] == ' ') i++;
  15. if (flag) printf(",");
  16. flag = true;
  17. int j = i;
  18. int cnt = 0;
  19. while (j < len && s[j] != ' ') j++;
  20. printf("%d", j - i);
  21. i = j - 1;
  22. }
  23. puts("");
  24. return 0;
  25. }
  26. // 第二种方法
  27. // 使用while (cin >> s),更好编写
  28. // 不过要注意一下格式控制的小技巧
  29. // 本地调试要使用freopen
  30. #include <bits/stdc++.h>
  31. using namespace std;
  32. int main()
  33. {
  34. //freopen("1.in", "r", stdin);
  35. string s;
  36. bool flag = false;
  37. while (cin >> s){
  38. if (flag) cout << ',';
  39. flag = true;
  40. int len = s.size();
  41. cout << len;
  42. }
  43. puts("");
  44. return 0;
  45. }

例题,单词翻转

  1. //getline(cin, s);
  2. //比较麻烦,构造一个一个单词出来,然后翻转
  3. //使用while (cin >> s)
  4. //会两个点,格式错误,调不出来