跳转至

第五讲 字符串

字符串是计算机与人类沟通的重要手段。——闫学灿

已经在数学上证明了程序可以实现任意的逻辑

1. 字符与整数的联系——ASCII码

每个常用字符都对应一个-128 ~ 127的数字,二者之间可以相互转化。注意:目前负数没有与之对应的字符。

#include <iostream>

using namespace std;

int main()
{
    char c = 'a';
    cout << (int)c << endl;

    int a = 66;
    cout << (char)97 << endl;

    return 0;
}

(重要)常用ASCII值:'A'- 'Z'是65 ~ 90,'a' - 'z'是97 - 122,0 - 9是 48 - 57。 字符可以参与运算,运算时会将其当做整数:

#include <iostream>

using namespace std;

int main()
{
    int a = 'B' - 'A';
    int b = 'A' * 'B';
    char c = 'A' + 2;

    cout << a << endl;
    cout << b << endl;
    cout << c << endl;

    return 0;
}

2. 字符数组

字符串就是字符数组加上结束符\0

可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个'\0'字符,因此字符数组的长度至少要比字符串的长度多 1!

#include <iostream>

using namespace std;

int main()
{
    char a1[] = {'C', '+', '+'};            // 列表初始化,没有空字符
    char a2[] = {'C', '+', '+', '\0'};      // 列表初始化,含有显示的空字符
    char a3[] = "C++";                      // 自动添加表示字符串结尾的空字符
    char a4[6] = "Daniel";                  // 错误:没有空间可以存放空字符
    return 0;
}
2.1 字符数组的输入输出:
#include <iostream>

using namespace std;

int main()
{
    char str[100];

    cin >> str;             // 输入字符串时,遇到空格或者回车就会停止
    cout << str << endl;    // 输出字符串时,遇到空格或者回车不会停止,遇到'\0'时停止
    printf("%s\n", str); // 注意字符串本身就是地址,不需要加&

    return 0;
}

读入一行字符串,包括空格:

#include <iostream>

using namespace std;

int main()
{
    char str[100];

    fgets(str, 100, stdin);  // gets函数在新版C++中被移除了,因为不安全。
                             // 可以用fgets代替,但注意fgets不会删除行末的回车字符
  // 100表示输入最多多少个字符

    cout << str << endl;

    return 0;
}

string 的话要用get line(cin, s)]

Char s[100]的话要用cin.getline(s, 100)

Puts(s) 等价于print("%s\n", s), 注意包括了换行符

2.2 字符数组的常用操作

下面几个函数需要引入头文件:

#include <string.h>

或者#include <cstring>

(1) strlen(str),求字符串的长度
(2) strcmp(a, b),比较两个字符串的大小,a < b返回-1,a == b返回0,a > b返回1。这里的比较方式是字典序!
(3) strcpy(a, b),将字符串b复制给从a开始的字符数组。注意顺序!

字典序,从头开始,比ASCII码,比如abc < abde 10 < 2(一位一位的比较)strcmp返回的是-1

字典序本身具有贪心的性质,所以多和贪心相关

strlen是不包含\0的

回车一般是刷新缓存,所以按了回车之后,会将缓存中的东西传递给程序

#include <iostream>
#include <string.h>

using namespace std;

int main()
{
    char a[100] = "hello world!", b[100];

    cout << strlen(a) << endl; // 12

    strcpy(b, a);

    cout << strcmp(a, b) << endl; //0

    return 0;
}
2.3遍历字符数组中的字符
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char s1[100], s2[100];
    scanf("%s", s1);
    for (int i = 0, len = strlen(s1); i < len; i++) cout << s1[i] << endl;

    return 0;
}

3. 标准库类型string

可变长的字符序列,比字符数组更加好用,需要引入头文件: #include <string>

3.1 定义和初始化
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s1;              // 默认初始化,s1是一个空字符串
    string s2 = s1;         // s2是s1的副本,注意s2只是与s1的值相同,并不指向同一段地址
    string s3 = "hiya";     // s3是该字符串字面值的副本
    string s4(10, 'c');     // s4的内容是 "cccccccccc"

    return 0;
}
3.2 string上的操作

(1) string的读写

#include <iostream>
#include <string.h>
using namespace std;

int main () {
    string a;
    cin >> a;
//    cout << a << endl;
    printf("%s", a.c_str());
    return 0;
}

注意:不能用scanf("%s", &s)来读入string

注意:不能用printf直接输出string,需要写成:printf(“%s”, s.c_str());

(2) 使用getline读取一整行

#include <iostream>
#include <string>

using namespace std;

int main () {
    string a;
    getline(cin, a);
    cout << a << endl;
    return 0;
}
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

int main()
{
    char s[100]; // 能一直读入,包括空格
    cin.getline(s, 100); // 读入字符组成的字符串

    // 引入string
    string s1, s2;
    cin >> s1; // 只能读入第一个字符串,遇到空格和回车会直接停止
    getline (cin, s2); // 前面写cin,后面写读入的字符串,会直接开始读入,哪怕第一个字符是空格

    cout << s << endl << s1 << endl << s2 << endl;
    return 0;
}

(3) string的的empty和size操作(注意size是无符号整数,因此 s.size() <= -1一定成立)?????

empty告诉我们字符串是不是空的,空的就是true(0),否则是false(1)

string的size功能的时间复杂度是O1,比strlen好多了!

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s1, s2 = "abc";

    cout << s1.empty() << endl; // 1
    cout << s2.empty() << endl; // 0
    cout << s1.size() << endl; // 0
    cout << s2.size() << endl; // 3

    return 0;
}

(4) string的比较

支持 >, <, >=, <=, ==, !=等所有比较操作,按字典序进行比较。

(5) 为string对象赋值

string s1(10, 'c'), s2;     // s1的内容是 cccccccccc;s2是一个空字符串
s1 = s2;                    // 赋值:用s2的副本替换s1的副本
                            // 此时s1和s2都是空字符串

(6) 两个string对象相加

string s1 = "hello,  "", s2 = "world\n";
string s3 = s1 + s2;                    // s3的内容是 hello, world\n
s1 += s2;                               // s1 = s1 + s2

(7) 字面值和string对象相加:

做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是将这些字面值串联起来:

string s1 = "hello", s2 = "world";      // 在s1和s2中都没有标点符号
string s3 = s1 + ", " + s2 + '\n';

当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string:

普通字符串和string不是一个东西!

string s4 = s1 + ", ";  // 正确:把一个string对象和有一个字面值相加
string s5 = "hello" + ", "; // 错误:两个运算对象都不是string

string s6 = s1 + ", " + "world";  // 正确,每个加法运算都有一个运算符是string
string s7 = "hello" + ", " + s2;  // 错误:不能把字面值直接相加,运算是从左到右进行的
3.3 处理string对象中的字符

可以将string对象当成字符数组来处理:

#include <iostream>
#include <string>;
using namespace std;

int main () {
    string s = "hello, world!";
    for (int i = s.size() - 1; i >= 0; i--) cout << s[i];
    return 0;
}

或者使用基于范围的for 语句:

这个叫C++的范围遍历,for (char c : s),前面是字符数组里每个字符的类型

注意得到的东西是,string里面复制过来的值,所以不能用:

for (char c : s) {
  c = 'a';
}

而要用

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "Hello, world!";
    for (char c: s) cout << c;
    cout << endl;
    for (char &c: s) c = 'a';
    cout << s;
    return 0;
}

cpp中还提供auto自动识别类型

比如:

for (auto &c : c) {
    c = 'a'
}

后面复杂起来之后用auto会非常简单

注意:fgets会把末尾的回车读进来

删掉最后一个字符:

s.pop_back();

取字符串:

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main () {
    string a, b;
    while (cin >> a >> b) {
        int p = 0;
        for (int i = 0; i < a.size(); i++) {
            if (a[i] > a[p]) {
                p = i;
            }
        }
        cout << a.substr(0, p + 1) + b +  a.substr(p + 1) << endl;
    }

    return 0;
}
760. 字符串长度
#include <iostream>
#include <string>
using namespace std;

int main () {
    string s;
    getline(cin, s); // 注意getline输入一整行
    printf("%d", s.size());
    return 0;
}
772. 只出现一次的字符

char在运算中会被自动转化为数字

#include <iostream>
#include <cstring>
using namespace std;
char str[100010];
int cnt[26];

int main () {
    scanf("%s", str);

    for (int i = 0; str[i]; i++) {
        cnt[str[i] - 'a']++;
    }
    for (int i = 0; str[i]; i++) {
        if (cnt[str[i] - 'a'] == 1) {
            cout << str[i] << endl;
            return 0;
        }

    }
    puts("no");

    return 0;
}

听到1.5字符串匹配1:47:32