转载请注明:
仰望高端玩家的小清新 http://www.cnblogs.com/luruiyuan/
C语言可以获得接近汇编的性能,而输入输出常常是最为耗时的过程,因此可以使用 C 语言中的 fread 和 fwrite 来获得最高的读写性能。
例如,可以将其写在源码文件中直接使用:不建议使用这种方式
1 #include <cstdio> // EOF 的定义 2 #include <cassert> // assert 函数定义 3 #include <sys/stat.h> // 读取文件状态 4 5 /** 6 * 快速输入输出模板 7 * 使用 fread 和 fwrite 获得高于 scanf 和 printf 的文件 I/O 性能 8 */ 9 namespace FastIO { 10 11 // 快速输入 12 namespace in{ 13 const int inputBuffSize = 67108864; // 输入缓冲区大小 64MB 14 char buff[inputBuffSize], *ptr = NULL, *pend = NULL; 15 FILE *stream = NULL; 16 int filesize, readsize, itemsize, maxcnt, maxbytes; // 文件大小字节数, 已读字节数 17 18 // 指定文件路径, 并根据文件头获取文件大小 19 inline int getsize(const char *path){ 20 struct stat statbuff; 21 stat(path, &statbuff); 22 return statbuff.st_size; 23 } 24 25 /* 初始化 Fast in 参数 26 * (const char*) path: 文件路径 27 * (const char*) mode: 文件打开模式 28 * (const int) element_size=1: 文件读取的块大小, 默认为 1 字节, 缓冲区大小为 64M 29 */ 30 inline void init(const char *path, const char *mode="rb", const int element_size=1){ 31 assert((stream == NULL) && (stream = fopen(path, mode)) != NULL); 32 filesize = getsize(path); 33 readsize = 0; 34 itemsize = element_size; 35 maxcnt = inputBuffSize / element_size; // buffer 整块读取时可容纳的最大块数 36 maxbytes = (inputBuffSize / element_size) * element_size; // buffer 整块读取时可容纳的最大字节数 37 ptr = pend = NULL; 38 } 39 40 /** 41 * 读取流 stream 中的下一个字符, 当缓冲区内容读取完毕后进行下一次I/O 42 * 返回EOF(-1)表示读取完成, 返回-2表示达到文件尾之前出错 43 */ 44 inline char nextchar(){ 45 if (readsize >= filesize) return EOF; // 文件读取完成 46 if (ptr >= pend){ 47 int realbytes = itemsize * fread(buff, itemsize, maxcnt, stream); // fread返回实际读取的块数 48 if (realbytes < maxbytes && realbytes + readsize < filesize) return -2; // 读取出错 返回-2 49 ptr = buff; // 重置首尾指针 50 pend = buff + realbytes; 51 } 52 return readsize++, *ptr++; 53 } 54 55 // 读取一个整数, true 表示读取成功, false 表示读取失败 56 inline bool read(int &x){ 57 char c = nextchar(); 58 while (c >= 0 && c != '-' && (c < '0' || c > '9')) c = nextchar(); 59 if (c < 0) return false; // c == -1 (EOF): 到达文件尾, c == -2: 读取出错 60 int sign = (c == '-') ? -1 : 1; // 正负号 61 x = (c == '-') ? 0 : c - '0'; 62 while (c = nextchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; 63 x *= sign; 64 return true; 65 } 66 67 // 读取一个长度为 n 的整数 tuple, 如 (1, -2, 31), true 表示读取成功, false 表示失败 68 inline bool read(int *p, const int n){ 69 for (int *end = p + n; p < end; ++p) if (!read(*p)) return false; 70 return true; 71 } 72 73 // 关闭输入流释放资源 74 inline int close(){ 75 int ret = fclose(stream); 76 filesize = readsize = itemsize = maxcnt = 0; 77 ptr = pend = NULL; 78 stream = NULL; 79 return ret; 80 } 81 } 82 83 // 快速输出 84 namespace out{ 85 const int outputBuffSize = 67108864; // 输出缓冲区大小 64MB 86 char buff[outputBuffSize], *ptr = NULL, *pend = NULL; 87 FILE *stream = NULL; 88 int itemsize, maxbytes; // 写入的块大小, 整块存放时缓存的最大字节数 89 90 inline void init(const char *path, const char *mode="wb", const int element_size=1){ 91 assert(stream == NULL && (stream = fopen(path, mode)) != NULL); 92 itemsize = element_size; 93 maxbytes = (outputBuffSize / element_size) * element_size; // 输出缓冲的最大字节数 94 ptr = buff; 95 pend = buff + maxbytes; 96 } 97 98 // 冲刷缓冲区 99 inline void flush(){ 100 fwrite(buff, itemsize, (ptr - buff) / itemsize, stream); 101 ptr = buff; // 调整首指针 102 fflush(stream); 103 } 104 105 // 写入一个字符到文件中 106 inline void write(const char &c){ 107 if (ptr >= pend) flush(); 108 *ptr++ = c; 109 } 110 111 // 写一个字符串到文件中 112 inline void write(const char *s){ 113 for(; *s; ++s) write(*s); // 读取到字符串尾部时 '\0' ASCII为0 114 } 115 116 // 写入一个整数到文件中 117 inline void write(int x){ 118 char buf[20], *p = buf; 119 if (x == 0) write('0'); 120 if (x < 0) write('-'), x = -x; 121 while (x > 0) *p++ = x % 10 + '0', x /= 10; 122 while (p > buf) write(*--p); 123 } 124 125 // 写入一个整型tuple到文件中 如 (32, -1, 14), drop_end 控制是否输出 end 符号 126 inline void write(const int *p, const int n, const char *left="(", const char *right=")", 127 const char *split=", ", const char *end="\n", const bool drop_end=false){ 128 write(left); 129 for (const int *ptrend = p + n - 1; p < ptrend; ++p){ 130 write(*p); 131 write(split); 132 } 133 write(*++p); 134 write(right); 135 if (!drop_end) write(end); 136 } 137 138 // 冲刷缓冲并关闭输出流释放资源 139 inline int close(){ 140 if (ptr > buff) flush(); 141 int ret = fclose(stream); 142 ptr = pend = NULL; 143 stream = NULL; 144 return ret; 145 } 146 } 147 }
由于内联函数可以写入到头文件中,因此可以将FastIO的实现放入 FastIO.h 中,然后在调用时通过 include 包含即可,FastIO.h 如下,其中包含了对于string类型的支持:强烈建议使用这种方式
1 #pragma once 2 #include <cstdio> // EOF 的定义 3 #include <string> // string 类型的支持 4 #include <cassert> // assert 函数定义 5 #include <sys/stat.h> // 读取文件状态 6 7 #define inputBuffSize (67108864) // 输入缓冲区大小 64MB 8 #define outputBuffSize (67108864) // 输入缓冲区大小 64MB 9 10 namespace FastIO { // 由于头文件中可以定义内联函数, 因此将FastIO定义在头文件中便于使用 11 // 快速输入 12 namespace in{ 13 char buff[inputBuffSize], *ptr = NULL, *pend = NULL; 14 FILE *stream = NULL; 15 int filesize, readsize, itemsize, maxcnt, maxbytes; // 文件大小字节数, 已读字节数 16 17 // 指定文件路径, 并根据文件头获取文件大小 18 inline int getsize(const char *path){ 19 struct stat statbuff; 20 stat(path, &statbuff); 21 return statbuff.st_size; 22 } 23 24 // 初始化 Fast in 参数 25 // (const char*) path: 文件路径 26 // (const char*) mode: 文件打开模式 27 // (const int) element_size=1: 文件读取的块大小, 默认为 1 字节, 缓冲区大小为 64M 28 inline void init(const char *path, const char *mode="rb", const int element_size=1){ 29 assert((stream == NULL) && (stream = fopen(path, mode)) != NULL); 30 filesize = getsize(path); 31 readsize = 0; 32 itemsize = element_size; 33 maxcnt = inputBuffSize / element_size; // buffer 整块读取时可容纳的最大块数 34 maxbytes = (inputBuffSize / element_size) * element_size; // buffer 整块读取时可容纳的最大字节数 35 ptr = pend = NULL; 36 } 37 38 // 初始化 Fast in 参数 39 // (const string) path: 文件路径 40 // (const char*) mode: 文件打开模式 41 // (const int) element_size=1: 文件读取的块大小, 默认为 1 字节, 缓冲区大小为 64M 42 inline void init(const std::string &path, const char *mode="rb", const int element_size=1){ 43 init(path.c_str(), mode, element_size); 44 } 45 46 // 读取流 stream 中的下一个字符, 当缓冲区内容读取完毕后进行下一次I/O 47 // 返回EOF(-1)表示读取完成, 返回-2表示达到文件尾之前出错 48 inline char nextchar(){ 49 if (readsize >= filesize) return EOF; // 文件读取完成 50 if (ptr >= pend){ 51 int realbytes = itemsize * fread(buff, itemsize, maxcnt, stream); // fread返回实际读取的块数 52 if (realbytes < maxbytes && realbytes + readsize < filesize) return -2; // 读取出错 返回-2 53 ptr = buff; // 重置首尾指针 54 pend = buff + realbytes; 55 } 56 return readsize++, *ptr++; 57 } 58 59 // 读取一个字符, 读取失败则不改变char, 否则改变char c的值 60 inline bool read(char &c){ 61 char tmp = nextchar(); // tmp == -1 (EOF): 到达文件尾, tmp == -2: 读取出错 62 return tmp < 0 ? false: (c = tmp, true); 63 } 64 65 // 读取一个整数, true 表示读取成功, false 表示读取失败 66 inline bool read(int &x){ 67 char c = nextchar(); 68 while (c >= 0 && c != '-' && (c < '0' || c > '9')) c = nextchar(); 69 if (c < 0) return false; // c == -1 (EOF): 到达文件尾, c == -2: 读取出错 70 int sign = (c == '-') ? -1 : 1; // 正负号 71 x = (c == '-') ? 0 : c - '0'; 72 while (c = nextchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; 73 x *= sign; 74 return true; 75 } 76 77 // 读取一个长度为 n 的整数 tuple, 如 (1, -2, 31), true 表示读取成功, false 表示失败 78 inline bool read(int *p, const int n){ 79 for (int *end = p + n; p < end; ++p) if (!read(*p)) return false; 80 return true; 81 } 82 83 // 关闭输入流释放资源 84 inline int close(){ 85 int ret = fclose(stream); 86 filesize = readsize = itemsize = maxcnt = 0; 87 ptr = pend = NULL; 88 stream = NULL; 89 return ret; 90 } 91 } 92 93 // 快速输出 94 namespace out{ 95 char buff[outputBuffSize], *ptr = NULL, *pend = NULL; 96 FILE *stream = NULL; 97 int itemsize, maxbytes; // 写入的块大小, 整块存放时缓存的最大字节数 98 99 // 初始化 Fast out 参数 100 // (const char*) path: 文件路径 101 // (const char*) mode: 文件打开模式 102 // (const int) element_size=1: 文件读取的块大小, 默认为 1 字节, 缓冲区大小为 64M 103 inline void init(const char *path, const char *mode="wb", const int element_size=1){ 104 assert(stream == NULL && (stream = fopen(path, mode)) != NULL); 105 itemsize = element_size; 106 maxbytes = (outputBuffSize / element_size) * element_size; // 输出缓冲的最大字节数 107 ptr = buff; 108 pend = buff + maxbytes; 109 } 110 111 // 初始化 Fast out 参数 112 // (const string) path: 文件路径 113 // (const char*) mode: 文件打开模式 114 // (const int) element_size=1: 文件读取的块大小, 默认为 1 字节, 缓冲区大小为 64M 115 inline void init(const std::string &path, const char *mode="wb", const int element_size=1){ 116 init(path.c_str(), mode, element_size); 117 } 118 119 // 冲刷缓冲区 120 inline void flush(){ 121 fwrite(buff, itemsize, (ptr - buff) / itemsize, stream); 122 ptr = buff; // 调整首指针 123 fflush(stream); 124 } 125 126 // 写入一个字符到文件中 127 inline void write(const char &c){ 128 if (ptr >= pend) flush(); 129 *ptr++ = c; 130 } 131 132 // 写一个字符串到文件中 133 inline void write(const char *s){ 134 for(; *s; ++s) write(*s); // 读取到字符串尾部时 '\0' ASCII为0 135 } 136 137 // 写一个 string 类型的字符串到文件中 138 inline void write(const std::string &s){ 139 write(s.c_str()); 140 } 141 142 // 写入一个整数到文件中 143 inline void write(int x){ 144 char buf[20], *p = buf; 145 if (x == 0) write('0'); 146 if (x < 0) write('-'), x = -x; 147 while (x > 0) *p++ = x % 10 + '0', x /= 10; 148 while (p > buf) write(*--p); 149 } 150 151 // 写入一个整型tuple到文件中 如 (32, -1, 14), drop_end 控制是否输出 end 符号 152 inline void write(const int *p, const int n, const char *left="(", const char *right=")", 153 const char *split=", ", const char *end="\n", const bool drop_end=false){ 154 write(left); 155 for (const int *ptrend = p + n - 1; p < ptrend; ++p){ 156 write(*p); 157 write(split); 158 } 159 write(*++p); 160 write(right); 161 if (!drop_end) write(end); 162 } 163 164 // 冲刷缓冲并关闭输出流释放资源 165 inline int close(){ 166 if (ptr > buff) flush(); 167 int ret = fclose(stream); 168 ptr = pend = NULL; 169 stream = NULL; 170 return ret; 171 } 172 } 173 }
调用时,只需将头文件 FastIO.h 引入,然后使用其命名空间 FastIO::in, FastIO::out
简单的使用方式如下,这里假定了 main.cpp 和 FastIO.h 在同一个目录下:(如果不在,需要用相对路径)
1 // 在 main.cpp 中引入FastIO.h 和 命名空间 FastIO 即可 2 3 #include"FastIO.h" 4 using namespace FastIO; 5 6 int main(int argc, char *argv[]){ 7 int buff[10]; 8 // 初始化写入文件流 9 out::init("out.txt", "wb"); 10 // 测试5个数一组的tuple读取和写入 11 in::init("in.txt", "rb"); 12 while(in::read(buff, 5)) out::write(buff, 5); 13 // 释放读文件资源 14 in::close(); 15 // 释放写文件资源,冲刷缓冲区 16 out::close(); 17 }