C++ 快读快写详解:读入整数与读入字符串
整数读入输出
众所周知,快读快写是众多 OI 选手必须掌握的一项卡常小技巧,优秀的读入输出可以让你的代码跑得飞快,有时甚至比别人多得一些分数,那么如何写快读快写呢,那就引入下面这道题:题目跳转:P10815 【模板】快速读入。
在此注明,这不是一篇关于P10815 【模板】快速读入的题解(当然也算是吧),只是借用它来展现各种读入输出方法的速度对比。
引入
读入 \(n\) 个数,求它们的和。每个测试点时限 \(2.5s\)。
非常简单,对吧?但是 \(n\leq 1\times 10^8\),怎么读入这么多数成了难题。
直接这样写:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,x,sum;
int main(){
cin>>n;
for(ll i=1;i<=n;i++){
cin>>x;
sum+=x;
}
cout<<sum;
return 0;
}
读入 \(10^8\) 的数据需要惊人的 \(67.526s\),这谁能接受?
先在此声明,本文在本地跑出来的时间均为在 Windows 下使用 DEV-C++ 编译后跑出来的时间,是只跑一组 \(10^8\) 的数据得出来的;在洛谷上的时间为所有洛谷提供的数据评测出来的时间。
先看一组对比数据:
- 读入对比(以 \(1 MiB\)
fread为 \(1\))
| 方法 | 相对耗时 | 说明 |
|---|---|---|
fread 大块读 |
\(1.0\) | 通常最快或接近最快;块 IO,库/系统调用开销被摊薄 |
cin.read 解绑 |
\(1.05\sim 1.6\) | 块 IO,接近 fread;需自行缓冲并处理 gcount() |
getchar_unlocked |
\(4\sim 10\) | 逐字符无锁;常配合手写整数解析,通常很快 |
getchar |
\(8\sim 20\) | 逐字符有锁和 stdio 开销;比 getchar_unlocked 慢 |
关流的 cin>> |
\(20\sim 60\) | 关闭同步并 tie(nullptr) 后明显变快,但仍是流式 token 格式化提取 |
普通 scanf |
\(20\sim 70\) | 有 stdio 锁和格式串解析;读整数通常比普通 cin>> 快 |
普通 cin>> |
\(50\sim 150\) | 未关同步,含同步/刷新等开销,通常最慢之一 |
- 写出对比(以 \(1 MiB\)
fwrite大块写为 \(1\))
| 方法 | 相对耗时 | 说明 |
|---|---|---|
fwrite 大块输出 |
\(1.0\) | 通常最快或接近最快;需先构造缓冲区 |
cout.write 解绑 |
\(1.05\sim 1.8\) | 块 IO,接近 fwrite;需自己管理输出缓冲区 |
putchar |
\(10\sim 30\) | 逐字符有锁;写整数需手写转字符串 |
关流的 cout<< |
\(20\sim 50\) | 关闭同步并 tie(nullptr) 后较快,但仍有格式化/类型处理开销 |
普通 printf |
\(20\sim 50\) | 每次格式化有解析开销和 stdio 锁;大块 %s 会更快 |
普通 cout<< |
\(50\sim 150\) | 未关同步,通常明显慢于关流 cout<< 和普通 printf |
这里解释一下 cin.read 和 cin.get 到底是啥,其实你可以理解为:
-
cin.read类似fread,是块读; -
cout.write类似fwrite,是块写。
以下按"从常见、易写到较快"的顺序介绍。不同平台、编译器、IO 环境下排名可能不同,表中相对耗时只表示数量级参考,不代表严格排名:
一、关闭同步流后的 cin、cout
这应该是许多 OI 选手在考场上最喜欢用的快读方式,因为它够用且非常好写。
关闭同步流后,cin/cout 与 scanf/printf 不再保证同步,千万不要混用。若必须混用,应重新打开同步并注意刷新,或全程只使用一套 IO。
但是它通过不了此题 ,本地实际读入 \(10^8\) 的数据需要 \(10.951s\),在洛谷上花了 \(6.11s\),点此查看,是无法接受的。
代码
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,x,sum;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n;
for(ll i=1;i<=n;i++){
cin>>x;
sum+=x;
}
cout<<sum;
return 0;
}
二、C 风格输入的优化之 getchar
这就是非常实用的 getchar 读入方式,它是通过先读入字符,然后再转成数字的形式进行读入加速的。
代码
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x=0,f=1;
char ch=getchar();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
void write(ll x){
if(x<0){
x*=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
ll n,sum;
int main(){
n=rd();
for(ll i=1;i<=n;i++){
sum+=rd();
}
write(sum);
return 0;
}
非常出乎意料,本地读入 \(10^8\) 的数据,getchar 居然用了 \(17.476s\)!比关同步流还慢!在洛谷上花了 \(4.20s\)(比关流稍快),点此查看,所以使用 getchar 的方式通过不了此题。
三、C 风格输入的优化之 getchar_unlocked
getchar_unlocked 是 getchar 的非线程安全版本,少了加锁开销,因此通常更快。正式比赛能否使用要看评测环境。
Linux 下一般用 getchar_unlocked;Windows/MinGW 下通常没有 getchar_unlocked,可用 _getchar_nolock 替代,写的时候要注意,下面的代码使用了宏定义的一些技巧,在 Windows 和 Linux 两种环境下都不会 CE。
代码
cpp
#include<bits/stdc++.h>
using namespace std;
#ifdef __unix__
#define gc getchar_unlocked
#else
#define gc _getchar_nolock
#endif
typedef long long ll;
ll rd(){
ll x=0,f=1;
char ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
void write(ll x){
if(x<0){
x*=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
ll n,sum;
int main(){
n=rd();
for(ll i=1;i<=n;i++){
sum+=rd();
}
write(sum);
return 0;
}
此方法在本地跑 \(10^8\) 的数据花费 \(4.499s\),在洛谷上跑出了 \(3.21s\) 的成绩,点此查看,已足够通过此题(但个人觉得还是慢!)。
四、fread 快读
fread 能将需要的文件部分读入内存缓冲区。由于 fread 是整段整段读取,所以比 getchar_unlocked 要快得多。
使用时一般把它写成宏定义的形式,既简洁又快速。
在Windows环境下,在 DEV-C++ 或 Visual Studio Code 里本地运行的时候,输入完了记得加 Ctrl+Z,才会执行程序。
在Linux环境下,在本地运行的时候,输入完了记得加 Ctrl+D,才会执行程序。
当然使用文件读入读出就不用执行 Ctrl+Z 或 Ctrl+D 操作。
使用时不能同时存在 cin/scanf/getchar 等(都用这么快的了还用啥 cin、scanf)。
代码
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
ll rd(){
ll x=0,f=1,ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
void write(ll x){
if(x<0){
x=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
ll n,sum;
int main(){
n=rd();
for(ll i=1;i<=n;i++){
sum+=rd();
}
write(sum);
return 0;
}
这种方法在本地读入 \(10^8\) 的数据只需 \(2.905s\),在洛谷上跑出了 \(2.33s\) 的成绩点此查看,是不是快了很多。
五、cin.read 读入
此方法需要关闭同步流,写法和 fread 方法类似,只有些许不同。
在Windows环境下,在 DEV-C++ 或 Visual Studio Code 里本地运行的时候,输入完了记得加 Ctrl+Z,才会执行程序。
在Linux环境下,在本地运行的时候,输入完了记得加 Ctrl+D,才会执行程序。
使用时不要同时用 cin>>、scanf、getchar 等其他读入方式,也不要和 fread 混用。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(cin.read((char*)buf,1<<20),p2=(p1=buf)+cin.gcount(),p1==p2)?EOF:*p1++)
ll rd(){
ll x=0,f=1,ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
void write(ll x){
if(x<0){
x=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
ll n,sum;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
n=rd();
for(ll i=1;i<=n;i++){
sum+=rd();
}
write(sum);
return 0;
}
本地读入 \(10^8\) 的数据实测下来 \(2.812s\),非常惊人!在洛谷上跑出 \(2.33s\) 的成绩点此查看,也是非常不错。
以上就是个人认为比较快且好写实用的读入方法,当然还有其他的一些奇奇怪怪的读入方法,代码都非常多,很难记,而且都不太建议在正式 OI 比赛中使用,所以这里就不一一介绍了。
六、关于输出
因为一般题目输出都比较小,而且在输出方面优化的速度不是很多(相比于优化读入),所以这里就给出几种好写且快速的输出方法(以下几种输出方法均是将整数转化为字符输出,速度还可以,想要详细速度对比请往上看,慢的输出咱就不给了):
1. 递归
非常常见的一种写法,可能比后面的方法慢。
cpp
void write(ll x){
if(x<0){
x=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
2. 非递归
可能会比递归的方法快一点点,但也比后面的方法慢一点点。
这里字符数组 ch 的大小要按最大位数开够。以 long long 为例,最多 \(19\) 位数字,开 char ch[20]; 足够。
cpp
void write(ll x){
if(x<0){
x=-x;
putchar('-');
}
if(x==0){
putchar('0');
return;
}
ll top=0;
char ch[20];
while(x){
ch[++top]=x%10+'0';
x/=10;
}
for(ll i=top;i>=1;i--){
putchar(ch[i]);
}
}
3. fwrite 快写(递归版)
与 fread 对应,用于实现更快的快写。
使用时一定不要再出现 cout。
注意此写法要在主函数最后,即 return 0; 前写上:fls(),以便一次性输出并清空缓冲区。
配合 fread 使用(fread 的注意事项见上):
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
ll rd(){
ll x=0,f=1,ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (fwrite(obuf,1,op-obuf,stdout),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void write(ll x){
if(x<0){
x=-x;
pc('-');
}
if(x>9){
write(x/10);
}
pc(x%10+'0');
}
ll n;
int main(){
n=rd();
write(n);
fls();
return 0;
}
4. fwrite 快写(非递归版)
与 fwrite 的递归版相似,这里与普通的非递归的 write 函数几乎一样。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
ll rd(){
ll x=0,f=1,ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (fwrite(obuf,1,op-obuf,stdout),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void write(ll x){
if(x<0){
x=-x;
pc('-');
}
if(x==0){
pc('0');
return;
}
ll top=0;
char ch[20];
while(x){
ch[++top]=x%10+'0';
x/=10;
}
for(ll i=top;i>=1;i--){
pc(ch[i]);
}
}
ll n;
int main(){
n=rd();
write(n);
fls();
return 0;
}
5. cout.write 快写(递归版)
和 fwrite 方法差不多,这里和 cin.read 读入方法搭配(cin.read 注意事项见上)。
注意此写法要在主函数最后,即 return 0; 前写上:fls(),以便一次性输出并清空缓冲区。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(cin.read((char*)buf,1<<20),p2=(p1=buf)+cin.gcount(),p1==p2)?EOF:*p1++)
ll rd(){
ll x=0,f=1,ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (cout.write((char*)obuf,op-obuf),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void write(ll x){
if(x<0){
x=-x;
pc('-');
}
if(x>9){
write(x/10);
}
pc(x%10+'0');
}
ll n;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
n=rd();
write(n);
fls();
return 0;
}
6. cout.write 快写(非递归版)
和 cout.write 递归版差不多,参照 fwrite 非递归版。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(cin.read((char*)buf,1<<20),p2=(p1=buf)+cin.gcount(),p1==p2)?EOF:*p1++)
ll rd(){
ll x=0,f=1,ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (cout.write((char*)obuf,op-obuf),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void write(ll x){
if(x<0){
x=-x;
pc('-');
}
if(x==0){
pc('0');
return;
}
ll top=0;
char ch[20];
while(x){
ch[++top]=x%10+'0';
x/=10;
}
for(ll i=top;i>=1;i--){
pc(ch[i]);
}
}
ll n;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
n=rd();
write(n);
fls();
return 0;
}
总结
以上就是读入输出整数的非常实用的方法了,选择一个自己喜欢的并记住它,对你有很大好处。
一些东西我都封装成了函数或宏定义,这些东西都可以直接套的。比如既要读入输出整数,又要读入输出字符串,那么就可以把两个的封装好的函数都写上。
但一定要记牢哪些不可以混用,一般同类的输入输出就当 CP 记,下面给出一般几类 "CP":
| 读入 | 输出 |
|---|---|
cin>> |
cout<< |
getchar |
putchar |
getchar_unlocked |
putchar |
fread |
fwrite |
cin.read |
cout.write |
cin.get |
cout.put |
scanf |
printf |
更准确地说:带自定义缓冲的 fread/cin.read 不要和 cin/scanf/getchar 混用;关闭同步后的 cin/cout 不要和 scanf/printf 混用;表中只是推荐搭配,不是唯一合法搭配。
一般有关 cin 和 cout 的快读快写方法都搭配关同步流操作。
如果还想看读入输出字符串的方法请往下看。
字符串读入输出
读入输出字符串的这几种方法之间还是有一定速度差距的:
- 读入,以 \(1 MiB\)
fread为 \(1\):
| 方法 | 相对耗时 | 说明 |
|---|---|---|
fread 大块读 |
\(1.0\) | 通常最快或接近最快 |
cin.read 解绑 |
\(1.05\sim1.6\) | 块 IO,接近 fread |
getline(cin,string) 解绑 |
\(2\sim5\) | 有按行扫描和 string 复用开销 |
getchar_unlocked |
\(4\sim10\) | 逐字符,但去掉锁 |
getchar |
\(8\sim20\) | 逐字符,有锁和 stdio 开销 |
cin.get 解绑 |
\(8\sim25\) | 逐字符,有 stream sentry 等开销 |
scanf("%c") |
\(20\sim50\) | 逐字符格式化,最慢之一 |
cin>> 解绑 |
\(20\sim60\) | 逐 token 格式化提取,含类型解析与空白跳过 |
- 写出,以 \(1 MiB\)
fwrite为 \(1\):
| 方法 | 相对耗时 | 说明 |
|---|---|---|
fwrite 大块输出 |
\(1.0\) | 通常最快或接近最快 |
cout.write 解绑 |
\(1.05\sim1.8\) | 块 IO,接近 fwrite |
printf("%s") 大块 |
\(1.2\sim2.5\) | 一次格式化,通常可接受 |
putchar_unlocked |
\(5\sim15\) | 逐字符无锁 |
putchar |
\(10\sim30\) | 逐字符有锁 |
cout.put 解绑 |
\(10\sim30\) | 逐字符流操作 |
printf("%c") |
\(20\sim50\) | 逐字符格式化,非常慢 |
cout<< 解绑 |
\(20\sim50\) | 格式化输出,有类型/格式标志处理开销 |
这里解释一下 cin.read 和 cin.get 到底是啥,其实你可以理解为:
-
cin.read类似fread,是块读; -
cin.get类似getchar,是逐字符读; -
cout.write类似fwrite,是块写; -
cout.put类似putchar,是逐字符写。
下面按主观认为的速度由慢到快给出各类方法(速度和实测排名差不多),为了节省时间,读入和输出的方法我就不分开了,常见的搭配就放一起了:
一、普通/关流的 cin>>、cout<< 组合
注释掉 int main(){} 里的前三行就是普通的,不注释就是关流的。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>s;
cout<<s;
return 0;
}
二、scanf 与 printf 组合
这种方法适合给了上限的读入输出字符串。
string 不是 char 数组,&s 的类型是 string*,不是 char*。用 %s 读写 string 属于未定义行为。所以要用 char 数组读入。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+5;//大小自行修改
char s[maxn];
int main(){
scanf("%1000000s",s);//这里的 1000000 只是长度上限,而不是实际读入多少
printf("%s",s);
return 0;
}
三、getchar 与 putchar 组合
分为两种,一种是知道字符串长度的,一种是不知道的,"三"这里都给出,到后面的"四、五、六"就只给出"不知道字符串"的写法(其实知道长度的场景也可以改用"不知道长度"的写法,读完后按需截取;反之则不行),知道字符串长度的写法与"三"这里同理。
这里给出的是支持读入"大/小写字母"、"数字"的写法,如果读入字符串里有其他字符,可以加些特判(下面的"四、五、六"同理)。
1. 知道字符串长度
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x=0,f=1,ch=getchar();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
string rds(ll len){
string x="";
ll ch=getchar();
while(ch!=EOF&&(ch<'a'||ch>'z')&&(ch<'0'||ch>'9')&&(ch<'A'||ch>'Z')){
ch=getchar();
}
if(ch==EOF){
return x;
}
for(ll i=1;i<=len;i++){
x+=ch;
if(i<len){
ch=getchar();
}
}
return x;
}
void writes(const string &x){
ll len=x.size();
for(ll i=0;i<len;i++){
putchar(x[i]);
}
}
ll n;
string s;
int main(){
n=rd();
s=rds(n);
writes(s);
return 0;
}
2. 不知道字符串长度
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string rds(){
string x="";
ll ch=getchar();
while(ch!=EOF&&(ch<'a'||ch>'z')&&(ch<'0'||ch>'9')&&(ch<'A'||ch>'Z')){
ch=getchar();
}
if(ch==EOF){
return x;
}
while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')||(ch>='A'&&ch<='Z')){
x+=ch;
ch=getchar();
}
return x;
}
void writes(const string &x){
ll len=x.size();
for(ll i=0;i<len;i++){
putchar(x[i]);
}
}
string s;
int main(){
s=rds();
writes(s);
return 0;
}
四、getchar_unlocked 与 putchar 组合
这里只给出不知道字符串长度的写法且支持读入"大/小写字母"、"数字",如果读入字符串里有其他字符,可以加些特判。
下面的代码在 Windows 和 Linux 中均不会 CE。
cpp
#include<bits/stdc++.h>
using namespace std;
#ifdef __unix__
#define gc getchar_unlocked
#else
#define gc _getchar_nolock
#endif
typedef long long ll;
string rds(){
string x="";
ll ch=gc();
while(ch!=EOF&&(ch<'a'||ch>'z')&&(ch<'0'||ch>'9')&&(ch<'A'||ch>'Z')){
ch=gc();
}
if(ch==EOF){
return x;
}
while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')||(ch>='A'&&ch<='Z')){
x+=ch;
ch=gc();
}
return x;
}
void writes(const string &x){
ll len=x.size();
for(ll i=0;i<len;i++){
putchar(x[i]);
}
}
string s;
int main(){
s=rds();
writes(s);
return 0;
}
五、fread 与 fwrite 组合
这里只给出不知道字符串长度的写法且支持读入"大/小写字母"、"数字",如果读入字符串里有其他字符,可以加些特判。
使用时一定不要再出现 cin 或 cout,记得在 return 0; 前加 fls();。
在Windows环境下,在 DEV-C++ 或 Visual Studio Code 里本地运行的时候,输入完了记得加 Ctrl+Z,才会执行程序。
在Linux环境下,在本地运行的时候,输入完了记得加 Ctrl+D,才会执行程序。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
string rds(){
string x="";
ll ch=gc();
while(ch!=EOF&&(ch<'a'||ch>'z')&&(ch<'0'||ch>'9')&&(ch<'A'||ch>'Z')){
ch=gc();
}
if(ch==EOF){
return x;
}
while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')||(ch>='A'&&ch<='Z')){
x+=ch;
ch=gc();
}
return x;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (fwrite(obuf,1,op-obuf,stdout),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void writes(const string &x){
ll len=x.size();
for(ll i=0;i<len;i++){
pc(x[i]);
}
}
string s;
int main(){
s=rds();
writes(s);
fls();
return 0;
}
六、cin.read 与 cout.write 组合
这里只给出不知道字符串长度的写法且支持读入"大/小写字母"、"数字",如果读入字符串里有其他字符,可以加些特判。
记得在 return 0; 前加 fls();。
在Windows环境下,在 DEV-C++ 或 Visual Studio Code 里本地运行的时候,输入完了记得加 Ctrl+Z,才会执行程序。
在Linux环境下,在本地运行的时候,输入完了记得加 Ctrl+D,才会执行程序。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(cin.read((char*)buf,1<<20),p2=(p1=buf)+cin.gcount(),p1==p2)?EOF:*p1++)
string rds(){
string x="";
ll ch=gc();
while(ch!=EOF&&(ch<'a'||ch>'z')&&(ch<'0'||ch>'9')&&(ch<'A'||ch>'Z')){
ch=gc();
}
if(ch==EOF){
return x;
}
while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')||(ch>='A'&&ch<='Z')){
x+=ch;
ch=gc();
}
return x;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (cout.write((char*)obuf,op-obuf),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void writes(const string &x){
ll len=x.size();
for(ll i=0;i<len;i++){
pc(x[i]);
}
}
string s;
int main(){
s=rds();
writes(s);
fls();
return 0;
}
七、读入整行(包含空格)的方法
使用 getline(cin,s) 的方法
搭配 cout<< 使用。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
int main(){
getline(cin,s);
cout<<s;
return 0;
}
getchar、getchar_unlocked、fread、cin.read 通用版
这里就以 fread 为例,我相信你已经会变通了,其他三种方法同理,这里就不给出了。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
uint8_t buf[1<<20],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
string rds(){
string x="";
ll ch=gc();
while(ch=='\n'){//跳过前导换行
ch=gc();
}
while(ch!='\n'&&ch!=EOF&&ch!='\r'){
x+=ch;
ch=gc();
}
return x;
}
uint8_t obuf[1<<20],*op=obuf;
#define fls() (fwrite(obuf,1,op-obuf,stdout),op=obuf,0)
#define pc(ch) (op==obuf+(1<<20)?fls():0,*op++=(ch))
void writes(const string &x){
ll len=x.size();
for(ll i=0;i<len;i++){
pc(x[i]);
}
}
string s;
int main(){
s=rds();
writes(s);
fls();
return 0;
}
总结
以上就是读入输出字符串的非常实用的方法了,希望你能选择一个自己喜欢的并记住它。
其实并没有那么难,很多都是相通的,希望你在今后的 OI 生涯中,不会因读入输出卡常而烦恼。
声明
本文由作者费时费力 \(+\) 纯手工制作,均为原创,转载请跟我说一声,并注明出处。
如果您觉得有不正确或不妥的地方,欢迎在评论区指正,感谢!
如果觉得对你有帮助,就帮忙点个赞吧!(❁´◡`❁)