Rcpp写入整数向量为二进制文件xxd查看数值异常问题咨询
R写入整数二进制文件供ImageJ读取异常问题
问题背景
在R中创建、处理了一个已转换为integer类型的大型向量,需要将数据写入二进制文件供第三方应用ImageJ读取。因未找到可实现该需求的R原生函数,尝试使用Rcpp编写功能实现。写入后的文件大小符合预期,但单个数据内容异常,测试示例如下。
测试环境
- RStudio 2021.09.0 Build 351
- R version 4.1.3 (2022-03-10) -- "One Push-Up"
- MacOS Monterey ver 12.4
异常复现代码
初始Rcpp实现代码
#include <Rcpp.h> #include <stdlib.h> #include <string> #include <stdio.h> using namespace Rcpp; // [[Rcpp::export]] int cpp_saveAsBin (const IntegerVector & imgData, const std::string fname) { FILE *outP = fopen(fname.c_str(), "wb"); if (outP == NULL) Rcpp::stop("Cannot open file", fname); int res = fwrite(imgData, sizeof(int), imgData.size(), outP); fclose(outP); return (res); }
测试执行流程
r <- 1:20 typeof(r) # [1] "integer" cpp_saveAsBin(as.integer(r), "test.rawP") # [1] 20
生成文件二进制查看结果(内容已截断缩短行长度)
xxd test.rawP 00000000: 8d00 0001 ffff 0000 e072 8259 0100 0000 00000010: 78a9 7058 0100 0000 0885 115b 0100 0000 00000020: 3880 c05f 0100 0000 0831 4b3c 0100 0000 00000030: c000 6f02 0060 0000 0200 0000 0100 0000 00000040: e072 8259 0100 0000 b04f 3938 0100 0000
查看得到的数值并非1到20对应的二进制表示。
问题原因
代码错误传入了写入的内存起始地址。
Rcpp的IntegerVector是包装R内部SEXP对象的代理类,直接将imgData作为fwrite的写入地址参数时,实际写入的是SEXP对象的结构体头部内存,这块区域存储的是R对象的元数据(类型标记、长度、属性指针、引用计数等),真正存储整数数值的连续数组位于结构体之后的偏移内存位置,因此输出的二进制内容完全不是目标向量的数值。文件大小符合预期仅为巧合。
修正实现
修正后的Rcpp代码
需要传入整数数组的真实起始地址,通过begin()方法获取底层数组指针即可:
#include <Rcpp.h> #include <stdio.h> #include <string> using namespace Rcpp; // [[Rcpp::export]] int cpp_saveAsBin (const IntegerVector & imgData, const std::string fname) { FILE *outP = fopen(fname.c_str(), "wb"); if (outP == NULL) Rcpp::stop("Cannot open file: %s", fname.c_str()); // 传入底层整数数组的起始指针 int res = fwrite(imgData.begin(), sizeof(int), imgData.size(), outP); fclose(outP); return res; }
更优的原生R实现
不需要编写Rcpp代码,R原生提供的writeBin()函数完全支持二进制写入需求,性能足以处理大型向量,且避免C++内存操作的风险:
r <- 1:20 # 以二进制写入模式打开文件连接 con <- file("test.raw", "wb") # 写入4字节整数,根据ImageJ要求明确指定字节序,跨平台更稳妥 writeBin(as.integer(r), con, size = 4L, endian = "little") close(con)
注意:供ImageJ读取时需要提前确认要求的整数字节序(大端/小端),明确设置endian参数,避免不同系统默认字节序差异导致读取出错。
内容的提问来源于stack exchange,提问作者Nate Lockwood
相关产品推荐
相关产品推荐

