Записать файл bin с помощью Python C-module и прочитать файл на C

#python #c #c #ctypes

#python #c #c #ctypes

Вопрос:

Я использую ctypes для преобразования некоторой информации в двоичный файл, который должен быть прочитан программой на C . Этот файл будет содержать строки со строками и другие строки с double / vectors of double .

Проблемы возникают, когда я пытаюсь прочитать вектор double в 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*)amp;structed_data, sizeof(structed_data));  
        output_file << amp;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*)amp;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*)amp;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;
}
  

Комментарии:

1. Начните с открытия ваших файлов в двоичном режиме; будет иметь значение, если вы используете Windows, хорошая практика на других ОС.

2. Спасибо, я забыл!

Ответ №1:

Здесь у вас одно глубокое недоразумение.

Пара полей

 unsigned int n_measures;
double* value;
  

это массив (выводимый из их имен), поэтому в writer вам нужно сохранить 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*)amp;structed_data.id, sizeof(int));
    output_file.write((char*)amp;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 *)amp;structed_data[i], sizeof(double));

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

return 0;
  

}

Надеюсь, вы поняли идею, по крайней мере, в части C . Код считывателя аналогичен — считывает id , n_measures а затем выделяет values массив и считывает значения одно за другим.

Комментарии:

1. Я попробую это и скоро дам вам отзыв, спасибо за ваш ответ.

2. Мы сделали подобный подход, с некоторыми изменениями в реальном коде, и мы его получаем. Спасибо, чувак!