如何用C++ Apache Arrow库读取Snappy压缩的Parquet文件?
问题描述
我用以下C++代码可以正常读取未压缩的b.parquet文件:
#include <iostream> #include <arrow/api.h> #include <arrow/io/api.h> #include <arrow/ipc/reader.h> #include <arrow/util/logging.h> #include <arrow/ipc/api.h> #include <parquet/arrow/reader.h> #include <parquet/arrow/writer.h> arrow::Status RunMain() { auto pool = arrow::default_memory_pool(); std::shared_ptr<arrow::io::ReadableFile> file; ARROW_ASSIGN_OR_RAISE(file, arrow::io::ReadableFile::Open("b.parquet")); std::unique_ptr<parquet::arrow::FileReader> reader; PARQUET_THROW_NOT_OK(parquet::arrow::OpenFile(file, arrow::default_memory_pool(), &reader)); std::shared_ptr<arrow::Table> parquet_table; PARQUET_THROW_NOT_OK(reader->ReadTable(&parquet_table)); std::cout << "Table Data:\n"; std::cout << parquet_table->ToString() << std::endl; return arrow::Status::OK(); } int main() { arrow::Status st = RunMain(); if (!st.ok()) { std::cerr << st << std::endl; return 1; } return 0; }
但换成Snappy压缩的Parquet文件时,控制台输出乱码。该压缩文件是用Python的pandas生成的(pandas.dataframe.to_parquet默认用Snappy压缩):
b = pd.DataFrame([[1,2,3,4],[2,2,3,4],[4,5,6,7],[9,10,11,101]]) b.to_parquet('b.parquet')
如果在Python代码里设置compression=None生成未压缩文件,C++代码就能正常运行。现在想知道:如何读取大量已压缩的Parquet文件?
解决方案
1. 确保Arrow/Parquet库编译时包含Snappy压缩支持
乱码问题本质是你的C++版Arrow/Parquet库未编译Snappy压缩支持,导致无法正确解码Snappy压缩的Parquet数据,进而输出乱码。
- 自行编译Arrow库时,需要在CMake编译阶段添加
-DARROW_WITH_SNAPPY=ON参数,同时确保系统已安装Snappy依赖(如Ubuntu下执行sudo apt-get install libsnappy-dev,CentOS下执行yum install snappy-devel)。编译Parquet库时,需依赖已开启Snappy支持的Arrow库。 - 使用预编译包时,选择包含Snappy支持的版本,比如通过conda安装:
conda install -c conda-forge arrow-cpp parquet-cpp snappy。
2. 原代码无需修改,验证Snappy支持
当库正确包含Snappy支持后,你现有的C++代码不需要任何修改,即可正常读取Snappy压缩的Parquet文件。建议先单文件测试,确认乱码问题消失。
3. 批量读取大量压缩Parquet文件的优化方法
处理大量Parquet文件时,可参考以下优化方式:
- 批量遍历文件:使用C++17及以上的
<filesystem>库遍历目标目录下的所有.parquet文件。 - 并行读取:利用Arrow的线程池(
arrow::ThreadPool)分发读取任务,提升处理效率。 - 分块读取:针对大文件,不要一次性读取整个表,而是用
reader->ReadRowGroup(row_group_idx, &table)按行组读取,降低内存占用。 - 合并表(可选):若需将多个文件的数据合并为一个表,可使用
arrow::ConcatenateTables函数合并读取到的多个arrow::Table。
批量读取目录下Parquet文件的简化代码片段:
#include <filesystem> #include <vector> namespace fs = std::filesystem; arrow::Status ReadAllParquet(const std::string& dir_path, std::vector<std::shared_ptr<arrow::Table>>& tables) { for (const auto& entry : fs::directory_iterator(dir_path)) { if (entry.path().extension() == ".parquet") { std::shared_ptr<arrow::io::ReadableFile> file; ARROW_ASSIGN_OR_RAISE(file, arrow::io::ReadableFile::Open(entry.path().string())); std::unique_ptr<parquet::arrow::FileReader> reader; PARQUET_THROW_NOT_OK(parquet::arrow::OpenFile(file, arrow::default_memory_pool(), &reader)); std::shared_ptr<arrow::Table> table; PARQUET_THROW_NOT_OK(reader->ReadTable(&table)); tables.push_back(table); } } return arrow::Status::OK(); }
内容的提问来源于stack exchange,提问作者DennisDai
相关产品推荐
相关产品推荐

