gpt4 book ai didi

c++ - 读取映射到内存的 CSV 文件的最简单方法?

转载 作者:太空狗 更新时间:2023-10-29 20:25:31 26 4
gpt4 key购买 nike

当我在 C++(11) 中读取文件时,我使用以下方法将它们映射到内存中:

boost::interprocess::file_mapping* fm = new file_mapping(path, boost::interprocess::read_only);
boost::interprocess::mapped_region* region = new mapped_region(*fm, boost::interprocess::read_only);
char* bytes = static_cast<char*>(region->get_address());

当我希望非常快地逐字节读取时,这很好。但是,我创建了一个 csv 文件,我想将其映射到内存,读取每一行并用逗号分隔每一行。

有没有一种方法可以通过对上述代码进行一些修改来做到这一点?

(我正在映射到内存,因为我有大量内存并且我不希望磁盘/IO 流出现任何瓶颈)。

最佳答案

这是我对“足够快”的看法。它在大约 1 秒内压缩了 116 MiB 的 CSV(2.5Mio 行[1])。

然后可以零拷贝随机访问结果,因此没有开销(除非页面被换出)。

For comparison:

  • that's ~3x faster than a naive wc csv.txt takes on the same file
  • it's about as fast as the following perl one liner (which lists the distinct field counts on all lines):

    perl -ne '$fields{scalar split /,/}++; END { map { print "$_\n" } keys %fields  }' csv.txt
  • it's only slower than (LANG=C wc csv.txt) which avoids locale functionality (by about 1.5x)

这是解析器的全部荣耀:

using CsvField = boost::string_ref;
using CsvLine = std::vector<CsvField>;
using CsvFile = std::vector<CsvLine>; // keep it simple :)

struct CsvParser : qi::grammar<char const*, CsvFile()> {
CsvParser() : CsvParser::base_type(lines)
{
using namespace qi;

field = raw [*~char_(",\r\n")]
[ _val = construct<CsvField>(begin(_1), size(_1)) ]; // semantic action
line = field % ',';
lines = line % eol;
}
// declare: line, field, fields
};

唯一棘手的事情(也是唯一的优化)是从源迭代器构造一个具有匹配字符数的 CsvField 的语义操作。

主要内容:

int main()
{
boost::iostreams::mapped_file_source csv("csv.txt");

CsvFile parsed;
if (qi::parse(csv.data(), csv.data() + csv.size(), CsvParser(), parsed))
{
std::cout << (csv.size() >> 20) << " MiB parsed into " << parsed.size() << " lines of CSV field values\n";
}
}

打印

116 MiB parsed into 2578421 lines of CSV values

您可以像 std::string 一样使用这些值:

for (int i = 0; i < 10; ++i)
{
auto l = rand() % parsed.size();
auto& line = parsed[l];
auto c = rand() % line.size();

std::cout << "Random field at L:" << l << "\t C:" << c << "\t" << line[c] << "\n";
}

打印例如:

Random field at L:1979500    C:2    sateen's
Random field at L:928192 C:1 sackcloth's
Random field at L:1570275 C:4 accompanist's
Random field at L:479916 C:2 apparel's
Random field at L:767709 C:0 pinks
Random field at L:1174430 C:4 axioms
Random field at L:1209371 C:4 wants
Random field at L:2183367 C:1 Klondikes
Random field at L:2142220 C:1 Anthony
Random field at L:1680066 C:2 pines

完整的示例在这里 Live On Coliru


[1] 我通过重复附加

的输出来创建文件
while read a && read b && read c && read d && read e
do echo "$a,$b,$c,$d,$e"
done < /etc/dictionaries-common/words

csv.txt,直到它计数为 250 万行。

关于c++ - 读取映射到内存的 CSV 文件的最简单方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23699731/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com