用c++快速读取文本文件
Fast textfile reading in c++
我目前正在用c++编写一个程序,其中包括读取许多大型文本文件。每行大约有4000000行,在极端情况下每行有4000个或更多字符。为了测试,我使用ifstream和cplusplus.com提供的实现读取了其中一个文件。大约花了60秒,这太长了。现在我想知道,有没有一种简单的方法可以提高阅读速度?
编辑:我使用的代码或多或少是这样的:
string tmpString;
ifstream txtFile(path);
if(txtFile.is_open())
{
while(txtFile.good())
{
m_numLines++;
getline(txtFile, tmpString);
}
txtFile.close();
}
编辑2:我读到的文件只有82 MB大。我主要说它可以达到4000,因为我认为可能需要知道才能进行缓冲。
编辑3:谢谢大家的回答,但考虑到我的问题,似乎没有太大的改进空间。我必须使用readline,因为我想计算行数。将ifstream实例化为二进制也不会加快读取速度。我会尽可能多地将其并行化,这至少应该有效。
编辑4:很明显,我有一些事情可以做。非常感谢你在这件事上花了这么多时间,我非常感激
更新:请务必检查初始答案下方的(令人惊讶的)更新
内存映射文件对我很有用1:
#include <boost/iostreams/device/mapped_file.hpp> // for mmap
#include <algorithm> // for std::find
#include <iostream> // for std::cout
#include <cstring>
int main()
{
boost::iostreams::mapped_file mmap("input.txt", boost::iostreams::mapped_file::readonly);
auto f = mmap.const_data();
auto l = f + mmap.size();
uintmax_t m_numLines = 0;
while (f && f!=l)
if ((f = static_cast<const char*>(memchr(f, 'n', l-f))))
m_numLines++, f++;
std::cout << "m_numLines = " << m_numLines << "n";
}
这应该很快。
更新
如果它能帮助您测试这种方法,这里有一个版本,直接使用mmap
而不是使用Boost:在Coliru 上实时查看
#include <algorithm>
#include <iostream>
#include <cstring>
// for mmap:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
const char* map_file(const char* fname, size_t& length);
int main()
{
size_t length;
auto f = map_file("test.cpp", length);
auto l = f + length;
uintmax_t m_numLines = 0;
while (f && f!=l)
if ((f = static_cast<const char*>(memchr(f, 'n', l-f))))
m_numLines++, f++;
std::cout << "m_numLines = " << m_numLines << "n";
}
void handle_error(const char* msg) {
perror(msg);
exit(255);
}
const char* map_file(const char* fname, size_t& length)
{
int fd = open(fname, O_RDONLY);
if (fd == -1)
handle_error("open");
// obtain file size
struct stat sb;
if (fstat(fd, &sb) == -1)
handle_error("fstat");
length = sb.st_size;
const char* addr = static_cast<const char*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0u));
if (addr == MAP_FAILED)
handle_error("mmap");
// TODO close fd at some point in time, call munmap(...)
return addr;
}
更新
通过查看GNU核心实用程序wc
的源代码,我发现了最后一点性能。令我惊讶的是,使用以下改编自wc
的(大大简化的)代码,大约84%的时间运行和上面的内存映射文件:
static uintmax_t wc(char const *fname)
{
static const auto BUFFER_SIZE = 16*1024;
int fd = open(fname, O_RDONLY);
if(fd == -1)
handle_error("open");
/* Advise the kernel of our access pattern. */
posix_fadvise(fd, 0, 0, 1); // FDADVICE_SEQUENTIAL
char buf[BUFFER_SIZE + 1];
uintmax_t lines = 0;
while(size_t bytes_read = read(fd, buf, BUFFER_SIZE))
{
if(bytes_read == (size_t)-1)
handle_error("read failed");
if (!bytes_read)
break;
for(char *p = buf; (p = (char*) memchr(p, 'n', (buf + bytes_read) - p)); ++p)
++lines;
}
return lines;
}
1请参阅这里的基准测试:如何快速解析C++中的空格分隔浮点?
4000*400000=1.6 GB如果您的硬盘不是SSD,则可能会获得约100 MB/s的顺序读取。这是I/O中的16秒。
由于你没有详细说明你使用的具体代码,也没有详细说明如何解析这些文件(你需要逐行读取吗,系统有很多RAM吗?你能把整个文件读入一个大的RAM缓冲区,然后解析它吗?)你几乎无法加快这一过程。
当按顺序读取文件时,内存映射文件不会提供任何性能改进。也许手动解析大块的新行而不是使用"getline"会有所改进。
编辑完成一些学习后(感谢@sehe)。以下是我可能使用的内存映射解决方案。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
int main() {
char* fName = "big.txt";
//
struct stat sb;
long cntr = 0;
int fd, lineLen;
char *data;
char *line;
// map the file
fd = open(fName, O_RDONLY);
fstat(fd, &sb);
//// int pageSize;
//// pageSize = getpagesize();
//// data = mmap((caddr_t)0, pageSize, PROT_READ, MAP_PRIVATE, fd, pageSize);
data = mmap((caddr_t)0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
line = data;
// get lines
while(cntr < sb.st_size) {
lineLen = 0;
line = data;
// find the next line
while(*data != 'n' && cntr < sb.st_size) {
data++;
cntr++;
lineLen++;
}
/***** PROCESS LINE *****/
// ... processLine(line, lineLen);
}
return 0;
}
Neil Kirk,不幸的是,我无法回复您的评论(声誉不够),但我在ifstream字符串流上进行了性能测试,逐行读取文本文件的性能完全相同。
std::stringstream stream;
std::string line;
while(std::getline(stream, line)) {
}
这需要在106MB的文件上花费1426ms。
std::ifstream stream;
std::string line;
while(ifstream.good()) {
getline(stream, line);
}
在同一个文件上,这需要1433ms。
以下代码反而更快:
const int MAX_LENGTH = 524288;
char* line = new char[MAX_LENGTH];
while (iStream.getline(line, MAX_LENGTH) && strlen(line) > 0) {
}
在同一个文件上,这需要884ms。这只是有点棘手,因为您必须设置缓冲区的最大大小(即输入文件中每行的最大长度)。
作为一个在竞争编程方面有点背景的人,我可以告诉你:至少对于整数解析这样的简单事情,C的主要成本是锁定文件流(默认情况下,这是为多线程完成的)。请改用unlocked_stdio
版本(fgetc_unlocked()
、fread_unlocked()
)。对于C++,常见的说法是使用std::ios::sync_with_stdio(false)
,但我不知道它是否像unlocked_stdio
一样快。
作为参考,这里是我的标准整数解析代码。它比scanf快很多,正如我所说的,主要是因为没有锁定流。对我来说,它和我以前使用的最好的手动编码mmap或自定义缓冲版本一样快,没有疯狂的维护债务。
int readint(void)
{
int n, c;
n = getchar_unlocked() - '0';
while ((c = getchar_unlocked()) > ' ')
n = 10*n + c-'0';
return n;
}
(注意:只有当任意两个整数之间恰好有一个非数字字符时,此选项才有效)。
当然,如果可能的话,避免内存分配。。。
是否必须同时读取所有文件?(例如,在应用程序开始时)
如果需要,请考虑将操作并行化。
无论哪种方式,都可以考虑使用二进制流,或者对数据块进行未转发的读取。
使用Random file access
或binary mode
。对于sequence来说,这是很大的,但它仍然取决于你在读什么。
- 文本文件中的单词链表
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 如何将内容数组写入文本文件?
- 无法通过空白将文本文件行分隔为矢量
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- C++将文本文件中的数据读取到结构数组中
- 在指针的帮助下,文本文件中单词的频率
- 将字符指针十六进制转换为字符串并保存在文本文件C++中
- 将值从二维数组输出到文本文件
- 如何在c++中从文本文件中逐行读取整数
- 从文本文件中读取时钟时间和事件时间并进行处理
- 如何从文本文件中读取值和数组
- 如何在C++中确定文本文件中的元素是字符还是数字
- C++试图读取一个文件并输出到另一个文本文件
- 如何通过套接字将文本文件的内容从服务器发送到客户端
- 已修改的LinkedList未在文本文件本身中更新
- C++文本文件的获取线
- 为什么C++的文件 I/O 在读取文本文件时忽略初始空行?我怎样才能让它不这样做?
- 从二进制文件中抓取文本时,为什么 xdg_vtnr=8 是我的结果
- Windows XP在C++中启动时读取文本文件的速度较慢;预取