Сделать файловый указатель считываемым/записываемым в ячейку памяти


Я могу сделать запись указателя файла в файл с помощью fopen (). Но могу ли я сделать указатель файла, который сделает его так, что вызывающие функции, такие как fputc или fprintf, будут записывать в указатель в памяти? Примером этого является ByteArrayOutputStream в java. Также: могу ли я запустить его в обратном порядке, когда библиотеке нужен указатель на файл для чтения, поэтому я выделяю память и создаю новый указатель на файл, который будет считываться из этого места памяти, но возвращать EOF, когда размер блока закончится? (любить ByteArrayInputStream на языке Java). Есть ли способ сделать это в C? Например:

FILE *p = new_memory_file_pointer();
fprintf(p, "Hello World!n");
char *data = get_written_stuff(p);
printf("%s", data); //will print Hello World!

&& / ||

char s[] = "Hello World!n";
FILE *p = new_memory_file_pointer_read(s, sizeof(s));
char *buffer = (char *)malloc( 1024*sizeof(char) );
fread((void *)buffer, 1, sizeof(s), p);
printf("%s", buffer); //prints Hello World!

EDIT: для тех, кто читает этот вопрос годы спустя, в дополнение к принятому ответу, вы должны посмотреть на open_memstream(3), который ведет себя более похоже на эти классы Java, чем fmemopen.

4 23

4 ответа:

Если ваша операционная система предоставляет fmemopen , возможно, это будет соответствовать вашей цели.

В C++ (и вы добавили тег C++) можно написать функцию, принимающую произвольный поток ввода / вывода. Так как std::stringstream или std::ofstream являются производными классами, вы можете передать их в эту функцию в равной степени. Пример:

#include <iostream> // for std::cout
#include <fstream> // for std::ofstream
#include <sstream> // for std::stringstream

void write_something(std::ostream& stream) {
  stream << "Hello World!" << std::endl;
}

int main() {
  write_something(std::cout); // write it to the screen
  {
    std::ofstream file("myfile.txt");
    write_something(file); // write it into myfile.txt
  }
  {
    std::stringstream stream;
    write_something(stream); // write it into a string
    std::cout << stream.str() << std::endl; // and how to get its content
  }
}

И аналогично с std::istream вместо std::ostream, Если вы хотите прочитать данные:

void read_into_buffer(std::istream& stream, char* buffer, int length) {
  stream.read(buffer, length);
}

int main() {
  char* buffer = new char[255];

  {
    std::ifstream file("myfile.txt");
    read_into_buffer(file, buffer, 10); // reads 10 bytes from the file
  }
  {
    std::string s("Some very long and useless message and ...");
    std::stringstream stream(s);
    read_into_buffer(stream, buffer, 10); // reads 10 bytes from the string
  }
}

Как метко указывает Ise, есть функция для этого fmemopen в POSIX 2008, и она поддерживается в Linux. Используя POSIX 2004 , вероятно, ближе всего к этому было бы использовать временный файл:

// This is for illustration purposes only. You should add error checking to this.
char name[] = "/tmp/name-of-app-XXXXXX";
int temporary_descriptor = mkstemp(name);
unlink(temporary_descriptor);
FILE* file_pointer = fdopen(temporary_descriptor, "r+");
// use file_pointer ...

Проще сделать наоборот; то есть иметь функцию, которая записывает в память, а затем использовать ее как для записи в память, так и для записи в файл. Вы можете использовать mmap так, что кусок памяти в конечном итоге будет поддерживаться файл, и запись в эту память записывает данные в соответствующий файл.

Если вы используете std::istream и std::ostream вместо низкоуровневых объектов C FILE*, то C++ удобно предоставляет std::istringstream и std::ostringstream для чтения/записи строк.

Вы заметите, что почти все функции в C, которые начинаются с "f "и которые работают с файлами, имеют эквиваленты, начинающиеся с" s", которые работают со строками. Возможно, лучшим подходом было бы разработать интерфейс для ввода-вывода, который не является специфичным для либо файлы, либо строки, а затем предоставить реализации, которые подключаются к файлам и строкам соответственно. Затем реализуйте свою основную логику в терминах этого интерфейса, а не в терминах низкоуровневого ввода-вывода C и C++, что также дает возможность для будущих расширений, таких как поддержка сетевых файлов со встроенной поддержкой сжатия, дублирования и т. д.

С файлом, сопоставленным с памятью. Это относится к конкретной платформе, поэтому вам нужно будет найти информацию о создании mem-сопоставленных файлов в вашей целевой системе(системах). Я считаю, что версия posix - это mmap. В любом случае, поиск "файла с отображением памяти" в google должен найти кучу справок.