MySQL C API 读取含零字节字段时怎么使用 mysql_fetch_lengths
来源:17golang原创
时间:2026-11-11 04:16:19 469浏览 收藏
读取 MySQL 的 BLOB 或其他可能包含零字节的字段时,正确做法是:先让 mysql_fetch_row() 取得当前行,再调用 mysql_fetch_lengths() 取得每一列的字节数,最后用这个长度复制数据。不要用 strlen() 推断字段大小,因为它会把第一个 \0 当成字符串结尾。
官方地址:https://dev.mysql.com/doc/c-api/8.4/en/mysql-fetch-lengths.html
关键边界是:长度数组只对应当前行;长度不包含结尾的空字节;长度为 0 既可能是空字段,也可能是 SQL NULL,必须再看 row[index] 是否为 NULL。
为什么二进制字段不能交给 strlen
MySQL C API 返回的 MYSQL_ROW 是一组字段指针。文本字段通常以零字节结束,看起来像 C 字符串;但 BLOB、压缩内容、序列化数据或用户主动保存的二进制内容都可能在中间出现 \0。此时 strlen(row[index]) 只能找到第一个零字节,后半段仍在结果缓冲区中,却不会被复制。
mysql_fetch_lengths(result) 返回当前行每列的 unsigned long 长度,单位是字节,而且不包含额外的终止零字节。它还避免了为文本字段再次扫描内存;因此即使字段看起来是字符串,按长度处理也更稳定。
图1把结果集、当前行、长度数组和复制动作放在同一张静态关系图中,重点看 MYSQL_ROW 与 unsigned long 长度之间的对应关系。它是结构说明图,不是运行截图。

在当前行里用 mysql_fetch_lengths 复制完整字段
调用顺序不要颠倒:必须先成功取得一行,之后长度数组才有明确的当前行。下面的辅助函数复制一个字段到调用方拥有的缓冲区,适合 BLOB 或含嵌入零字节的数据。
#include#include #include /* 返回 1 表示读取成功;out == NULL 且 out_len == 0 表示 SQL NULL。 */ static int copy_binary_field(MYSQL_RES *result, MYSQL_ROW row, unsigned int index, unsigned char **out, unsigned long *out_len) { unsigned long *lengths; unsigned int field_count; /* 长度数组只对刚刚取得的当前行有效。 */ lengths = mysql_fetch_lengths(result); field_count = mysql_num_fields(result); if (lengths == NULL || index >= field_count) { return 0; /* 结果无效或列下标越界。 */ } if (row[index] == NULL) { *out = NULL; *out_len = 0; /* NULL 列和空列都长为 0,要靠指针区分。 */ return 1; } *out_len = lengths[index]; *out = (unsigned char *)malloc(*out_len == 0 ? 1 : *out_len); if (*out == NULL) { return 0; /* 内存分配失败时不复制半截数据。 */ } if (*out_len != 0) { memcpy(*out, row[index], *out_len); /* 按字节数复制,允许中间出现零字节。 */ } return 1; } /* 当前行的生命周期结束后,调用方负责释放复制出来的缓冲区。 */ void consume_row(MYSQL_RES *result, unsigned int blob_index) { MYSQL_ROW row = mysql_fetch_row(result); unsigned char *data = NULL; unsigned long data_len = 0; if (row == NULL) { return; /* 没有下一行,或使用流式结果时发生了读取错误。 */ } if (!copy_binary_field(result, row, blob_index, &data, &data_len)) { return; /* 真实程序应记录错误并走统一清理路径。 */ } if (data != NULL) { /* 这里可以把 data[0..data_len) 写入文件或交给二进制解析器。 */ free(data); } }
这里的 data_len 是字段的真实字节数,memcpy 不会因为中间有零字节而提前停止。复制完成后,数据不再依赖 MySQL 结果集内部的当前行缓冲区,可以在下一次 mysql_fetch_row() 或释放结果集后继续使用。
图2强调的是字段级对应:列下标选中一个指针和一个长度,源字节区按该长度进入应用侧缓冲区。图中只表达静态数据关系,不代表某次真实执行输出。

空字段、SQL NULL 和当前行生命周期要分开处理
官方 C API 对空字段和 SQL NULL 都返回长度 0,但两者的指针状态不同:空字段的 row[index] 不是 NULL,只是 lengths[index] == 0;SQL NULL 的字段指针才是 NULL。因此不要只依据长度判断是否缺失。
| 字段状态 | row[index] | lengths[index] | 处理建议 |
|---|---|---|---|
| SQL NULL | NULL | 0 | 按缺失值处理,不读取指针 |
| 空字段 | 非 NULL | 0 | 保留为空字节序列 |
| 普通文本 | 非 NULL | 实际字节数 | 可按长度复制,不依赖 strlen |
| 含零字节的二进制 | 非 NULL | 完整字节数 | 必须用 memcpy 等长度感知接口 |
还有三个容易踩到的边界:
- 只对当前行有效:取得下一行后,上一行的长度数组和字段指针都不应继续当作稳定存储。
- 不包含终止零字节:如果业务最终要构造 C 字符串,应另行分配
length + 1并手动补零,不能把补零空间算进数据库字段长度。 - 调用时机要正确:在
mysql_fetch_row()之前,或结果集的所有行都读完之后,调用mysql_fetch_lengths()都没有可用的当前行。
用这张检查表避免字段被截断
如果代码要把 MySQL 字段保存到文件、消息或自定义协议,按下面顺序检查即可:
- 是否在成功取得当前行之后调用了
mysql_fetch_lengths()? - 是否用
lengths[index]作为复制长度,而不是用strlen()? - 是否先判断
row[index] == NULL,再读取字段内容? - 是否在下一次取行或释放结果集前,把需要长期保存的数据复制到自己的缓冲区?
- 复制后的缓冲区是否在所有分支都释放,且没有把二进制数据当作以零结尾的文本打印?
一句话总结:mysql_fetch_row() 给出当前行的字段指针,mysql_fetch_lengths() 给出这些指针对应的字节长度;只要数据可能包含零字节,就按“指针 + 长度”处理。
相关问题
- mysql_fetch_lengths 返回的长度包含字符串结尾吗?不包含,长度只表示字段数据本身的字节数。
- 长度为 0 能说明字段是 NULL 吗?不能,空字段和 SQL NULL 都可能是 0,要检查字段指针。
- 可以直接保存 row[index] 指针吗?不建议。需要跨越下一次取行或结果集释放后使用时,应按返回长度复制。
-
499 收藏
-
206 收藏
-
500 收藏
-
193 收藏
-
270 收藏
-
278 收藏
-
389 收藏
-
443 收藏
-
334 收藏
-
370 收藏
-
数据库 · MySQL | 28分钟前 | MySQL · 多线程 · mysql C API mysql_thread_init mysql_thread_end mysql_library_init mysql_init187 收藏
-
数据库 · MySQL | 31分钟前 | MySQL · 字符集 · 连接字符集 MySQL C API mysql_real_escape_string_quote SQL字符串转义 quote参数337 收藏
-
178 收藏
-
490 收藏
-
484 收藏
-
119 收藏
-
301 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习