Home > Back-end >  Writing an integer vector to file is not producing expected values as viewed in xxd
Writing an integer vector to file is not producing expected values as viewed in xxd

Time:06-15

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)
> 
  • Related