一文讲明白ACM输入输出模式
在线练习链接:牛客网OJ在线编程常见输入输出练习场
ACM输入输出模式是指在竞赛编程(ACM/ICPC)中常用的一种输入输出方式,它与普通控制台的输入输出有所不同。
输入模式
选择合适的输入方式
- 判断输入的类型,是数字还是字符串,如果是数字,则可以使用
cin标准输入流,如果是字符串,我们可以用getline()函数; - 判断是否知道输入数据的数量:
- 如果数量已知,可以直接用
for循环读取; - 如果数量未知,需要在循环输入中设置退出输入模式的条件;
- 如果数量已知,可以直接用
使用cin还是getline进行输入
getline()函数:
getline() 是 C++ 标准库中的一个函数,用于从输入流中读取一整行字符串,直到遇到换行符(\n)或文件结尾。它通常用于从 cin (标准输入)或文件中读取一行字符串。
函数原型:
std::istream& getline (std::istream& is, std::string& str, char delim = '\n');
is是输入流对象,可以是std::cin或文件流对象。str是一个字符串引用,用于存储从输入流中读取的字符串。delim是一个可选参数,表示结束字符,默认为换行符 ‘\n’。
(std::stringstream也可以放在std::istream位置,例如getline(stringsteam& ss, string& str, char c))
注意的是:
getline()会保留读取的字符串中的所有字符,包括空格、制表符等空白字符。- 如果输入流为空或遇到文件结尾,
getline()会返回 false,否则返回 true。(没必要退出循环时判断为空,如果为空则不会进入循环while(getline(cin, s))) - 读取的字符串不包含结束字符(默认为换行符 ‘
\n’)。
如果一行中数字的数量是固定的,则使用cin和getline()都可以,并且cin可能更方便;
例如:A+B(1)中,计算输入的两个数字之和;
输入:
1 5
10 20
每一行规定了只有两个数据,则使用cin和getline()都可以;
如果一行中数字的数量是未知的,比如计算每一行数字的累加和中:
输入:
1 2 3 4 5
1 2 3
1 1
每一行的数据长度不固定,而且没有显式说明每一行数据的长度,此时使用cin明显变得复杂不可控,直接使用getline()读取一行字符串,然后字符串切割;
如果显式告诉了每行的数据的个数,则cin和getline()都可以,比如:
输入:
2 1 1
3 1 2 3
其中每行第一个数字是该行要累加的数据个数;
getline()更加全能,但是在getline()之后涉及字符串的切割、转换字符串为整型等一系列操作,所以使用时也要斟酌。
输入模式的退出
输入和输出是两个独立的部分,只有输入模式退出之后,才能进行输出;
例如:A+B(1)中,计算输入的两个数字之和中;
输入:
1 5
10 20
可以看出我们并不知道要输入几行数据,所以需要加入输入结束的判断逻辑,如果是使用getline()读取的字符串数据,则输入结束条件就是getline()获取到的为空行时结束输入,开始输出结果;
而在A+B(2)中,输入的第一行包括一个数据组数t,接下来每一行是两个正整数,计算两数之和;
输入:
2
1 5
10 20
此时就并不需要进行判断输入结束条件,而是直接使用for循环读取输入:for (int i = 0; i < t; i++) {读取数据,使用cin读取};
也有一些题目会给出输入退出条件,比如当输入0时,退出输入,只需要在输入后加入判断语句即可;
简而言之,如果知道输入的组数,则可以使用for循环控制,如果不知道输入的组数且没有显式的输入结束退出条件,只需要自己在循环中设置退出条件;
输入为字符串的处理
使用getline()获取到一行的字符串时,就涉及到字符串的处理;
例如:
输入:
1 2 3 4
1 2 3
我们使用getline()读取一行字符串,例如是"1 2 3 4",则需要将字符串按照空格进行切割成多个子串,这些子串还需要转换为整型进行计算;
字符串的处理方法多种多样,这里介绍4种常用的方法:
- 将字符串看成一个字符数组,然后遍历字符数组;
- 使用
std::stringstream字符串流进行处理(推荐使用); - 使用**
find()和substr()函数**组合处理; - 使用正则表达式进行匹配;
将字符串看作字符数组处理
例如:(以A+B(1)为例)
for (int j = s.size() - 1; j >= 0; j--) {
if (s[j] == ' ') count = 1;
else {
res += (s[j] - '0') * count;
count *= 10;
}
}
注意事项:
- 字符串要从右向左遍历,因为数字的低位靠右;
- 字符串拼接直接使用加号运算符(重载后的运算符);
- 注意字符串转整型的逻辑(按位处理);
使用std::stringstream进行处理
例如:(以A+B(1)为例)
#include <sstream>
// 方法2: 使用std::sstream;
stringstream ss(s);
int a, b;
ss >> a >> b;
res = a + b;
stringstream是C++标准库中的一个类,它可以让你像使用输入/输出流一样来操作字符串。它继承自iostream类,因此具有和cin、cout类似的操作符>>和<<。
stringstram对象ss的使用方法和cin类似;我们可以使用>>操作符从ss中提取整数,就像从cin中读取一样。
函数原型:
stringstream::stringstream(const string& str);
stringstream默认是按照空格进行切分字符串的,切分结果可以通过>>操作符提取,可以提取整型,也可以提取字符串;
如果字符串不是按照空格进行切分的,则不能使用stringstream进行切分;
比如题目字符串排序(3)中,数据用’,'而不是空格进行隔开,则此时无法直接使用stringstream获取到切割后的子串,而是联合getline()进行使用:
stringstream ss(s); // 只能切割为整型?不是
vector<string> strline;
string str;
// while (ss >> str) strline.push_back(str);// 按照逗号切割而不是空格;
while (getline(ss, str, ',')) { // 按照逗号切割; 注意getline在cin中只会按照\n切割,
// cin是std::istream类型,而ss是std::stringsteam类型,所以是两个重载方法,参数不一致,不要混淆;
strline.push_back(str);
}
注意如果是字符串连接,则使用加号运算符;如果是在字符串后面添加一个字符,则可以使用push_back,因为字符串本质就是一个vector<char>;
使用find()和substr()进行处理
std::string::find是C++标准库中的一个字符串查找函数,它返回指定子串在字符串中第一次出现的位置。如果没有找到,则返回string::npos。
std::string::substr是一个字符串截取函数,它返回一个新字符串,该字符串是原字符串的一个子串。
例如:(以A+B(1)为例)
// 方法2:使用find和substr(切割字符串为子串)
size_t pos = s.find(' '); // 找到字符串中第一次出现空格处的索引,注意使用size_t而不是int类型;
int a = stoi(s.substr(0, pos)); // 注意stoi即string to int,此外注意substring的左闭右开;
int b = stoi(s.substr(pos + 1)); // 余下子串不必写结束位置;
res = a + b;
std::stoi函数是C++11新增的一个字符串转整数的函数,它的原型为:
int stoi(const string& str, size_t* idx = 0, int base = 10);
它将字符串str转换为整数,base参数指定进制(默认为10进制)。如果转换失败,它会抛出一个std::invalid_argument异常。idx参数是一个可选的输出参数,用于存储转换停止的位置。
注意find函数返回的数据类型是size_t而不是int,size_t是 C++ 标准库中的一种无符号整数类型,用于表示大小或计数。它的确切大小取决于操作系统和编译器的实现,但至少有足够的范围来表示当前机器的最大可能对象大小。使用size_t类型作为索引或者计数变量的好处:
- 避免下标越界;(无符号类型)
- 尺寸兼容性;(和使用的库函数、容器类进行尺寸匹配)
- 跨平台兼容性;
- 标准化;
使用正则表达式处理
例如:(以A+B(1)为例)
#include <regex>
// 方法3:使用正则表达式匹配;stl::regex;
regex re("\\s+"); // 正则表达式匹配一个或多个空白字符,\\是因为防止转义字符
sregex_token_iterator iter(s.begin(), s.end(), re, -1); // 创建分词迭代器
sregex_token_iterator end; // 结束迭代器
int num1 = std::stoi(*iter++); // 提取第一个单词,并转换为整数,迭代器自增到第二个单词位置
int num2 = std::stoi(*iter); // 提取第二个单词,并转换为整数
res = num1 + num2;
正则表达式(Regular Expression)是一种用于匹配字符串的模式(pattern)。它是由一些特殊字符和普通字符组成的字符串,可以用来描述、匹配或替换另一个字符串。
正则表达式常用的元字符:
.匹配任意单个字符(除了换行符)\d匹配任意数字字符,等价于[0-9]\D匹配任意非数字字符,等价于[^0-9]\w匹配任意字母、数字或下划线字符\W匹配任意非字母、非数字和非下划线字符\s匹配任意空白字符(空格、制表符、换行符等)\S匹配任意非空白字符^匹配字符串的开始位置$匹配字符串的结束位置[]匹配括号内的任意单个字符[^]匹配不在括号内的任意单个字符|匹配左右两个表达式中的任意一个()用于分组
+匹配前面的子表达式一次或多次
*匹配前面的子表达式零次或多次?匹配前面的子表达式零次或一次{n}匹配前面的子表达式恰好 n 次{n,}匹配前面的子表达式至少 n 次{n,m}``匹配前面的子表达式至少 n 次,但不超过 m 次
正则表达式匹配实例
^\d{3}\s+\d{3,8}$匹配连续的数字(区号)和数字(电话号码),例如"800 8888888"\b\w+\b匹配单词边界(一个或多个字母、数字和下划线)\d{4}-\d{2}-\d{2}匹配日期格式,例如"2023-05-28"[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+匹配电子邮件地址
输出模式
输入使用cout进行输出即可;一般情况下,输入数据经过处理的结果在数组中,只需要循环访问数组中元素然后依次cout即可;
对字符串进行排序
在字符串排序(1)中,需要对输入的字符串进行排序,可以使用sort函数;
输入:
5
c d a bb e
可以使用stringstream轻易地将一行中切割好后的各个字符串保存在一个字符串向量中(也可以使用其他方法)
在得到一个字符串向量ss之后,要对字符串向量进行排序:
#include <algorithm> // sort函数头文件;
// 对字符串数组ss进行排序;
sort(ss.begin(), ss.end(), compareStrings);
compareStrings函数是一个自定义函数,返回布尔类型:
bool compareStrings (const string& a, const string& b) {
return a < b;
}
此时即可实现字符串排序;
2140

被折叠的 条评论
为什么被折叠?



