Skip to content

1 字符与整数之间的关系

在 C++中单个字符是使用 ASCII 进行编码的,因此 C++中的单字符又称为 ASCII 字符或 ASCII 码。每个常用字符都对应着 0 ~ 127 之间的一个整数,二者之间是可以相互转换的。例如 'a' 对应的数字是 97,'A' 对应的数字是 65

cpp
#include <bits/stdc++.h>

using namespace std;

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

    return 0;
}

常用的 ASCII 值:

A ~ Z65 ~ 90a ~ z97 ~ 1220 ~ 948 ~ 57

提示:字符可以参与运算,运算时字符按对应的整数处理。

2 字符数组

由单个字符组成的数组称为字符数组。如果字符数组的最后一个元素是 '\0' 那么这个字符数组就是字符串。

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

cpp
char s[10], ss[10][100];
char s1[] = {'z', 'z', 's', 'z'};
char s2[] = {'z', 'z', 's', 'z', '\0'};
char s3[] = "zzsz";
char s4[4] = "zzsz"; // 错误

2.1 字符数组的输入输出

  • 读入时:cinscanf 以空格或是回车作为字符串的结尾
  • 输出时:coutprintf'\0' 作为字符串的结尾
cpp
#include <cstdio>

using namespace std;

int main() {
    char s[1000];
    // 输入 let's go
    scanf("%s", s);
    printf("%s\n", s); // 输出:let's

    return 0;
}

读入时可以指定字符串存储的起始地址:scanf("%s", s + 1);。需要注意的时,这时输出不能这样写 printf("%s\n", s); (什么也不输出,因为 s[0] = '\0')而应该这样写 printf("%s\n", s + 1);

如何读入一行带空格或者是回车换行的字符串呢?

  1. fgets

    cpp
    #include <cstdio>
    
    using namespace std;
    
    int main() {
        // 输入 let's go
        char s[1000];
    
        // gets函数在新版C++中被移除了,因为不安全。
        // 可以用fgets代替,但注意fgets不会删除行末的回车字符
        fgets(s, 1000, stdin);
    
        printf("%s\n", s); // 输出:let's go 加两个回车
    
        return 0;
    }
  2. getchar

    cpp
    #include <cstdio>
    
    using namespace std;
    
    int main() {
        // 输入 let's go
        char s[1000];
    
        int n = 0;
        while ((s[n++] = getchar()) != '\n'); // 读到回车换行结束
        s[n] = '\0'; // 将换行符转化为字符串结尾字符
    
        printf("%s\n", s);
    
        return 0;
    }

2.2 字符数组的常用操作

以下函数需要引入头文件:

cpp
#include <cstring>
  1. strlen(str):求字符串的长度
  2. strcmp(a, b):按字典序比较两个字符串的大小,a > b 返回 1, a < b 返回 -1a == b 返回 0
  3. strcpy(a, b):将字符串 b 复制给从 a 的首地址开始的字符数组。
  4. strncpy(a, b, n):将字符串 b 的前 n 个字符复制给从 a 的首地址开始的字符数组。
  5. strcat(a, b):将字符串 b 拼接到 a 的末尾。
  6. strstr(a, b):判断 b 是否是 a 的子串,如果是,返回 ba 中首次出现 的地址,否则,返回 NULL(空地址)

2.3 遍历字符数组

cpp
#include <cstdio>
#include <cstring>

using namespace std;

int main() {
    char s[100] = "hello world!";

    int n = strlen(s);
    for (int i = 0; i < n; i++) {
        printf("%c\n", s[i]);
    }

    return 0;
}

3 标准库类型:string

标准库提供了一种可变长的字符序列 string,比字符数组更加好用。需要引入头文件:

cpp
#include <string>

3.1 定义和初始化

cpp
#include <iostream>
#include <string>

using namespace std;

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

    return 0;
}

3.2 string的输入和输出

  1. string 的输入和输出使用的是 cincout,不能使用 scanfprintf

  2. getline 可以读入一行

    cpp
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main() {
    	// 输入 let's go
    	string s;
    
    	getline(cin, s);
    
    	cout << s << endl;
    
    	return 0;
    }

3.3 string的常用操作

两个常用函数:

  1. empty:判断 string 是否为空,为空返回 true
  2. size:返回 string 的长度。
  3. length:返回 string 的长度。

string 按字典序比较大小:

  1. 直接使用 ><>=<=!===

string 加法:

cpp
string a = "hello", b = "world\n";
string c = a + b;
c += a;

string d = c + '\n';
string s = c + "hello" + "world\n"; // 正确
string str = "hello" + "world\n" + c; // 错误

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

3.4 将string当成字符数组

cpp
#include <iostream>
#include <string>

using namespace std;

int main() {
    string s = "hello world";

    for (int i = 0; i < s.size(); i ++ )
        cout << s[i] << endl;

    // 增强 for 循环
    for (char c : s) cout << c << endl;

    // 修改 s 的每一个字符
    for (char &c : s) c = 'c';
    cout << s << endl;

    return 0;
}

3.5 string数组

cpp
string strs[10];

strs[0] = "you";
strs[1] = "are";
strs[2] = "hero";

4 习题讲解

4.1 YBT 1129:统计数字字符个数

  • 考察点:基础知识,ASCII 值
cpp
#include <bits/stdc++.h>

using namespace std;

const int N = 260;
char s[N];

int main() {

	int n =0;
	while ((s[n++] = getchar()) != '\n');
	s[--n] = '\0';

	int cnt = 0;
	for (int i = 0; i < n; i++) {
		if (s[i] >= '0' && s[i] <= '9') cnt++;
	}

	cout << cnt << endl;

	return 0;
}

4.2 YBT 1146:判断字符串是否为回文

  • 考察点:字符串遍历
cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
	char s[1010];
	cin >> s;

	int len = strlen(s);

	bool flag = true;
	for (int i = 0, j = len - 1; i < len; i++, j--) {
		if (s[i] != s[j]) {
			flag = false;
			break;
		}
	}

	if (flag) cout << "yes" << endl;
	else cout << "no" << endl;

	return 0;
}

4.3 YBT 2046:替换字母

  • 考察点:带空格字符串的读入
cpp
#include <bits/stdc++.h>

using namespace std;

int main() {
    char s[250];
    int n = 0;
    while ((s[n++] = getchar()) != '\n') ;
    s[n] = '\0';

    char o, p;
    scanf("%c %c", &o, &p); // 注意这里的输入格式

    for (int i = 0; s[i] != '\0'; ++i) {
        if (s[i] == o) {
            printf("%c", p);
        } else {
            printf("%c", s[i]);
        }
    }

    return 0;
}

4.4 YBT 2047:过滤空格

  • 考察点:空格处理
cpp
#include <bits/stdc++.h>

using namespace std;

int main() {
    char s[250];

    while (scanf("%s", s) != -1) {
        printf("%s ", s);
    }

    return 0;
}

4.5 YBT 2049:字符串判等

  • 考察点:大小写字符的转换,ASCII 值
cpp
#include <bits/stdc++.h>

using namespace std;

int main() {
    char s[250], s2[250];
    int len = 0, len2 = 0;

    char c;
    while (c = getchar()) {
        if (c == '\n') break;
        if (c == ' ') continue;
        if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
        s[len++] = c;
    }
    s[len] = '\0';

    while (c = getchar()) {
        if (c == '\n') break;
        if (c == ' ') continue;
        if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
        s2[len2++] = c;
    }
    s2[len2] = '\0';

    if (strcmp(s, s2) == 0) {
        puts("YES");
    } else {
        puts("NO");
    }

    return 0;
}

4.6 YBT 2050:字串包含

  • 考察点:字符串的常用函数
cpp
#include <iostream>
#include <string>

using namespace std;

int main() {
	string s1, s2;
	cin >> s1 >> s2;

	if (s1.length() < s2.length()) {
		swap(s1, s2);
	}
    s1 = s1 + s1;

    if (s1.find(s2) != s1.npos) {
    	cout << "true" << endl;
	} else {
		cout << "false" << endl;
	}

    return 0;
}

4.7 YBT 1143:最长最短单词

  • 考察点:string
cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    string s;
    getline(cin, s);
    int tmp = 0, maxx = -1e9, minn = 1e9, minidx = 0, maxidx = 0;
    s += ' ';

    for (int i = 0; i < s.size(); i++) {
        if (s[i] != ' ' && s[i] != ',' && s[i] != '.')
            tmp++;
        else if (tmp > 0) {
            if (tmp > maxx) {
                maxx = tmp;
                maxidx = i - tmp;
            }
            if (tmp < minn) {
                minn = tmp;
                minidx = i - tmp;
            }
            tmp = 0;
        }
    }

    for (int i = maxidx; i <= maxx + maxidx - 1; i++)
        cout << s[i];
    cout << endl;
    for (int i = minidx; i <= minidx + minn - 1; i++)
        cout << s[i];
    cout << endl;

    return 0;
}

4.8 YBT 1839:谁拿了最多奖学金

  • 考察点:字符串,整数等混合读入问题
cpp
#include <iostream>
using namespace std;

const int N = 110;

char name[N][25], info[N][2];
int grade[N][2], paper[N];

int main() {
    int n; scanf("%d", &n);

    for (int i = 0; i < n; i++) {
    	scanf("%s %d %d %c %c %d", name[i], &grade[i][0], &grade[i][1], &info[i][0], &info[i][1], &paper[i]);
	}

	int total = 0, maxv = -1, idx = -1;
	for (int i = 0; i < n; i++) {
		int p = 0;
		if (grade[i][0] > 80 && paper[i] >= 1) p += 8000;
		if (grade[i][0] > 85 && grade[i][1] > 80) p += 4000;
		if (grade[i][0] > 90) p += 2000;
		if (grade[i][0] > 85 && info[i][1] == 'Y') p += 1000;
		if (grade[i][1] > 80 && info[i][0] == 'Y') p += 850;

		total += p;
		if (p > maxv) {
			maxv = p;
			idx = i;
		}
	}

	printf("%s\n%d\n%d\n", name[idx], maxv, total);

    return 0;
}

4.9 x 进制转 10 进制

题目描述

给一个小整数 x 和一个 x 进制的数 S。将 S 转为 10 进制数。对于超过十进制的数码,用 AB 表示。

输入格式

第一行一个整数 x;

第二行一个字符串 S

输出格式

输出仅包含一个整数,表示答案。

样例 #1

样例输入 #1
16
7B
样例输出 #1
123

提示

【数据规模和约定】

保证目标数在十进制下不超过 1091x36

参考代码

cpp
#include <bits/stdc++.h>

using namespace std;

int main()
{

	int r; cin >> r;
	char x[50];
	cin >> x;

	int ans = 0;
	for (int i = 0; i < strlen(x); i++) {
		int t = x[i] >= 'A' && x[i] <= 'Z' ? x[i] - 'A' + 10 : x[i] - '0';
		ans = ans * r + t;
	}

	cout << ans << endl;

	return 0;
}

4.10 10 进制转 x 进制

题目描述

给定一个十进制整数 n 和一个小整数 x。将整数 n 转为 x 进制。对于超过十进制的数码,用 AB ... 表示。

输入格式

第一行一个整数 n

第二行一个整数 x

输出格式

输出仅包含一个整数,表示答案。

样例 #1

样例输入 #1
1000
2
样例输出 #1
1111101000

提示

【数据规模和约定】
保证 n 不超过 109x 不超过 36

参考代码

cpp
#include<cmath>
#include<iostream>
using namespace std;

int a[100000000];

void f(int x, int r) {
    // a 数组存储进制转换后的每一位
    char c[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
    int idx = 0;
    while (x != 0) {
        a[idx++] = x % r;
        x /= r;
    }
    idx--;

    for (int i = idx; i >= 0; i--) {
        if (a[i] < 10) cout << a[i];
        else cout << c[a[i] - 10];
    }
}

int main()
{
	int r, x;
	cin >> x >> r;
	f(x, r);

	return 0;
}