Chapter 2 Exercises – Professional C++

练习 2-1:编写一个程序,要求用户输入两个字符串,然后使用三向比较运算符按字母顺序打印它们。要要求用户输入一个字符串,可以使用在第 1 章中简要介绍的 std::cin 流。第 13 章“揭秘 C++ I/O”详细解释了输入和输出,但现在,以下是有关如何从控制台中读取字符串的信息。要终止该行,只需按 Enter 键。

std::string s;
getline(cin, s1);

我的解答:

#include <iostream>
#include <string>

int main() {
	std::string s1;
	std::getline(std::cin, s1);
	
	std::string s2;
	std::getline(std::cin, s2);

	auto result{ s1 <=> s2 };

	if (std::is_lt(result)) {
		std::cout << s1 << std::endl;
		std::cout << s2 << std::endl;
	}
	else {
		std::cout << s2 << std::endl;
		std::cout << s1 << std::endl;
	}

	return 0;
}

练习 2-2:编写一个程序,要求用户输入一个源字符串(= 干草堆)、一个要在源字符串中查找的字符串(= 针)和一个替换字符串。编写一个具有三个参数(干草堆、针和替换字符串)的函数,该函数返回一个干草堆的副本,其中所有针都用替换字符串替换。仅使用 std::string ,不用 string_view 。你会使用什么类型的参数,以及为什么选择这种类型?从 main() 调用此函数,并打印出所有字符串以进行验证。

我的解答:

#include <iostream>
#include <string>

std::string process(std::string haystack, std::string const& needle, std::string const& replacement) {
	if (needle == replacement) {
		return haystack;
	}
	
	std::size_t posNeedle{ 0 };
	while ((posNeedle = haystack.find(needle, posNeedle)) != haystack.npos) {
		haystack.replace(posNeedle, needle.size(), replacement);
		posNeedle += replacement.size();
	}
	
	return haystack;
}

int main() {
	std::string strSrc;
	std::string strFind;
	std::string strReplace;

	std::cout << "输入源字符串: ";
	std::getline(std::cin, strSrc);

	std::cout << "输入要在源字符串中查找的字符串: ";
	std::getline(std::cin, strFind);

	std::cout << "输入替换字符串: ";
	std::getline(std::cin, strReplace);


	std::string result{ process(strSrc, strFind, strReplace) };

	std::cout << "Haystack: " << strSrc << std::endl;
	std::cout << "Needle: " << strFind << std::endl;
	std::cout << "Replacement: " << strReplace << std::endl;
	std::cout << "Result: " << result << std::endl;

	return 0;
}

对于函数的三个参数,我使用的类型分别是std::stringstd::string const&std::string const&。对于后两个参数,由于不会在函数中对它们进行修改,所以使用了 const 引用。对于第一个参数,由于题目中要求返回该参数的副本,因此我直接使用了 std::string,在调用函数时,该参数会进行一次拷贝,所以在函数内部对该变量的修改不会影响原始变量,同时也省去了在函数内部再次显式拷贝字符串的代码。在函数返回时,我也是直接返回的 std::string,而不是 std::move(std::string),原因是编译器对此有优化,如果写成后者反倒是画蛇添足。

练习 2-3:修改练习 2-2 中的程序,并在尽可能多的位置使用 std::string_view

我的解答:

#include <iostream>
#include <string>

std::string process(std::string haystack, std::string_view needle, std::string_view replacement) {
	if (needle == replacement) {
		return haystack;
	}
	
	std::size_t posNeedle{ 0 };
	while ((posNeedle = haystack.find(needle, posNeedle)) != haystack.npos) {
		haystack.replace(posNeedle, needle.size(), replacement);
		posNeedle += replacement.size();
	}
	
	return haystack;
}

int main() {
	std::string strSrc;
	std::string strFind;
	std::string strReplace;

	std::cout << "输入源字符串: ";
	std::getline(std::cin, strSrc);

	std::cout << "输入要在源字符串中查找的字符串: ";
	std::getline(std::cin, strFind);

	std::cout << "输入替换字符串: ";
	std::getline(std::cin, strReplace);


	std::string result{ process(strSrc, strFind, strReplace) };

	std::cout << "Haystack: " << strSrc << std::endl;
	std::cout << "Needle: " << strFind << std::endl;
	std::cout << "Replacement: " << strReplace << std::endl;
	std::cout << "Result: " << result << std::endl;

	return 0;
}

可以改成 std::string_view 的地方不多,我就只把函数的后两个参数的类型改了。第一个参数,我想了想,感觉没必要,感觉现在这样就很优雅了。我特别想用 std::string_view 的成员函数 remove_prefix(),可以用来跳过字符串的前面已查找过的部分,但是由于 std::string_view 只是一个视图,不能修改字符串,所以用在这里不适合。

这个成员函数remove_prefix()我在一个类似的程序中用过,那个程序是查找一个字符串在另一个字符串中出现的次数,见:std::string_view 类

练习 2-4:编写一个程序,要求用户输入未知数量的浮点数,并将所有数字存储在 vector 中。每个数字输入后都应换行。当用户输入数字 0 时,停止要求输入更多数字。要从控制台读取浮点数,请以与在第 1 章中输入整数值相同的方式使用 cin 。在具有几列的表格中设置所有数字的格式,其中每列以不同的格式输出数字。表格中的每一行对应一个输入的数字。

我的解答:

import std;

int main() {
	double value;
	std::vector<double> vec;
	
	std::cin >> value;
	while (value != 0.0) {
		vec.push_back(value);
		std::cin >> value;
	}

	for (double value : vec) {
		// 默认;科学计数法;定点计数法;通用计数法;十六进制计数法
		//std::println("{0}\t{0:e}\t{0:f}\t{0:g}\t{0:a}", value);

		std::println("{0:16e} | {0:12f} | {0:<12g} | {0:>+#12g}", value);
		// +,表示对负数和正数显示符号。
		// #,对于浮点数类型,将始终输出一个十进制分隔符(decimal separator),
		// 如`3`,将显示`3.000000`(0的个数依赖其他设置),3 后面的`.`将始终显示
	}

	return 0;
}

练习 2-5:编写一个程序,要求用户输入未知数量的单词。当用户输入 `*` 时,停止输入。将所有单个单词存储在 `vector` 中。您可以使用以下内容输入单个单词:

std::string word;
cin>> word;

我的解答:

import std;

int main() {
	std::string word;
	std::vector<std::string> vec;

	while (true) {
		std::cin >> word;
		if (word == "*") {
			break;
		}

		vec.push_back(std::move(word));
	}

	// 使用算法 std::max_element() 查找最长字符串的迭代器
	auto longest_str{ std::max_element(vec.begin(), vec.end(), [](std::string_view a, std::string_view b) {return a.size() < b.size(); }) };
	// 最长字符串的长度
	std::size_t longest_length{ longest_str != vec.end() ? longest_str->size() : 0 };
	
	for (int i{}; i < vec.size(); ++i) {
		if ((i + 1) % 5 == 0) {
			std::println("|{:^{}}|", vec[i], longest_length);
		}
		else {
			std::print("|{:^{}}", vec[i], longest_length);
		}
	}

	return 0;
}

我的答案有点“杀鸡用牛刀”。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注