使用 R 的 .C 接口处理 read/write 个文件

Using `.C` Interface of R to handle read/write files

我正在尝试逐行过滤一个巨大的 txt 文件,纯 R 不太擅长。所以,我写了一个 c 函数,希望可以加快这个过程。以下是 filter.c 最小工作示例 ,仅用于演示目的。

目前,我已经尝试 .C 完成这个技巧,但运气不佳。这是我的尝试。

  1. 使用 gcc -shared -o lfilter.so -fPIC filter.c
  2. 构建 filter.so
  3. dyn.load("lfilter.so")
  4. .C("filter", as.character("I1.txt"), as.character("I1.out.txt"), as.character("filter.txt"))

R 我在第 3 步崩溃了。但不幸的是,我必须留在R.

欢迎任何帮助或建议。

filter.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LL 256

int get_row(char *filename)
{
  char line[LL];
  int i = 0;
  FILE *stream = fopen(filename, "r");
  while (fgets(line, LL, stream))
  {
    i++;
  }
  fclose(stream);
  return i;
}


void filter(char *R1_in,
            char *R1_out,
            char *filter)
{
  char R1_line[LL];
  
  FILE *R1_stream = fopen(R1_in, "r");
  FILE *R1_out_stream = fopen(R1_out,"w");
 
  /*****************loading filters*******************/
  int nrows = get_row(filter);
  
  FILE *filter_stream = fopen(filter, "r");
  
  char **filter_list = (char **)malloc(nrows * sizeof(*filter_list));
  for(int i = 0; i <nrows; i++)
  {
    filter_list[i] = malloc(LL * sizeof(char));
    fgets(filter_list[i], LL, filter_stream);
  }
  
  fclose(filter_stream);
  
  /*****************filtering*******************/
  
  while (fgets(R1_line, LL, R1_stream))
  {
    // printf("%s", R1_line);
    
    for(int i = 0; i<nrows; i++)
    {
      if(strcmp(R1_line, filter_list[i])==0)
      {
        fprintf(R1_out_stream, "%s", R1_line);
        break;
      } 
    }
  }
  printf("\n");
  
  for(int i=0; i<nrows; i++)
  {
    free(filter_list[i]);
  }
  free(filter_list);
  
  fclose(R1_stream);
  fclose(R1_out_stream);
  
}

// int main()
// {
//   char R1_in[] = "I1.txt";
//   char R1_out[] = "I1.out.txt";
// 
//   char filters[] = "filter.txt";
// 
//   filter(R1_in, R1_out, filters);
//   return 0;
// }

I1.txt

aa
baddf
ca
daa

filter.txt

ca
cb

预期输出I1.out.txt

ca

我以前从未使用过 R。但是,我有点好奇。所以,我安装了 R 并做了一些研究。

R [使用 .C 接口] 中的所有内容都作为指针传递给 C 函数。

来自:https://www.r-bloggers.com/2014/02/three-ways-to-call-cc-from-r/ 我们有:

Inside a running R session, the .C interface allows objects to be directly accessed in an R session’s active memory. Thus, to write a compatible C function, all arguments must be pointers. No matter the nature of your function’s return value, it too must be handled using pointers. The C function you will write is effectively a subroutine.

因此,如果我们传递一个整数,C 函数参数必须是:

int *

我猜了一下:

char *

需要:

char **

然后用以下方法测试:

#include <stdio.h>

#define SHOW(_sym) \
    show(#_sym,_sym)

static void
show(const char *sym,char **ptr)
{
    char *str;

    printf("%s: ptr=%p",sym,ptr);

    str = *ptr;
    printf(" str=%p",str);

    printf(" '%s'\n",str);
}

void
filter(char **R1_in,char **R1_out,char **filt)
{

    SHOW(R1_in);
    SHOW(R1_out);
    SHOW(filt);
}

这是输出:

> dyn.load("filter.so");
> .C("filter",
+   as.character("abc"),
+   as.character("def"),
+   as.character("ghi"))
R1_in: ptr=0x55a9f8cb1798 str=0x55a9f9de9760 'abc'
R1_out: ptr=0x55a9f8cb1818 str=0x55a9f9de9728 'def'
filt: ptr=0x55a9f8cb1898 str=0x55a9f9de96f0 'ghi'
[[1]]
[1] "abc"

[[2]]
[1] "def"

[[3]]
[1] "ghi"

> q()

那么,你想要:

void
filter(char **R1_in, char **R1_out, char **filt)
{

    FILE *R1_stream = fopen(*R1_in, "r");

    // ...
}