
Apache Arrow GLib 官方示例精读C / Lua / Vala 多语言列式数据读写实战指南【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrowApache Arrow GLib 是 Apache Arrow 的 C 语言封装库它通过 GObject Introspection 机制让 Lua、Ruby、Vala 等多种语言可以在运行时或编译期获得与 C 一致的数据读写能力。本文以 c_glib/example/README.md 为骨架逐一对官方示例代码进行精读你将掌握 Arrow IPC 两种序列化格式文件格式与流格式的读写 API 调用链、RecordBatch 的构建与打印方法并了解如何基于 Arrow GLib 在 C、Lua、Vala 三种语言中落地同样的列式数据处理逻辑最后还会深入到扩展类型与网络传输两个高级示例的实现细节。一、示例目录结构与定位c_glib/example目录是 Arrow GLib 官方示例代码的集合。README 明确指出C 语言的示例代码直接位于本目录而其他语言绑定的示例代码位于各子目录中例如 Lua 示例位于lua/子目录。当前仓库中该目录的实际结构如下c_glib/example/ ├── README.md # C 示例说明文档 ├── read-file.c # 从文件格式中读取 Arrow 数据 ├── read-stream.c # 从流格式中读取 Arrow 数据 ├── extension-type.c # 自定义 UUID 扩展类型完整示例 ├── send-network.c # 通过网络发送 RecordBatch客户端 ├── receive-network.c # 通过网络接收 RecordBatch服务端 ├── lua/ # Lua 绑定示例基于 LGI │ ├── README.md │ ├── write-file.lua / read-file.lua │ └── write-stream.lua / read-stream.lua └── vala/ # Vala 绑定示例 ├── README.md ├── write-file.vala / read-file.vala └── write-stream.vala / read-stream.vala需要留意的是README 的 C 示例清单中列出了build.c、write-file.c、write-stream.c但这三项在源文件中处于 HTML 注释块!--- ... --内且当前仓库的c_glib/example目录下并不存在对应的.c文件——这意味着 README 中处于注释状态的示例尚未落盘实际可编译运行的 C 示例是read-file.c、read-stream.c、extension-type.c、send-network.c与receive-network.c五个文件。相比之下Lua 与 Vala 子目录中的写文件、读文件、写流、读流四个示例均已完整提供。所有 C 示例都通过统一的头文件入口引入 API#include arrow-glib/arrow-glib.h该头文件聚合了 Arrow GLib 的全部 C 接口数组、数据类型、RecordBatch、读写器、内存映射输入流等是使用 Arrow GLib 编写 C 程序的唯一前置依赖。二、C 示例一以文件格式读取 Arrow 数据read-file.cread-file.c 演示了Arrow IPC 文件格式file format的读取流程。所谓文件格式是指数据带有可随机访问的页脚footer元数据、可反复读取的持久化文件典型应用场景是磁盘上的.arrow文件。2.1 打开输入流程序首先通过garrow_memory_mapped_input_stream_new()以内存映射方式打开文件默认路径为/tmp/batch.arrow也支持通过命令行参数argv[1]覆盖const char *input_path /tmp/batch.arrow; GArrowMemoryMappedInputStream *input; if (argc 1) input_path argv[1]; input garrow_memory_mapped_input_stream_new(input_path, error);内存映射输入流使文件内容被映射到进程地址空间读取时无需显式拷贝适合需要随机访问的场景——这正是文件格式读取器的前提。2.2 创建文件读取器文件格式带有页脚因此读取器要求输入流是可随机访问seekable的。示例通过类型宏GARROW_SEEKABLE_INPUT_STREAM(input)将内存映射流向上转型为GArrowSeekableInputStream再交给文件读取器构造函数reader garrow_record_batch_file_reader_new(GARROW_SEEKABLE_INPUT_STREAM(input), error);这一点与 reader.h 中garrow_record_batch_file_reader_new(GArrowSeekableInputStream *file, GError **error)的函数签名严格对应文件格式读取器只接受可寻址流。2.3 按索引随机读取 RecordBatch文件格式支持随机访问因此示例用garrow_record_batch_file_reader_get_n_record_batches()拿到批次总数后直接按下标循环读取任意一个 RecordBatchn garrow_record_batch_file_reader_get_n_record_batches(reader); for (i 0; i n; i) { record_batch garrow_record_batch_file_reader_read_record_batch(reader, i, error); ... print_record_batch(record_batch); g_object_unref(record_batch); }每个读取到的GArrowRecordBatch是GObject对象用完后必须调用g_object_unref()释放引用这是 GLib 内存管理的标准约定。2.4 逐列打印 RecordBatch 内容示例配套了两个打印函数构成一条完整的读出来、看得见的验证链路print_record_batch()通过garrow_record_batch_get_n_columns()、garrow_record_batch_get_column_name()与garrow_record_batch_get_column_data()遍历每一列输出列序号、列名与数据print_array()通过garrow_array_get_value_type()获取数组的实际数据类型再用宏ARRAY_CASE生成针对uint8double共 10 种数值类型的switch分支逐一调用对应类型数组的garrow_*_array_get_value()取值并格式化打印。宏展开的核心是 GObject 的类型向下转型例如case GARROW_TYPE_INT32: { GArrowInt32Array *real_array; real_array GARROW_INT32_ARRAY(array); for (i 0; i n; i) { g_print(% G_GINT32_FORMAT, garrow_int32_array_get_value(real_array, i)); } } break;这展示了 Arrow GLib 的类型体系运行时先用GArrowType枚举判别再用GARROW_XXX_ARRAY()宏做类型安全的向下转换最后调用具体类型的取值函数。三、C 示例二以流格式读取 Arrow 数据read-stream.cread-stream.c 演示的是Arrow IPC 流格式stream format的读取。与文件格式不同流格式是顺序、单向的消息序列没有页脚、不支持随机跳转只能从头到尾依次消费天然适配管道、Socket、实时传输等场景。3.1 创建流读取器流读取器只需要顺序输入流因此直接传入GARROW_INPUT_STREAM(input)stream_reader garrow_record_batch_stream_reader_new(GARROW_INPUT_STREAM(input), error); reader GARROW_RECORD_BATCH_READER(stream_reader);这里出现了第二个重要的继承关系GArrowRecordBatchStreamReader是GArrowRecordBatchReader的子类示例通过GARROW_RECORD_BATCH_READER(stream_reader)将其向上转型为通用读取器接口。3.2 循环读取直到流结束流格式没有批次总数的概念示例采用读到 NULL 即结束的惯用循环while (TRUE) { record_batch garrow_record_batch_reader_read_next(reader, error); if (error) { /* 出错处理 */ } if (!record_batch) { break; /* 流已读完 */ } print_record_batch(record_batch); g_object_unref(record_batch); }garrow_record_batch_reader_read_next()返回NULL且error为空时表示正常到达流末尾返回NULL且error非空时表示读取失败。这一空指针 GError的双重约定是 GLib 风格 I/O 的典型写法。3.3 文件格式与流格式的选型对照结合两个示例可以总结出选型要点维度文件格式read-file.c流格式read-stream.c底层载体带页脚元数据的随机访问文件顺序消息序列输入流要求可寻址流GArrowSeekableInputStream仅需顺序流GArrowInputStream读取方式按索引随机读取read_record_batch(i)顺序读取直到 NULLread_next()典型场景磁盘持久化文件、反复读取管道、网络传输、实时数据四、C 示例三自定义 UUID 扩展类型extension-type.cextension-type.c 是 Arrow 扩展类型Extension Type机制的完整演示它定义了一个以 16 字节fixed-size-binary为存储类型、名为uuid的自定义扩展类型并完成注册、构建、序列化、反序列化、注销的全生命周期。4.1 定义扩展数据类型与扩展数组示例使用 GObject 的类型宏体系定义了两个类G_DECLARE_DERIVABLE_TYPE(ExampleUUIDArray, example_uuid_array, EXAMPLE, UUID_ARRAY, GArrowExtensionArray) G_DEFINE_TYPE(ExampleUUIDArray, example_uuid_array, GARROW_TYPE_EXTENSION_ARRAY) G_DECLARE_DERIVABLE_TYPE(ExampleUUIDDataType, example_uuid_data_type, EXAMPLE, UUID_DATA_TYPE, GArrowExtensionDataType) G_DEFINE_TYPE(ExampleUUIDDataType, example_uuid_data_type, GARROW_TYPE_EXTENSION_DATA_TYPE)扩展数据类型的父类是GArrowExtensionDataType扩展数组的父类是GArrowExtensionArray。类型实现的关键在于覆写父类 vtable 中的四个虚函数extension_klass-get_extension_name example_uuid_data_type_get_extension_name; extension_klass-equal example_uuid_data_type_equal; extension_klass-deserialize example_uuid_data_type_deserialize; extension_klass-serialize example_uuid_data_type_serialize; extension_klass-get_array_gtype example_uuid_data_type_get_array_gtype;get_extension_name()返回扩展名uuidserialize()/deserialize()扩展类型在序列化时必须附带一段标识数据示例用常量字符串uuid-serialized作为序列化标识反序列化时先校验标识是否匹配再校验存储类型是否仍为fixed-size-binary(16)get_array_gtype()将扩展数据类型与对应的扩展数组类关联。4.2 注册与构建数据main()中首先取得全局扩展类型注册表并注册自定义类型GArrowExtensionDataTypeRegistry *registry garrow_extension_data_type_registry_default(); ... garrow_extension_data_type_registry_register(registry, extension_data_type, error);随后从扩展类型中取出存储类型用GArrowFixedSizeBinaryArrayBuilder构造存储数组两个 16 字节字符串加一个 NULL再用garrow_extension_data_type_wrap_array()把存储数组包装成扩展数组实现存储数据 扩展语义的解耦GArrowExtensionArray *extension_array garrow_extension_data_type_wrap_array(extension_data_type, storage);4.3 序列化—反序列化闭环验证示例随后把扩展数组装入 RecordBatch用GArrowRecordBatchStreamWriter写入内存 BufferGArrowBufferOutputStreamGArrowResizableBuffer再立刻用GArrowRecordBatchStreamReader读回验证扩展类型在 IPC 往返后依然完整保留gchar *record_batch_content garrow_record_batch_to_string(record_batch, error); g_print(record batch:\n%s\n, record_batch_content); ... g_print(array: %s\n, G_OBJECT_TYPE_NAME(deserialized_array));最后在exit:标签处通过garrow_extension_data_type_registry_unregister()注销该扩展类型体现了注册—使用—注销的完整资源管理闭环。该示例是理解 Arrow 扩展类型机制自定义类型如何在 IPC 层无损传输的最佳起点。五、C 示例四基于 Socket 的网络传输send-network.c / receive-network.csend-network.c与receive-network.c是一对配套示例演示如何通过 GLib 的GSocket体系在网络上实时传输 Arrow RecordBatch本质上是把上一节的流格式应用到网络字节流上。5.1 客户端构建数据并写入 Socketsend-network.c 的运行方式是send-network PORT例如send-network 2929。客户端流程如下用g_socket_client_new()g_inet_socket_address_new_from_string(127.0.0.1, port)建立 TCP 连接到本机指定端口用build_schema()构建包含boolean与int32两列的 Schema把 Socket 连接包装为 Arrow 输出流创建流写入器GArrowGIOOutputStream *output garrow_gio_output_stream_new(g_io_stream_get_output_stream(G_IO_STREAM(connection))); GArrowRecordBatchStreamWriter *writer garrow_record_batch_stream_writer_new(GARROW_OUTPUT_STREAM(output), schema, error);通过GArrowRecordBatchBuilder构建 3 行 × 2 列的 RecordBatch其中 boolean 列第 2 行为 NULL、int32 列第 1 行为 NULL循环写入 5 个批次for (i 0; i n_record_batches; i) { GArrowRecordBatch *record_batch build_record_batch(); garrow_record_batch_writer_write_record_batch(GARROW_RECORD_BATCH_WRITER(writer), record_batch, error); ... }这里体现了garrow_record_batch_builder_get_column_builder()配合garrow_*_array_builder_append_values()的批量构建方式values 数组与 is_valids 数组同时传入即可表达 NULL 语义。5.2 服务端监听端口并解析流receive-network.c 使用GThreadedSocketService创建多线程 Socket 服务监听任意可用端口g_socket_listener_add_any_inet_port()在event信号中打印监听地址在incoming信号回调中处理每个连接GArrowGIOInputStream *input garrow_gio_input_stream_new(g_io_stream_get_input_stream(G_IO_STREAM(connection))); GArrowRecordBatchStreamReader *reader garrow_record_batch_stream_reader_new(GARROW_INPUT_STREAM(input), error); while (TRUE) { record_batch garrow_record_batch_reader_read_next(GARROW_RECORD_BATCH_READER(reader), error); if (!record_batch) break; print_record_batch(record_batch); ... }服务端在 Unix 平台还注册了SIGINT/SIGTERM信号处理器g_unix_signal_add()实现优雅退出。这对示例完整演示了 Arrow 流格式在网络传输中的应用只要底层是可靠的字节流Arrow 数据就可以在进程、机器之间无缝流动。六、Lua 示例基于 LGI 的动态绑定lua/ 子目录Lua 子目录 的示例运行在LGILua GObject Introspection之上——LGI 在运行时读取 Arrow GLib 导出的 GIR 元数据动态生成 Lua 绑定因此 Lua 侧不需要编译任何绑定代码。6.1 安装 LGIREADME 给出了 Debian/Ubuntu 上的安装方式$ sudo apt install -y luarocks $ sudo luarocks install lgi6.2 加载绑定所有 Lua 示例的开头都是一致的local lgi require lgi local Arrow lgi.ArrowArrow命名空间即对应 C 侧全部以GArrow为前缀的类命名规则为去掉G前缀GArrowRecordBatchFileReader→Arrow.RecordBatchFileReader。6.3 写入示例write-file.lua / write-stream.luawrite-file.lua 与 write-stream.lua 结构完全对称仅写入器不同前者用Arrow.RecordBatchFileWriter写文件格式到/tmp/batch.arrow后者用Arrow.RecordBatchStreamWriter写流格式到/tmp/stream.arrow。写入流程的核心步骤local output Arrow.FileOutputStream.new(output_path, false) local writer Arrow.RecordBatchStreamWriter.new(output, schema) local record_batch Arrow.RecordBatch.new(schema, 4, columns) writer:write_record_batch(record_batch) -- 用 slice 构建第二个批次 local sliced_columns {} for i, column in pairs(columns) do sliced_columns[i] column:slice(1, 3) end record_batch Arrow.RecordBatch.new(schema, 3, sliced_columns) writer:write_record_batch(record_batch) writer:close() output:close()两个值得注意的细节Schema 由 10 个字段uint8到double组成每个字段由Arrow.Field.new(name, data_type)创建第二个批次通过数组的slice(1, 3)方法对同一份列数据做零拷贝切片偏移 1、长度 3展示了一个文件里写入多个不同形状批次的能力。6.4 读取示例read-file.lua / read-stream.luaread-file.lua 与 C 版read-file.c逻辑一致Arrow.MemoryMappedInputStream打开文件Arrow.RecordBatchFileReader按索引读取区别在于 Lua 侧用record_batch:get_value(k)泛型取值不再需要 C 版的 switch 分派——这正是动态语言的便利之处local input Arrow.MemoryMappedInputStream.new(input_path) local reader Arrow.RecordBatchFileReader.new(input) for i 0, reader:get_n_record_batches() - 1 do local record_batch reader:read_record_batch(i) for j 0, record_batch:get_n_columns() - 1 do local column_data record_batch:get_column_data(j) for k 0, record_batch:get_n_rows() - 1 do io.write(column_data:get_value(k)) end end end input:close()read-stream.lua则使用Arrow.RecordBatchStreamReader顺序读取运行方式均为lua 脚本名.lua [输入文件]文件参数缺省时使用/tmp下的默认路径。七、Vala 示例静态类型绑定vala/ 子目录Vala 子目录 的示例通过valac编译器直接消费 Arrow GLib 的 GIR 元数据在编译期生成强类型绑定。README 给出的构建命令是$ valac --pkg arrow-glib --pkg posix XXX.vala--pkg arrow-glib引入 Arrow GLib 的 Vala API 包--pkg posix提供Posix.EXIT_SUCCESS等常量。read-file.vala 与 C 版read-file.c一一对应但类型系统更强input as GArrow.SeekableInputStream是显式向下转型因为MemoryMappedInputStream需要转换为SeekableInputStream才能传给文件读取器数组取值则仍需要像 C 一样按类型分支switch (array.get_value_type()) { case GArrow.Type.UINT8: var concrete_array array as GArrow.UInt8Array; for (var i 0; i n; i) { stdout.printf(%hhu, concrete_array.get_value(i)); } break; ... }错误处理使用 Vala 的try/catch语法捕获GErrortry { input new GArrow.MemoryMappedInputStream(input_path); } catch (Error error) { stderr.printf(failed to open file: %s\n, error.message); return Posix.EXIT_FAILURE; }write-file.vala/write-stream.vala/read-stream.vala与 Lua 版本逻辑相同仅语法不同。值得说明的是Vala 子目录 README 的示例清单中提到的build.vala在当前仓库中并不存在实际提供的是写/读文件与写/读流四个文件。八、示例背后的实现原理Arrow GLib 的封装层次贯穿全部示例的一条主线是 Arrow GLib 的分层封装架构最底层是 Apache Arrow Ccpp/src中实现了列式内存布局、IPC 序列化ipc/、计算内核等核心能力中间层是 Arrow GLibc_glib/arrow-glib/将 C 对象包装为 GObject 类例如 reader.h 中的GArrowRecordBatchFileReader/GArrowRecordBatchStreamReader并统一暴露 C API同时导出 GIR 元数据最上层是各语言绑定Lua 通过 LGI 运行时动态绑定Vala 通过 valac 编译期静态绑定Ruby 则通过 red-arrow / gobject-introspection gem见 c_glib/README.md。这种一次实现、多语言复用的设计正是示例目录中同一套读写逻辑以 C、Lua、Vala 三种语言各写一遍的根本原因——它们最终都调用同一份 C 实现。关于构建与运行前提要编译运行这些 C 示例需要先构建并安装 Arrow C 与 Arrow GLib。Arrow GLib 使用 Meson Ninja 构建开发构建还需要 GTK-Doc 与 GObject Introspection官方推荐直接使用发行版软件包详见 c_glib/README.md 的 Install 章节。九、小结从示例到实战的迁移路径场景推荐示例关键 API读取磁盘上的.arrow文件read-file.cgarrow_record_batch_file_reader_newread_record_batch顺序消费管道/网络流read-stream.cgarrow_record_batch_stream_reader_newread_next自定义业务数据类型extension-type.cgarrow_extension_data_type_registry_registerwrap_array跨进程/跨机器实时传输send-network.c 与 receive-network.cGArrowGIOOutputStream/GArrowGIOInputStream StreamWriter/Reader脚本语言快速原型lua/ 子目录LGI Arrow.RecordBatch*强类型语言生产代码vala/ 子目录valac --pkg arrow-glib无论选择哪种语言入口其背后都是同一套 Arrow 列式格式与 IPC 协议。把官方示例跑通一遍你就同时掌握了 Arrow 数据读写的最基本单元Array、Schema、RecordBatch、两种序列化载体文件与流以及它们在不同语言中的调用姿势足以直接迁移到自己的数据处理管线中。【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考