I am trying to read a text file into memory without any knowledge of
how long each line will be. I am looking to store each line in a
linked list structure, however, I am unsure of how to dynamically
allocate space for each line.
This is how I set up the linked list...
typedef struct node {
char *line;
struct node *next;
} linkedlist;
linkedlist* createlinkedlist(void) {
linkedlist* head;
head = (linkedlist *)malloc(sizeof(linkedlist));
head = malloc(sizeof *head);
You don't need to cast the result of malloc() in C, and in fact doing
so may mask a type mismatch error if you forget to #include stdlib.h
or otherwise don't have a prototype for malloc() in scope.
You also don't have to worry about syncing up typenames everywhere.
Finally, *always* check the result of malloc().
if (!head)
/* handle out-of-memory condition */
head->line = NULL;
head->next = NULL;
return head;
}
void addnode(linkedlist* list, char *line) {
linkedlist* freespot;
linkedlist* newnode;
freespot = list;
while (freespot->next != NULL)
freespot = freespot->next;
newnode = (linkedlist *)malloc(sizeof(linkedlist));
newnode = malloc(sizeof *newnode);
Same comment as above.
If it were me, I'd create a new copy of the line, rather than just
storing the pointer; if the pointer you passed in later gets free()'d
or changed, then you've lost your data.
newnode->line = malloc(strlen(line) + 1);
if (newnode->line)
strcpy(newnode->line, line);
else
/* handle out-of-memory condition */
newnode->next = NULL;
freespot->next = newnode;
}
So with this in place, how can I read in variable length lines,
malloc() the proper storage for each and pass the pointer to
addnode()?
Here's how I've done it in the past (untested, probably some holes):
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define BUFSIZE ... /* Big enough to handle most lines of text,
but not so big as to be a waste of space.
Exact value will depend on what you're reading.
*/
typedef enum {
NEXTLINE_OK,
NEXTLINE_MEM, /* Could not allocate or extend buffer */
NEXTLINE_ERR, /* Read error on input */
NEXTLINE_EOF /* Hit end of file */
} NextlineStatus;
NextlineStatus GetNextLine(FILE *stream, char **line, size_t *length)
{
char inputBuffer[BUFSIZE];
int done = 0;
NextLineStatus status;
*length = 0;
/**
* Read the next BUFSIZE-1 characters from the input
* stream.
*/
while (!done && fgets(inputBuffer, sizeof inputBuffer, stream))
{
/**
* Attempt to extend the buffer by the number of
* characters read plus 1.
*
* The first time through, this just allocates
* a new buffer.
*
* We use tmp because it's not guaranteed that
* realloc will succeed, and we don't want
* to overwrite line in that case.
*/
char *tmp = realloc(*line, *length + strlen(inputBuffer) + 1);
if (!tmp)
{
/**
* realloc failed. For now, leave line and length alone,
* and just return the out of memory status.
*/
status = NEXTLINE_MEM;
done = 1;
}
else
{
*line = tmp;
*length += strlen(inputBuffer) + 1;
/**
* Look for the newline character in the
* input buffer. If it's present,
* then we've read the whole line;
* replace the newline with a null
* terminator and set the done
* flag.
*/
if (strchr(inputBuffer, '\n'))
{
done = 1;
status = NEXTLINE_OK;
*strchr(inputBuffer, '\n') = 0;
}
/**
* Append the buffer to the line
*/
strcat(*line, inputBuffer);
}
}
/**
* If fgets() fails before the done flag is set, then we've
* either hit an end-of-file condition or an error.
*/
if (!done)
{
if (feof(stream))
{
status = NEXTLINE_EOF;
}
else
{
status = NEXTLINE_ERR;
}
}
return status;
}
int main(void)
{
linkedlist *myList = createlinkedlist();
NextLineStatus status = NEXTLINE_OK;
FILE *inFile;
char *nextLine = NULL;
size_t lineLength = 0;
...
while (status == NEXTLINE_OK)
{
char *nextLine;
size_t lineLength;
status = GetNextLine(inFile, &nextLine, &lineLength);
if (status == NEXTLINE_OK)
{
addnode(myList, nextLine);
free(nextLine);
lineLength = 0;
}
}
...
}