Common I/O Operations in C, C++, and POSIX
This guide covers essential input/output operations across three paradigms:
- File-to-memory and memory-to-file transfers
- In-memory stream operations
- Buffered versus unbuffered POSIX I/O
File and Memory Operations
The following examples demonstrate reading from and writing to files using standard C and C++ libraries.
// Read file contents into memory buffer
void read_file_content()
{
char buffer[BUFFER_SIZE];
size_t bytes_read = 0;
FILE *file_handle = fopen("data.txt", "r+");
if (file_handle != NULL) {
bytes_read = fread(buffer, sizeof(char), BUFFER_SIZE, file_handle);
printf("Bytes read: %zu\n", bytes_read);
printf("Content:\n%s\n\n", buffer);
fclose(file_handle);
}
}
// Write memory buffer contents to file
void write_file_content()
{
const char *data = "Sample text data";
char buffer[BUFFER_SIZE];
strncpy(buffer, data, BUFFER_SIZE);
size_t bytes_written = 0;
FILE *file_handle = fopen("data.txt", "w+");
if (file_handle != NULL) {
bytes_written = fwrite(buffer, sizeof(char), strlen(buffer), file_handle);
printf("Bytes written: %zu\n\n", bytes_written);
fclose(file_handle);
}
}
// Read formatted data from file using scanf semantics
void scan_file_content()
{
char buffer[BUFFER_SIZE];
FILE *file_handle = fopen("data.txt", "r+");
if (file_handle != NULL) {
while (fscanf(file_handle, "%s", buffer) != EOF) {
printf("Scanned: %s\n", buffer);
}
printf("\n");
fclose(file_handle);
}
}
// C++ stream-based file reading
void read_with_ifstream()
{
ifstream input("data.txt");
string token;
while (input >> token) {
cout << "Token: " << token << endl;
}
input.close();
cout << endl;
}
// Write formatted data to file
void write_formatted_output()
{
const char *message = "Formatted output";
FILE *file_handle = fopen("output.txt", "w+");
int chars_written = fprintf(file_handle, "%s", message);
printf("Characters written: %d\n", chars_written);
fclose(file_handle);
}
// C++ stream-based file writing
void write_with_ofstream()
{
ofstream output("output.txt", ios::app | ios::out);
string content = "Appended content";
output.write(content.c_str(), content.length());
output.close();
}
In-Memory Stream Operations
These operations handle string buffers instead of files, enabling string parsing and formatting.
// Parse formatted data from string buffer
void parse_string_buffer()
{
const char *source = "Parse this string";
char destination[BUFFER_SIZE];
sscanf(source, "%s", destination);
printf("Parsed result: %s\n\n", destination);
}
// C++ string stream for tokenization
void tokenize_with_stringstream()
{
string text = "Tokenize this sentence";
string word;
istringstream stream(text);
while (stream >> word) {
cout << "Word: " << word << endl;
}
}
// Format data into string buffer
void format_into_string()
{
const char *source = "Format this";
char destination[BUFFER_SIZE];
sprintf(destination, "%s", source);
printf("Formatted: %s\n\n", destination);
}
// C++ string stream for building strings
void build_string_with_stream()
{
string data = "String data";
ostringstream stream;
stream << data;
string result = stream.str();
cout << "Result: " << result << endl;
}
Buffered and Unbuffered POSIX I/O
POSIX provides both buffered and unbuffered I/O operations. The unbuffered functions (read/write) interact directly with the kernel, while buffered versions reduce system call overhead.
// Robust read function handling partial reads
ssize_t read_all(int file_descriptor, void *user_buffer, size_t requested_bytes)
{
size_t remaining = requested_bytes;
ssize_t bytes_received;
char *ptr = user_buffer;
while (remaining > 0) {
bytes_received = read(file_descriptor, ptr, remaining);
if (bytes_received < 0) {
if (errno == EINTR) {
bytes_received = 0; // Retry on signal interrupt
} else {
return -1; // Actual error occurred
}
} else if (bytes_received == 0) {
break; // End of file reached
}
remaining -= bytes_received;
ptr += bytes_received;
}
return (requested_bytes - remaining);
}
// Robust write function handling partial writes
ssize_t write_all(int file_descriptor, void *user_buffer, size_t requested_bytes)
{
size_t remaining = requested_bytes;
ssize_t bytes_sent;
char *ptr = user_buffer;
while (remaining > 0) {
bytes_sent = write(file_descriptor, ptr, remaining);
if (bytes_sent <= 0) {
if (errno == EINTR) {
bytes_sent = 0; // Retry on signal interrupt
} else {
return -1; // Write failed
}
}
remaining -= bytes_sent;
ptr += bytes_sent;
}
return requested_bytes;
}
// Buffered I/O context structure
typedef struct {
int fd; // File descriptor
int available; // Bytes currently in buffer
char *current_ptr; // Current position in buffer
char internal_buffer[8192]; // Internal read buffer
} buffered_io_t;
// Buffered read with internal buffering
static ssize_t buffered_read(buffered_io_t *ctx, char *dest, size_t count)
{
int bytes_to_copy;
// Refill internal buffer when empty
while (ctx->available <= 0) {
ctx->available = read(ctx->fd, ctx->internal_buffer, sizeof(ctx->internal_buffer));
if (ctx->available < 0) {
if (errno != EINTR) {
return -1; // Error (not signal interruption)
}
// Retry on EINTR
} else if (ctx->available == 0) {
return 0; // EOF reached
} else {
ctx->current_ptr = ctx->internal_buffer; // Reset pointer
}
}
// Transfer min(count, available) bytes
bytes_to_copy = count;
if (ctx->available < count) {
bytes_to_copy = ctx->available;
}
memcpy(dest, ctx->current_ptr, bytes_to_copy);
ctx->current_ptr += bytes_to_copy;
ctx->available -= bytes_to_copy;
return bytes_to_copy;
}
Conceptual Overview
The fundamental pattern across these APIs involves redirecting the data source or destination. The following table illustrates this principle:
// C standard library - destination varies
printf() -> stdout scanf() <- stdin
fprintf() -> FILE* fscanf() <- FILE*
sprintf() -> char* sscanf() <- char*
// C++ stream library - mirrors C patterns with buffering
ostream istream
ofstream ifstream
ostringstream istringstream
Buffered implementations like the custom buffered_read() demonstrate significant performance advantages by minimizing transitions between user space and kernel space, allowing larger chunks of data to be transferred in single system calls.