Nate Lockwood
Nate Lockwood

Reputation: 3655

Writing an integer vector to file is not producing expected values as viewed in xxd

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

Upvotes: 2

Views: 56

Answers (1)

Dirk is no longer here
Dirk is no longer here

Reputation: 368181

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

Upvotes: 3

Related Questions