c - Mathematica 和 C/C++ : Exchanging Data

标签 c wolfram-mathematica

我想知道如何使用管道在 Mathematica 和 C/C++ 之间交换数据。在 Mathematica tutorial它说“当您打开文件或管道时,Mathematica 会创建一个‘流对象’,指定与文件或管道关联的打开流”。

我知道如何在 C 和 Mathematica 中创建文件,并且我可以让每个程序读取和写入它们。我仍然不知道该怎么做的是如何通过管道将 C 的输出发送到另一个程序,更不用说如何从 Mathematica 中做到这一点了。

这是 Mathematica 将矩阵写入二进制文件以及读取以该格式编写的文件的函数。

writeDoubleMatrix[obj_, fileName_] := Module[{file},
  file = OpenWrite[fileName, BinaryFormat -> True];
  BinaryWrite[file, Length@obj, "Integer32"];
  BinaryWrite[file, Length@obj[[1]], "Integer32"];
  BinaryWrite[file, Flatten[obj], "Real64"];
  Close[file]
 ]
readDoubleMatrix[fileName_] := Module[{file, obj, m, n},
  file = OpenRead[fileName, BinaryFormat -> True];
  m = BinaryRead[file, "Integer32"];
  n = BinaryRead[file, "Integer32"];
  obj = BinaryReadList[file, "Real64", m*n];
  Close[file];
  Partition[obj, n]
 ]

第一个函数会将 2 个整数写入文件(矩阵的大小)和矩阵的数据。我在这里没有做任何错误检查,因此我假设数据 以 {{r11, r12, ..., r1n}, ...., {rm1, rm2, ..., rmn}} 的形式具体编写。第二个函数将能够读取二进制文件并返回矩阵。

接下来是我的 C 程序。该程序将读取存储在文件 MathematicaData.bin 中的数据,将该矩阵乘以 2 并将数据写入另一个文件。

// genData.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
    int m, n, i;
    double* matrix;
    FILE* fin;
    FILE* fout;

    // Reading input file
    fin = fopen(argv[1], "rb");
    fread(&m, sizeof(int), 1, fin);
    fread(&n, sizeof(int), 1, fin);
    matrix = (double*)malloc(m*n*sizeof(double));
    fread(matrix, sizeof(double), m*n, fin);
    fclose(fin);

    //Modifying data
    for (i = 0; i < m*n; ++i) matrix[i] = 2*matrix[i];

    // Writing output file
    fout = fopen(argv[2], "wb");
    fwrite(&m, sizeof(int), 1, fout);
    fwrite(&n, sizeof(int), 1, fout);
    fwrite(matrix, sizeof(double), m*n, fout);
    fclose(fout);

    // De-allocate memory used for matrix.
    free(matrix);
    return 0;
 }

这个程序没有任何错误检查。您需要小心使用它,否则程序可能无法检测到文件,甚至无法分配您想要的内存量。在任何情况下,我们都可以使用您选择的编译器来编译程序。

gcc -o genData genData.c

现在我们可以尝试使用这些函数在 Mathematica 的两种语言之间进行通信。

matrix = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
writeDoubleMatrix[matrix, "MathematicaData.bin"];
Run["./genData MathematicaData.bin CData.bin"];
readDoubleMatrix["CData.bin"]

如果一切顺利,你应该得到的输出是

{{2., 4., 6., 8.}, {10., 12., 14., 16.}, {18., 20., 22., 24.}}

是的,这是一种将矩阵乘以 2 的非常耗时的方法,但这只是一个简单的示例,用于说明如何将数据从 Mathematica 交换到 C 以及从 C 到 Mathematica。 我不喜欢的是所有内容都先存储到一个文件中,然后在另一个程序中读取。谁能告诉我如何在不写文件的情况下交换数据。我觉得管道是我需要的,但我不知道如何用这两种语言读取或写入它们。如果您可以修改此程序以使其适应管道,将会很有帮助。


更新:

我发现了如何使 C 程序“可管道化”。

//genDataPipe.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
    int m, n, i;
    double* matrix;

    // Reading input file
    fread(&m, sizeof(int), 1, stdin);
    fread(&n, sizeof(int), 1, stdin);
    matrix = (double*)malloc(m*n*sizeof(double));
    fread(matrix, sizeof(double), m*n, stdin);

    //Modifying data
    for (i = 0; i < m*n; ++i) matrix[i] = 2*matrix[i];

    // Writing output file
    fwrite(&m, sizeof(int), 1, stdout);
    fwrite(&n, sizeof(int), 1, stdout);
    fwrite(matrix, sizeof(double), m*n, stdout);

    // Deallocate memory used for matrix.
    free(matrix);
    return 0;
}

这意味着我们必须像这样使用程序:

./genDataPipe < fileIn.bin > fileOut.bin

我一直在搜索 Mathematica 方面的文档,但我发现我可以通过管道传输外部命令来打开文件。以OpenWrite为例:

On computer systems that support pipes, OpenWrite["!command"] runs the external program specified by command, and opens a pipe to send input to it.

这意味着我可以将我的二进制输入直接提供给 c 程序。问题是我找不到重定向程序输出的方法。我想到的是,将数据写入文件并制作一个包装器来运行外部命令并读取外部命令输出的内容。这里我们假设之前存在 writeDoubleMatrix

getDataPipe[fileName_] := Module[{file, obj, m, n},
  file = OpenRead["!./genDataPipe < " <> fileName, 
  BinaryFormat -> True];
  m = BinaryRead[file, "Integer32"];
  n = BinaryRead[file, "Integer32"];
  obj = BinaryReadList[file, "Real64", m*n];
  Close[file];
  Partition[obj, n]
 ]
matrix = {{1, 2, 3}, {4, 5, 6}};
writeDoubleMatrix[matrix, "MData.bin"];
output = getDataPipe["MData.bin"]

这导致输出具有以下内容 {{2., 4., 6.}, {8., 10., 12.}}

现在剩下的唯一目标是找出如何消除 writeDoubleMatrix 的需要并直接传递数据而无需写入文件。

最佳答案

第一步是创建命名管道(如上面 Artefacto 的评论所述)。这称为 fifo,先进先出。管道使一个文件的输出成为另一个文件的输入。我想指出,这些方法只是在我开始之前的 Linux。

基本上,管道是这样工作的:

mkfifo mypipe 或在 C: system ("mkfifo mypipe");

下一步是将输出写入管道,因为 Linux 中的所有内容都是文件,您可以为此使用标准的 i/o 操作,甚至将标准输入重定向到管道.您已经在 Mathematica 和 C 中有了相应的代码。因此,在将文件的输出写入您的管道后,Mathematica 版本可以从管道读取输入,将其相乘,然后将其显示在 stdout 或任何您喜欢的地方。你真的不应该对这种方法有问题,因为管道在读取后被清空并且可以在你完成后轻松移除。如果您之后想移除管道,只需运行 system ("rm myfifo");

如果您真的不想要任何附件文件,即使它不是那么糟糕,请尝试创建另一个实际以标准输出方式输出的文件。然后制作一个让 Matematica 从标准输入中读取的文件。现在,通过管道:

./cprogram | ./m程序

这意味着 C 程序的输出应该是 Mathematica 程序的输入。据我所知,这仍然会创建一个管道,但它会在完成后被系统自动删除,一般的最终用户可能不会看到它。

关于c - Mathematica 和 C/C++ : Exchanging Data,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6462275/

相关文章:

wolfram-mathematica - 如何使用Mathematica绘制经典的状态图?

wolfram-mathematica - 为什么我要评估两次?

c - 如果正在等待的线程自行分离怎么办?

c - Yacc 找不到 YY_CURRENT_BUFFER

c - 在solaris中如何解决时间函数的2038问题

python - 如何在 Python 程序中使用 Mathematica 函数?

wolfram-mathematica - 备份文件在内存中的说明

c - 为什么 struct tm 中的 tm_year 成员相对于 1900 而不是 macosx 上 C 中的 1970?

一次更改多个文件名

wolfram-mathematica - 为什么我会在此构造中收到安全警告消息 "This file contains potentially unsafe dynamic content"