快读快写
Charlie_ljk · · 科技·工程
源码
此处展示的是 2.5 版本源码:
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
inline bool blank(const char x) {return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<typename Tp> inline void read(Tp &x) {x=0; register bool z=true; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=(x<<1)+(x<<3)+(a^48); x=(z?x:~x+1);}
inline void read(double &x) {x=0.0; register bool z=true; register double y=0.1; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=x*10+(a^48); if(a!='.') return x=z?x:-x,void(); for(a=gc();isdigit(a);a=gc(),y/=10) x+=y*(a^48); x=(z?x:-x);}
inline void read(char &x) {for(x=gc();blank(x)&&(x^-1);x=gc());}
inline void read(char *x) {register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) *x++=a; *x=0;}
inline void read(string &x) {x=""; register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) x+=a;}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y) {read(x),read(y...);}
template<typename Tp> inline void write(Tp x) {if(!x) return pc(48),void(); if(x<0) pc('-'),x=~x+1; register int len=0; register char tmp[64]; for(;x;x/=10) tmp[++len]=x%10+48; while(len) pc(tmp[len--]);}
inline void write(const double x) {register int a=6; register double b=x,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); pc('.'); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);}
inline void write(const pair<int,double>x) {register int a=x.first; if(a<7) {register double b=x.second,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); a&&(pc('.')); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);} else cout<<fixed<<setprecision(a)<<x.second;}
inline void write(const char x) {pc(x);}
inline void write(const bool x) {pc(x?49:48);}
inline void write(char *x) {fputs(x,stdout);}
inline void write(const char *x) {fputs(x,stdout);}
inline void write(const string &x) {fputs(x.c_str(),stdout);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y) {write(x),write(y...);}
代码长度
写在前面
-
如果您希望更加舒适无误的使用此快读快写,请仔细阅读如下内容。
-
有更加简洁的 1.0 版本,但是只支持对于整形的输入输出,读入效率与 2.5 版本完全相同,输出效率略低于 2.5 版本,详见 一些工具。
-
2.5 版本的存在意义在于在保证了相对较高的效率同时更加方便使用,以避免了需要输出非整形时还需要使用其他读写方式从而降低了效率且不方便使用的问题,而非最求最快,所以在极端卡常环境下作用可能没有一些“超级快读”显著。
-
绝大多数函数基于
getchar_unlocked
和putchar_unlocked
实现,没有采用fread
和fwrite
的原因是不想让输入输出占用格外的空间,所以效率方面有一定的保证但难以达到最快。上述仅针对于
linux
环境,对于windows
环境则基于_getchar_nolock
和_putchar_nolock
,此函数与getchar_unlocked
和putchar_unlocked
类似,效率均高于getchar
和putchar
。不管什么评测环境,请勿将其和解绑
cin
、cout
同时使用,原因可自行上网查询。同时,对于每一个函数,请保证您的传参是合法的,否则出现死循环等问题一概不负责。
-
对于
double
的输出是存在精度误差的,但是在保留小数点后6 位及以内的数据正确率约为100\% ,但是在7 位及以上时,即使使用printf
也会存在精度问题,但为了避免不必要的麻烦,对于7 位及以上的,我使用了cout<<fixed<<setprecision(a)<<b
,a 为保留位数,b 为输出的数,这个cout
是没法解绑的,但是效率没有比解绑的低太多,所以当其出现精度问题时并不是快写的问题。 -
如果将浮点数输入到一个整形或
bool
类型的变量中,使用cin
会自动将其向零取整作为结果,使用scanf
后果比较严重,此快读为了保证效率,在该方面不支持类似自动取整的功能且可能出现输入结果异常的问题,如有该方面需要请先输入为double
类型再转为整形。 -
经过调试,目前应该没有大的问题,若存在问题请及时指出,我会尽快改正,后续可能要优化代码所以可能会更新版本。
功能介绍
-
支持对于整形的输入,您可以使用
read(x)
读入一个整形x ,效率保证通过 P10815【模板】快速读入。 -
支持对于
double
的输入,不支持对于float
的输入,您可以使用read(x)
输入一个double
类型的x ,效率与对整形输入类似。 -
支持对于
bool
的输入,您可以使用read(x)
输入一个整形x ,若您输入的x 为合法的整形,其将自动识别为bool
类型,若为0 或-0 则识别为false
,否则为true
,效率与对整形输入效率类似。 -
支持对于字符的输入,自动过滤空格与换行,您可以使用
read(x)
读入一个字符x ,如果您想读入一个空格或换行请使用gatchar
等自带函数。 -
支持对于字符串的输入,包括一维
char
数组(char*
)和string
,您可以使用read(x)
输入一个字符串x 。效率方面,基于
O(len) 次的getchar_unlocked
,约为scanf
的2 倍,约为不解绑cin
的10 倍,但只有解绑cin
的\dfrac{1}{2} ,目前没有想到更好的解决方案,欢迎大家指出。同时不管什么读入方式对于字符串的输入效率都是极高的,不必担心输入字符串被卡常,实测10^9 个字符的量只需要0.5s 左右。 -
支持多个类型同时输入,具体的,您可以使用
read(a,b,c,...)
依次进行a,b,c 等若干个任意类型的输入。特殊的,对于一个
char*
类型的s ,您可以正常的将s 传入该函数,但不支持将其类似与s+1 的类型传入上述函数,因为我对于输入类型均进行了取地址,而s+1 本身就是一个地址,不能够重复两次取地址,存在解决方案但是并不优美,因为我并不想在每个变量前都手动加一个取地址,所以在不影响其他类型输入的手感的前提下,请单独使用read(s+1)
。 -
支持对于整形的输出,您可以使用
write(x)
输出一个整形x ,相对于 1.0 版本的提升在于将其从递归改为递推,从而提高了效率,效率高于解绑的cout
。 -
支持对于
double
的输出,不支持对于float
的输出。-
您可以使用
write(x)
输出一个保留小数点后6 位的double
类型的x 。 -
您可以使用
write(make_pair(a,b))
输出一个保留小数点后a 位的double
类型的b ,推荐将define
掉make_pair
以避免过于麻烦。 -
如果您想输出小数点后不是保留
6 位的浮点数,可以使用上述的第二种方案;或者将源码中将void write(double x)
函数中a 的值进行修改,如果整个程序需要输出的浮点数保留位数均相同,推荐使用后者从而更加方便和效率。
效率方面,因为
double
不可以进行取模运算,所以采用了floor
以实现\bmod 10 的效果,所以效率不如整形的输出,这是无法避免的,但是效率依然高于printf
和解绑的cout
。精度问题已在前面阐述。
-
-
支持对于
bool
的输出,您可以使用write(x)
输出一个bool
类型的x ,若x=true 输出1
,若x=false 输出0
。 -
支持对于字符的输出,您可以使用
write(x)
输出一个字符x ,本质就是把x 直接putchar_unlocked
出去。 -
支持对于字符串的输出,包括一维
char
数组(char*
)和string
,您可以使用write(x)
输出一个字符串x 。此函数不基于
getchar_unlocked
而是基于fputs
,因为对于一个一个字符的输出效率是严格低于整个输出的,使用fputs
的效率极高,和解绑的cout
几乎相同。也正因为使用了
fputs
,若您想要输出的是一个char*
,请保证其末尾字符为\0
,此处输出的字符串中可以有空格等特殊字符。另外,我对字符串的输出进行了取地址操作,不会占用格外的空间。
-
支持对于
const char*
和const string
的输出,您可以使用write(x)
输出一个上述类型的x 。例如,您可以使用
write("Charlie")
输出一个Charlie
,此处的Charlie
为const char*
类型。效率与实现方面与对
char*
和string
的输出相同。 -
支持多个类型同时输出,具体的,您可以使用
write(a,b,c,...)
依次进行a,b,c 等若干个任意类型的输出。此函数不存在取地址问题,可以放心使用。
-
原则上可以还可以加入输入一整行包括空格字符串、输入一整个数组等其他功能,但我认为那是没有什么意义的,例如若有输入一整行字符串的要求,请自行使用
fgets
等函数。
更新说明
-
2.1 版本,增加了对于
const char*
和const string
的输出,经过测试发现 2.0 版本不支持类似write("Charlie")
的操作,因为此处的Charlie
为const char*
类型,同时发现对于const string
有着同样的问题,现已修复,对于其余类型的变量没有此类问题。——更新时间:2025年01月11日。
-
2.2 版本,通过
define
解决了不兼容 windows 的问题。——更新时间:2025年01月23日。
-
2.4 版本,感谢 @LionBlaze、@yangfengzhao 等人的提醒,共做出了以下五点修改:
-
对于 windows 环境下采用了更快速的
_getchar_nolock
和_putchar_nolock
,解决了在 windows 环境下效率低下的问题。 -
对于大多数输出函数的参数添加了
const
,更加符合规范并提高安全性。 -
修复了对于
char
、char*
、string
类型的输入无法识别\t
的问题。 -
对于
char*
的输入进行了一些效率方面的优化,小幅度提升效率,但尚未解决无法与其他类型同时传入的问题。 -
进行了一些写法上的优化,去除部分冗余函数,减少代码长度。
因为在同一天进行了多次修改,故跳过 2.3 进入 2.4 版本。
——更新时间:2025年02月05日。
-
-
2.5 版本,共做出以下三点修改:
-
修复了对于有前导零情况下
bool
输入结果异常的问题,并发现针对bool
的输入函数是没有意义的,现对于bool
的输入和整形共用一个函数,正常使用时返回值正常。 -
去除对于
double
的输出函数中的冗余部分,提高了效率,简短了代码长度。 -
在「写在前面」中更新了有关将浮点数输入到整形或
bool
类型的变量中可能出现的问题,请仔细观看。
——更新时间:2025年02月10日。
-