I have data created/modified in r in a large vector converted to integer. I need to write the data as a binary file which will be read by another app, ImageJ. I was unable to find an R function to do this so I tried Rcpp. The written file size is as expected but the individual data are not. My test example is below.
RStudio 2021.09.0 Build 351
R version 4.1.3 (2022-03-10) -- "One Push-Up"
MacOS Monterey ver 12.4
Rcpp function
r <- 1:20
typeof(r)
[1] "integer"
#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);
}
Testing
> r <- 1:20
> typeof(r)
[1] "integer"
> cpp_saveAsBin(as.integer(r), "test.rawP")
[1] 20
Checking with xxd (trimmed to shorten the line.
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
The values are not binary representations from 1 through 20. What's going on? Is there a better method?
TIA
CodePudding user response:
You are very close. Recall that the first argument of the fwrite()
function is a void*
to the blob of memory of the given type and size you want to write.
But you pointed at the beginning of the (internally a SEXP
) R container object of minimal meta data and the actual twenty integers, and not the 20 integers! So by making that imgData.begin()
(or, equally, a &(imgData[0])
) you actually write the payload you want to write!
Another nice thing is that we can actually test the reading back from R itself via readBin
(and you could also write from R if you wanted to...)
My modified version of your code, along with the test code follows. I also simplified the headers (as Rcpp.h
gets us what we need) and removed on unneeded cast on the R side):
Code
#include <Rcpp.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.begin(), sizeof(int), imgData.size(), outP);
fclose(outP);
return (res);
}
/*** R
r <- 1:20
typeof(r)
class(r)
cpp_saveAsBin(r, "/tmp/test.raw.bin")
fp <- file("/tmp/test.raw.bin", "rb")
readBin(fp, integer(), 20)
close(fp)
*/
Output
> Rcpp::sourceCpp("~/git/stackoverflow/72610544/answer.cpp")
> r <- 1:20
> typeof(r)
[1] "integer"
> class(r)
[1] "integer"
> cpp_saveAsBin(r, "/tmp/test.raw.bin")
[1] 20
> fp <- file("/tmp/test.raw.bin", "rb")
> readBin(fp, integer(), 20)
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
> close(fp)
>