1

我正在逐行读取文件,并从中提取整数。一些值得注意的点:

  • 输入文件不是二进制文件;
  • 我无法将整个文件加载到内存中;
  • 文件格式(仅整数,由一些分隔符分隔):

    x1 x2 x3 x4 ...
    y1 y2 y3 ...
    z1 z2 z3 z4 z5 ...
    ...
    

只是为了添加 context,我正在读取整数,并使用std::unordered_map<unsigned int, unsinged int>.

简单地遍历行,并分配无用的字符串流,如下所示:

std::fstream infile(<inpath>, std::ios::in);
while (std::getline(infile, line)) {
    std::stringstream ss(line);
}

一个 700MB 的文件给我大约 2.7 秒。

解析每一行:

unsigned int item;
std::fstream infile(<inpath>, std::ios::in);
while (std::getline(infile, line)) {
    std::stringstream ss(line);
    while (ss >> item);
}

给我约 17.8 秒的同一个文件。

如果我将运算符更改为std::getline+ atoi

unsigned int item;
std::fstream infile(<inpath>, std::ios::in);
while (std::getline(infile, line)) {
    std::stringstream ss(line);
    while (std::getline(ss, token, ' ')) item = atoi(token.c_str());
}

它给出了~14.6s。

有什么比这些方法更快的吗?我认为没有必要加快文件读取速度,只是解析本身 - 两者都不会造成任何伤害,但(:

4

5 回答 5

9

这个节目

#include <iostream>
int main ()
{
    int num;
    while (std::cin >> num) ;
}

读取文件大约需要 17 秒。这段代码

#include <iostream>   
int main()
{
    int lc = 0;
    int item = 0;
    char buf[2048];
    do
    {
        std::cin.read(buf, sizeof(buf));
        int k = std::cin.gcount();
        for (int i = 0; i < k; ++i)
        {
            switch (buf[i])
            {
                case '\r':
                    break;
                case '\n':
                    item = 0; lc++;
                    break;
                case ' ':
                    item = 0;
                    break;
                case '0': case '1': case '2': case '3':
                case '4': case '5': case '6': case '7':
                case '8': case '9':
                    item = 10*item + buf[i] - '0';
                    break;
                default:
                    std::cerr << "Bad format\n";
            }    
        }
    } while (std::cin);
}

同一个文件需要 1.25 秒。做你想做的...

于 2013-03-01T18:23:01.347 回答
2

流很慢。如果你真的想快速将整个文件加载到内存中,并在内存中解析它。如果实在无法将其全部加载到内存中,则分块加载,使这些块尽可能大,然后解析内存中的块。

在内存中解析时,将空格和行尾替换为空值,以便您可以atoi随时转换为整数。

哦,你会遇到块结束的问题,因为你不知道块结束是否会切断一个数字。为了解决这个问题,在块结束之前轻松停止一小段距离(应该做 16 个字节),并将这个尾部复制到开头,然后再加载它之后的下一个块。

于 2013-03-01T17:49:29.023 回答
1

您是否尝试过输入迭代器?

它跳过了字符串的创建:

std::istream_iterator<int> begin(infile);
std::istream_iterator<int> end;
int item = 0;
while(begin != end)
    item = *begin++;
于 2013-03-01T17:53:56.953 回答
1

为什么不跳过流和行缓冲区并直接从文件流中读取?

template<class T, class CharT, class CharTraits>
std::vector<T> read(std::basic_istream<CharT, CharTraits> &in) {
    std::vector<T> ret;
    while(in.good()) {
        T x;
        in >> x;
        if(in.good()) ret.push_back(x);
    }
    return ret;
}

http://ideone.com/FNJKFa

于 2013-03-01T18:05:34.017 回答
1

跟进 Jack Aidley 的回答(不能在评论中添加代码),这里有一些伪代码:

vector<char> buff( chunk_size );
roffset = 0;
char* chunk = &buff[0];
while( not done with file )
{
    fread( chunk + roffset, ... ); // Read a sizable chunk into memory, filling in after roffset
    roffset = find_last_eol(chunk); // find where the last full line ends
    parse_in_mem( chunk, chunk_size - roffset ); // process up to the last full line
    move_unprocessed_to_front( chunk, roffset ); // don't re-read what's already in mem
}
于 2013-03-01T18:14:13.870 回答