<<< Split from ancient reading a file line by line and char * >>>
I wanted this problem solved, and me not being satisfied with the results I found, I implemented my own code that performs this.
Code:
/************************************************************************************
* Author: Michael Bentley
* Date Created: June 28, 2011
* Description: This library file provides three useful methods and one struct that
* are useful for loading the contents of a file into memory and
* separated by lines.
* Note: All newlines are removed in all of the provided methods
*
* Functions:
* char* read_one_line (FILE *file)
* Reads from the current location in the file to the end of a line
* or the end of the file. No matter how long the line is, this
* function will allocate memory just large enough to fit the line
* without the newline character. The user that calls this function
* is responsible for freeing the pointer that is returned.
*
* lines* load_file_to_memory (char *filename)
* Opens the file and creates a linked list structure containing the
* lines in the file. The user that calls this function is
* responsible for freeing the linked list that is returned by
* calling the free_lines() function in this library.
*
* void free_lines (lines* head_node)
* Frees a previously allocated linked list. This function will
* traverse the linked list and free all of the memory.
*
************************************************************************************/
#ifndef __MIKE_FILE_TO_MEMORY_H
#define __MIKE_FILE_TO_MEMORY_H
// A linked list containing the lines of a file
typedef struct { // lines
lines* prev;
lines* next;
char* line;
} lines;
char* read_one_line (FILE *file) {
// The size of each read. If too large, this method will waste memory
// when the line isn't too long. If too small, the linked list will be
// too long when reading very long lines.
int buffer_size = 512;
// We need to account for a very large line, so we will make a linked list
// then find out how long the line was and malloc that size in one go.
lines* head_buffer;
lines* current_buffer, prev_buffer;
while (feof(fp) != NULL && ferror(fp) != NULL) {
// Allocate the memory and link it onto the end of our linked list
prev_buffer = current_buffer;
current_buffer = (lines*) malloc (sizeof(lines));
current_buffer->line = (char*) malloc (buffer_size * sizeof(char));
if (prev_buffer != NULL) prev_buffer->next = current_buffer;
else head_buffer = current_buffer;
current_buffer->prev = prev_buffer;
// Read the rest of the line, or at least as much as the buffer can
// hold.
fgets (current_buffer->line, buffer_size, file);
// If we reached the end of the line, exit the loop
int length_filled = strlen(current_buffer->line);
if (current_buffer->line[length_filled - 1] == '\n') { // newline character found
current_buffer->line[length_filled - 1] = '\0';
break;
}
else if (feof(fp) == NULL) // This was the last line in the file
break;
}
if (ferror(fp))
return NULL;
// Now find out how long the line was
current_buffer = head_buffer;
int line_size = 0;
while (current_buffer->next != NULL) {
line_size += buffer_size;
current_buffer = current_buffer->next;
}
line_size += strlen(current_buffer->line);
// Allocate the return string memory size and copy the line into it
char* return_string = (char*) malloc (line_size * sizeof(char));
current_buffer = head_buffer;
while (current_buffer != NULL) {
strcpy (&(return_string[strlen(return_string)]), current_buffer->line);
current_buffer = current_buffer->next;
}
// Free the temperary memory used by this function
free_lines (head_buffer);
return return_string;
}
lines* load_file_to_memory (char *filename) {
FILE *file = fopen (filename, "r");
lines* head_node;
lines* current_line, prev_line;
while (feof(fp) != NULL && ferror(fp) != NULL) {
// Allocate the memory and link it onto the end of our linked list
prev_line = current_line;
current_line = (lines*) malloc (sizeof(lines));
if (prev_line != NULL) prev_line->next = current_line;
else head_node = current_line;
current_line->prev = prev_line;
// Read the current line in the file.
current_line->line = read_one_line (file);
}
if (ferror(fp))
return NULL;
fclose (file);
}
void free_lines (lines* head_node) {
lines* current_line = head_node;
while (current_line != NULL) {
free (current_line->line);
if (current_line->next != NULL) {
current_line = current_line->next;
free (current_line->prev);
}
else {
free (current_line);
current_line = NULL;
}
}
}
#endif // __MIKE_FILE_TO_MEMORY_H