Make a file pointer read/write to an in-memory location
Solution 1:
If your operating system provides fmemopen, probably it will meet your purpose.
Solution 2:
In C++ (and you added the C++ tag) you can write a function accepting an arbitrary input/output stream. Since std::stringstream
or std::ofstream
are derived classes you can pass both of them equally into this function. An example:
#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
}
}
And analogously with std::istream
instead of std::ostream
if you want to read the data:
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
}
}
Solution 3:
As Ise aptly points out, there is a function for this fmemopen in POSIX 2008 and that is supported on Linux. Using POSIX 2004, probably the closest thing to that would be to use a temporary file:
// 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 ...
It's easier to do the reverse; that is, to have a function that writes into memory, and then to use that both to write into memory and also to write to a file. You can use mmap so that a chunk of memory ends up being backed by a file, and writing into that memory writes the data to the associated file.
If you use std::istream
and std::ostream
instead of low-level C FILE* objects, then C++ conveniently provides std::istringstream
and std::ostringstream
for reading/writing strings.
You will note that almost all of the functions in C that begin with an "f" and that operate on files, have equivalents beginning with "s" that operate on strings. A perhaps better approach would be to design an interface for I/O that is not specific to either files or strings, and then provide implementations that connect to files and strings, respectively. Then implement your core logic in terms of this interface, instead of in terms of low-level C and C++ I/O. Doing that also has the benefit of allowing for future extensions, such as supporting network files with builtin support for compression, duplication, etc.