模板 - 基于 fread /fwrite 的快读快写

  • 有符号 / 无符号整数
  • 字符
  • C 风格字符串
  • std::string
  • std::pair
  • std::tuple

的输入输出,支持流式写法

https://cplib.tifa-233.com/src/code/util/fastio.hpp 存放了笔者对该算法 / 数据结构的最新实现,建议前往此处查看相关代码

实际上没什么应用价值,开了 O2、关闭流同步、解绑、不使用 std::endl 等的 std::cin / std::cout 的性能已经和本模板差不多了,就当作模板偏特化方面的练习了

经测试,当数据规模在 1e7 及以上时,该模板效率提升显著

  • 仅在 GCC 下测试过,推荐用于 C++17 及以上
  • 为保障效率,读入过程中遇到文件尾是未预期的,绝大多数情况下读入过程中遇到文件尾会死循环

Show code

FastIO.hppview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
namespace fast_io {
namespace type_traits {
template <class Tp>
constexpr bool is_char_v =
std::is_same_v<Tp, char> || std::is_same_v<Tp, signed char> ||
std::is_same_v<Tp, unsigned char>;
template <class Tp>
constexpr bool is_int_v =
(std::is_integral_v<Tp> && std::is_signed_v<Tp> && !is_char_v<Tp>) ||
std::is_same_v<Tp, __int128_t>;
template <class Tp>
constexpr bool is_uint_v =
(std::is_integral_v<Tp> && std::is_unsigned_v<Tp> && !is_char_v<Tp>) ||
std::is_same_v<Tp, __uint128_t>;
template <class Tp>
using make_uint_t = typename std::conditional_t<
(std::is_same_v<Tp, __int128_t> || std::is_same_v<Tp, __uint128_t>),
std::common_type<__uint128_t>,
typename std::conditional_t<std::is_signed_v<Tp>,
std::make_unsigned<Tp>,
std::common_type<Tp>>>::type;
} // namespace type_traits

//! Will enter a dead loop if EOF occured during reading
template <size_t BUFFER_SIZE>
class FastIn {
using self = FastIn<BUFFER_SIZE>;

protected:
char buffer_[BUFFER_SIZE], *now_ = buffer_, *end_ = buffer_;
FILE *file_;

public:
explicit FastIn(FILE *file = stdin): file_(file) {}

char fetch() {
return now_ == end_ &&
(end_ = (now_ = buffer_) + fread(buffer_, 1, BUFFER_SIZE, file_),
now_ == end_) ?
EOF :
*(now_)++;
}
char visit() {
return now_ == end_ &&
(end_ = (now_ = buffer_) + fread(buffer_, 1, BUFFER_SIZE, file_),
now_ == end_) ?
EOF :
*(now_);
}
void set_file(FILE *file) {
file_ = file;
now_ = end_ = buffer_;
}
bool iseof() { return visit() == EOF; }

template <class Tp, std::enable_if_t<type_traits::is_int_v<Tp>> * = nullptr>
self &read(Tp &n) {
bool is_neg = false;
char ch = fetch();
while (!isdigit(ch)) {
is_neg |= ch == '-';
ch = fetch();
}
n = 0;
while (isdigit(ch)) {
(n *= 10) += ch & 0x0f;
ch = fetch();
}
if (is_neg) n = -n;
return *this;
}
template <class Tp, std::enable_if_t<type_traits::is_uint_v<Tp>> * = nullptr>
self &read(Tp &n) {
char ch = fetch();
while (!isdigit(ch)) ch = fetch();
n = 0;
while (isdigit(ch)) {
(n *= 10) += ch & 0x0f;
ch = fetch();
}
return *this;
}
//! ignore cntrl and space
template <class Tp, std::enable_if_t<type_traits::is_char_v<Tp>> * = nullptr>
self &read(Tp &n) {
while (!isgraph(n = fetch()))
;
return *this;
}
self &read(char *n) {
char *n_ = n;
while (!isgraph(*n_ = fetch()))
;
while (isgraph(*(++n_) = fetch()))
;
*n_ = '\0';
return *this;
}
self &read(std::string &n) {
n.clear();
char n_;
while (!isgraph(n_ = fetch()))
;
n.push_back(n_);
while (isgraph(n_ = fetch())) n.push_back(n_);
return *this;
}
template <class Tp, class Up>
self &read(std::pair<Tp, Up> &p) {
return read(p.first).read(p.second);
}
template <typename... Ts>
self &read(std::tuple<Ts...> &p) {
std::apply([&](Ts &...targs) { ((read(targs)), ...); }, p);
return *this;
}

self &getline(char *n) {
char *n_ = n;
while (!isprint(*n_ = fetch()))
;
while (isprint(*(++n_) = fetch()))
;
*n_ = '\0';
return *this;
}
self &getline(std::string &n) {
char n_;
while (!isprint(n_ = fetch()))
;
n.push_back(n_);
while (isprint(n_ = fetch())) n.push_back(n_);
return *this;
}

//! NOT ignore cntrl and space
template <class Tp, std::enable_if_t<type_traits::is_char_v<Tp>> * = nullptr>
self &strict_read(Tp &n) {
n = fetch();
return *this;
}

template <class Tp>
self &operator>>(Tp &val) {
return read(val);
}
};

template <size_t BUFFER_SIZE, size_t INT_BUFFER_SIZE>
class FastOut {
using self = FastOut<BUFFER_SIZE, INT_BUFFER_SIZE>;

private:
char int_buffer_[INT_BUFFER_SIZE], *now_ib_ = int_buffer_;

protected:
FILE *file_;
char *now_, buffer_[BUFFER_SIZE];
const char * const end_ = buffer_ + BUFFER_SIZE;

public:
explicit FastOut(FILE *file = stdout): file_(file), now_(buffer_) {}

self &operator=(const self &rhs) {
file_ = rhs.file_;
now_ = buffer_ + (rhs.now_ - rhs.buffer_);
memcpy(buffer_, rhs.buffer_, sizeof(*buffer_) * (rhs.now_ - rhs.buffer_));
return *this;
}
FastOut(const self &rhs) { *this = rhs; }

~FastOut() { flush(); }

void flush() {
fwrite(buffer_, 1, now_ - buffer_, file_);
now_ = buffer_;
}
void rebind(FILE *file) { file_ = file; }

template <class Tp, std::enable_if_t<type_traits::is_char_v<Tp>> * = nullptr>
self &write(const Tp &n) {
if (now_ == end_) flush();
*(now_++) = n;
return *this;
}
self &write(const char *n) {
size_t len = strlen(n), l_;
const char *n_ = n;
while (now_ + len >= end_) {
l_ = end_ - now_;
memcpy(now_, n_, l_);
now_ += l_;
n_ += l_;
len -= l_;
flush();
}
memcpy(now_, n_, len);
now_ += len;
return *this;
}
template <class Tp, std::enable_if_t<type_traits::is_int_v<Tp>> * = nullptr>
self &write(Tp n) {
if (n < 0) {
write('-');
n = -n;
}
return write(static_cast<typename type_traits::make_uint_t<Tp>>(n));
}
template <class Tp, std::enable_if_t<type_traits::is_uint_v<Tp>> * = nullptr>
self &write(Tp n) {
now_ib_ = int_buffer_ + INT_BUFFER_SIZE - 1;
do { *(--(now_ib_)) = char(n % 10) | '0'; } while (n /= 10);
return write(now_ib_);
}
self &write(const std::string &str) { return write(str.c_str()); }
template <class Tp, class Up>
self &write(const std::pair<Tp, Up> &p) {
return write(p.first).space().write(p.second);
}
template <typename... Ts>
self &write(const std::tuple<Ts...> &p) {
std::apply(
[&](Ts const &...targs) {
std::size_t n{0};
((write(targs).space_if(++n != sizeof...(Ts))), ...);
},
p);
return *this;
}

self &linebreak() { return write('\n'); }
self &linebreak_if(bool flag) { return flag ? linebreak() : *this; }
self &space() { return write(' '); }
self &space_if(bool flag) { return flag ? space() : *this; }

template <class Tp>
self &operator<<(const Tp &val) {
return write(val);
}
};

const std::size_t BUFFER_SIZE = (1 << 21) + 5;
FastIn<BUFFER_SIZE> fastin;
FastOut<BUFFER_SIZE, 21> fastout;
} // namespace fast_io
using fast_io::fastin;
using fast_io::fastout;