C++快速文件输入输出

转载请注明:

仰望高端玩家的小清新 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.cpp

由于内联函数可以写入到头文件中,因此可以将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.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 }

猜你喜欢

转载自www.cnblogs.com/luruiyuan/p/10518374.html