Reputation: 4289
I can make a file pointer write to a file with fopen(). But can I make a file pointer that will make it so calling functions such as fputc or fprintf will write to a pointer in memory? An example of this is ByteArrayOutputStream in java. Also: could I run it in reverse, where a library needs a file pointer to read from, so I allocate memory, and make a new file pointer that will read from this memory location but return EOF when the size of the chunk runs out? (like ByteArrayInputStream in Java). Is there a way to do this in C? For example:
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: To those reading this question years later, in addition to the accepted answer, you should look at open_memstream(3)
, which behaves more like these Java classes than fmemopen
does.
Upvotes: 28
Views: 32063
Reputation: 67224
Here's an example using fmemopen
that works on Linux. Apparently someone also wrote an fmemopen
for Windows
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char *fakeStdin = "The fake stdin input\n"
"The end of the stream will be at the null ->";
size_t streamBytes = strlen( fakeStdin );
FILE *f = fmemopen( (void*)fakeStdin, streamBytes, "rb" );
if( !f ) {
puts( "Couldn't open FILE* to memory" );
return 1;
}
char* buf = (char*)malloc( streamBytes + 1 );
size_t readBytes = fread( buf, 1, streamBytes, f );
printf( "Read %zu bytes from stream:\n", readBytes );
puts( buf );
free( buf );
fclose( f );
return 0;
}
Upvotes: 0
Reputation: 2949
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
}
}
Upvotes: 4
Reputation: 95479
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.
Upvotes: 3
Reputation: 11669
If your operating system provides fmemopen, probably it will meet your purpose.
Upvotes: 28