首页 > 解决方案 > 使用 Python C 模块编写 Bin 文件并在 C 中读取文件

问题描述

ctypes用来转换二进制文件中的一些信息,这些信息需要由 C++ 程序读取。该文件将包含带有字符串的行和其他带有双精度/双精度向量的行。

当我尝试在 C++ 中读取双精度向量时会出现问题:向量的地址位于行尾,地址有 11 个字符,而 C++ 中有 8 个字符。当我尝试将向量直接读入 C++ 时,由于这种差异会发生错误。

一种可能的解决方案是逐个元素地读取向量,但这会降低性能。

有没有可能在转换和读取过程中撕掉地址,或者忽略它,以及整个向量?

这是我们用来测试的一些代码:

C++ DLL 模块:

#include<iostream>
#include<fstream>
#include<cstring>
#include <typeinfo>

using namespace std;

#define DLLEXPORT extern "C" __declspec(dllexport)

struct new_element {
    int id;
    unsigned int n_measures;
    double* value;
};

DLLEXPORT int writer_bin_file_c(const char* file_path, new_element structed_data)
{
    try {
        ofstream output_file(file_path);
        output_file.write((char*)&structed_data, sizeof(structed_data));  
        output_file << &structed_data;
        output_file.close();
    } catch (...) {
        return -1;
    }

    return 0;
}

DLLEXPORT new_element reader_bin_file_c(const char* file_path, new_element structed_data)
{
    try {
        ifstream input_file(file_path, ios::binary);
        input_file.read((char*)&structed_data, sizeof(structed_data));       
        input_file.close();
    } catch (...) {
        cout << "Error ao ler o arquivo" << endl;
    }

    return structed_data;
}

Python写入文件:

from ctypes import *
import sys
import numpy as np

lib = CDLL("version4/template_file.so")

class new_element(Structure):
    _fields_ = [("id", c_int),
                ("n_measures", c_uint),
                ("value", POINTER(c_double))]

template_file = lib
new_element_pointer = POINTER(new_element)

writer_bin_file = template_file.writer_bin_file_c
writer_bin_file.argtypes = [c_char_p, new_element]
writer_bin_file.restype = c_void_p 

reader_bin_file_c = template_file.reader_bin_file_c
reader_bin_file_c.restype = new_element

tam = 10
medida = np.arange(tam, dtype=c_double)
medida = medida.ctypes.data_as(POINTER(c_double))

element = new_element(4, tam)
element.value = medida

file_out = b'version4/element.txt'

answer = writer_bin_file(file_out, element)

C++ 读取文件:

#include<iostream>
#include<fstream>
#include<cstring>
#include <typeinfo>

using namespace std;

struct new_element {
    int id;
    unsigned int n_measures;
    double* value;
};

new_element reader_bin_file(const char* file_path, new_element structed_data)
{
    try {
        ifstream input_file(file_path);
        input_file.read((char*)&structed_data, sizeof(structed_data));    
        input_file.close();
    } catch (...) {
        cout << "Error ao ler o arquivo" << endl;
    }

    return structed_data;
}

int main(int argc, char const *argv[])
{
    new_element read_template;
    read_template = reader_bin_file(file_out, read_template);

    cout << "ID: " << read_template.id << endl;
    cout << "n_measures: " << read_template.n_measures << endl;
    cout << "Value: ";
    for (int i = 0;  i < read_template.n_measures; i++) 
    {
      cout << "( " << i << " ): " << read_template.value[i] << " | ";
    }
    cout << endl;

    return 0;
}

标签: pythonc++cctypes

解决方案


你在这里有一个深刻的误解。

一对字段

unsigned int n_measures;
double* value;

是一个数组(从他们的名字推导出来),所以在编写器中你必须保存n_measures双精度值,而不是单个指针。因此,在阅读器中,您应该读取 n_measures值,而不仅仅是指针。指针只是内存空间中的一个索引,而不是一些“全能”的 C/C++ 语言功能,它可以保存您需要的一切。

所以,在你的 C++ 编写代码中

DLLEXPORT int writer_bin_file_c(const char* file_path, new_element structed_data)
{
  try {
    ofstream output_file(file_path);
    output_file.write((char*)&structed_data.id, sizeof(int));
    output_file.write((char*)&structed_data.n_measures, sizeof(int));
    // write out all the elements one by one, not just the pointer
    for (int i = 0 ; i < structed_data.n_measures ; i++)
       output_file.write((char *)&structed_data[i], sizeof(double));

    output_file.close();
} catch (...) {
    return -1;
}

return 0;

}

希望你明白,至少在 C++ 部分。读取器代码类似——读取idn_measures然后分配values数组并一一读取值。


推荐阅读