C++读取含arrow::large_utf8列的feather文件出现段错误问题求解
段错误触发原因
- 核心问题是你使用的Arrow版本对应的Feather实现对
large_utf8类型的兼容性存在缺陷:Feather V2(默认使用的旧版本格式)的字符串类型只支持32位偏移的普通utf8,不支持64位偏移的large_utf8。写入large_utf8类型列时,API没有触发显式报错,但实际写入的元数据、数据偏移存在不匹配,读取时解析类型错误导致内存访问越界,调用ToString()遍历列数据时就会触发段错误。 - Parquet格式本身原生支持64位偏移的字符串类型,所以相同逻辑读写Parquet不会出现异常。
修复方案
- 最优兼容方案:写入前显式转换类型
如果你的业务场景中单个字符串长度不超过2GB,完全可以用普通utf8类型存储,写入Feather前先将所有large_utf8列转换为utf8列即可解决问题,示例转换逻辑如下:
#include <arrow/compute/api.h> // 转换单个large_utf8列为utf8列 arrow::Result<std::shared_ptr<arrow::Array>> CastLargeUtf8ToUtf8(const std::shared_ptr<arrow::Array>& arr) { return arrow::compute::Cast(arr, arrow::utf8()); } // 遍历Table所有列完成类型转换 arrow::Result<std::shared_ptr<arrow::Table>> ConvertTableLargeUtf8ToUtf8(const std::shared_ptr<arrow::Table>& input) { std::vector<std::shared_ptr<arrow::Field>> new_fields; std::vector<std::shared_ptr<arrow::ChunkedArray>> new_columns; for (int i = 0; i < input->num_columns(); i++) { auto field = input->field(i); auto col = input->column(i); if (field->type()->id() == arrow::Type::LARGE_UTF8) { ARROW_ASSIGN_OR_RAISE(auto casted, arrow::compute::Cast(col, arrow::utf8())); new_fields.push_back(arrow::field(field->name(), arrow::utf8(), field->nullable())); new_columns.push_back(casted.chunked_array()); } else { new_fields.push_back(field); new_columns.push_back(col); } } return arrow::Table::Make(arrow::schema(new_fields), new_columns); }
你只需要在调用write_to_feather前,用上面的函数转换输入的Table即可。
- 需要保留
large_utf8类型的方案
如果确实需要存储长度超过2GB的单个字符串,可升级Arrow到14.0+的稳定版本,写入时显式指定使用Feather V3格式,该版本格式原生支持所有large类型:
// 写入时指定V3格式 arrow::ipc::feather::WriteOptions options; options.version = arrow::ipc::feather::kFeatherV3Version; ABORT_ON_FAILURE(arrow::ipc::feather::WriteTable(*table, output.get(), options));
- 临时验证方法
如果需要确认问题根源,可注释掉ToString调用,先打印读取到的Table每列的类型信息,确认large_utf8列是否被错误解析为其他类型,即可验证是否为类型不兼容导致的越界。
内容的提问来源于stack exchange,提问作者Sourabh Kulhari
相关产品推荐
相关产品推荐

