Re: Variable-sized lines of text in linked list



On Feb 28, 1:45 pm, Scottman <FonzoC...@xxxxxxxxx> wrote:
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.

newnode->line = line;

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;
}
}
...
}

Thanks!

Cheers,
Scott Nelson

.



Relevant Pages

  • Re: why the usage of gets() is dangerous.
    ... struct S { ... char c; ... clear at all where the buffer ends. ... making here, and I agreed already that bounded pointers, even while not ...
    (comp.lang.c)
  • Re: String to fixed buffer (and vice versa)
    ... > I would copy the characters of a string variable to a fixed character ... > in a struct in C#. ... internal unsafe fixed char ca; ... But the real question for you to answer is - why do I need fixed buffer?, ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: why the usage of gets() is dangerous.
    ... Or does it store the end ... all where the buffer ends. ... It is illegal to convert from a struct S * to a char *, except in the niggly case of a char or char array being the first member, in which case it must have the same address as the whole struct. ...
    (comp.lang.c)
  • Re: Pointer hell
    ... Alignment requirements are generally referred to as, ... I used a struct like so, which reflected the structure of the data ... char ca; ... because the buffer was obtained from callocand ...
    (comp.lang.c)