Creating a linked list from a text file - c

I'm teaching myself about linked lists and have proposed a basic problem to solve. I want to line by line read a text file that will have names and add each name to my linked list.
An example of the text file would be:
John
Jacob
Jingleheimer
Smith
I am having trouble figuring out how to dynamically add to my proposed linked list. Here is what I have so far.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
struct node {
char *name;
struct node* next;
};
static const char* fileName = "test.txt";
FILE *fp = fopen(fileName,"r");
char *line = NULL;
size_t len = 0;
ssize_t read;
struct node* head = NULL; // first node
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1)
{
//add line of text to linked list
}
if (line)
free(line);
exit(EXIT_SUCCESS);
}
Any pointers into the right direction would be helpful.

Ok, to do this you need to first allocate a node entry, then copy the line you just read into it. Then add it to the list. (I've left out error handling, such as malloc returning NULL).
/* This will store the last read line first */
while ((read = getline(&line, &len, fp)) != -1)
{
struct node *n = malloc(sizeof(*n));
n->name = strdup(line); /* copy the line since it can get reused by getline */
n->next = head;
head = n;
}

Allocate memory to hold the string, set that memory = to the string.
Then
node *next = malloc(sizeof(node));
next->name = name;
next->next = NULL;
head->next = next;
head = next;

Related

How can I read any file into a linked list?

I'm supposed to create a program that can read any file into a linked list. This is what I came up with so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BUFFER_SIZE 1024
typedef struct list {
char *string;
struct list *next;
} LIST;
void print_list(LIST *head) {
LIST *current = head;
while (current != NULL) {
printf("%s", current->string);
current = current->next;
}
}
void push(LIST **head, FILE **fp) {
char line[MAX_BUFFER_SIZE];
LIST *node, *current = *head;
while(fgets(line, sizeof(line), *fp)) {
node = malloc(sizeof(LIST));
if (node == NULL) exit(1);
node->string = strdup(line);
node->next = NULL;
if(current == NULL) {
*head = node;
current = node;
} else {
current->next = node;
current = current->next;
}
}
}
int main(int argc, char *argv[]) {
FILE *fp = fopen(argv[1], "r");
LIST *head = NULL;
push(&head, &fp);
fclose(fp);
print_list(head);
return 0;
}
When comparing the contents of the linked list with the contents of the input file this comparison succeeds when using a .txt file but fails when using a file with binary data. This suggests that my program changes the contents of the binary file.
What am I doing wrong?
Random binary data can contain characters that are not printable. Or might contain zeroes, which is the string terminator and thus terminate your strings early. Just don't read and write raw binary data as strings or using string functions, it will simply not work as you expect.
If you want to read and write arbitrary data of any kind, use e.g. fread and fwrite instead, and open your files in binary mode.
Since you are using Linux, you can use POSIX.1 getline() to read lines, including lines with embedded NUL bytes; you do need to write those lines using fwrite().
For the linked list, you should include a length field for fwrite(). I'd also make the linked list data element a flexible array member:
struct node {
struct node *next;
size_t size;
char data[];
/* Note: data[size+1], data[size] == '\0'.
This is not necessary for correct operation,
but allows one to assume there is always at
least one char in data, and the data is followed
by a nul byte. It makes further use of this
structure easier. */
};
struct node *node_new(const char *data, size_t size)
{
struct node *n;
n = malloc(sizeof (struct node) + size + 1);
if (!n) {
fprintf(stderr, "node_new(): Out of memory.\n");
exit(EXIT_FAILURE);
}
n->next = NULL;
n->size = size;
if (size > 0)
memcpy(n->data, data, size);
n->data[size] = '\0';
return n;
}
When reading lines, it is easiest to prepend the lines to the list:
struct node *list = NULL;
struct node *curr;
char *line = NULL;
size_t size = 0;
ssize_t len;
while (1) {
len = getline(&line, &size, stdin);
if (len < 0)
break;
curr = node_new(line, (size_t)len);
curr->next = list;
list = curr;
}
list = list_reverse(list);
When done, you reverse the list, to get the first read line at the beginning of the list:
struct node *list_reverse(struct node *curr)
{
struct node *root = NULL;
struct node *next;
while (curr) {
next = curr->next;
curr->next = root;
root = curr;
curr = next;
}
return root;
}
To write each line to a stream, you use for example fwrite(node->data, node->size, 1, stdout).
If the output stream is not a local file, but a pipe or socket, fwrite() can return a short count. It is not an error; it only means that only part of the data could be written. To cater for those cases, you can use two helper functions: one to ensure all of the data is written, even when writing to a pipe, and another to scan through the list, using the first one to output each line:
static int fwriteall(const char *data, size_t size, FILE *out)
{
size_t n;
while (size > 0) {
n = fwrite(data, 1, size, out);
if (n > 0) {
data += n;
size -= n;
} else
return -1; /* Error */
}
return 0; /* Success */
}
int list_writeall(FILE *out, struct node *list)
{
for (; list != NULL; list = list->next)
if (list->size > 0)
if (fwriteall(list->data, list->size, out)
return -1; /* Error */
return 0; /* Success */
}
Instead of getline(), you can read chunks of some predefined size using fread():
struct node *read_all(FILE *in, const size_t size)
{
struct node *list = NULL;
struct node *curr;
size_t used;
while (1) {
curr = malloc(sizeof (struct node) + size + 1);
if (!curr) {
fprintf(stderr, "read_all(): Out of memory.\n");
exit(EXIT_FAILURE);
}
size = fread(curr->data, 1, size, in);
if (used > 0) {
/* Optional: Optimize memory use. */
if (used != size) {
void *temp;
temp = realloc(curr, sizeof (struct node) + used + 1);
/* Reallocation failure is not fatal. */
if (temp) {
curr = temp;
curr->size = used;
}
}
}
curr->data[used] = '\0';
curr->next = list;
list = curr;
}
return list_reverse(list);
}
The function returns the reversed list (i.e., with first line first in list). After calling the function, you should check using ferror(in) whether the entire input stream was read, or if there was an error.

Program execution interrupted when certain String is read from File

I have a small problem with my code and hope you can help me.
This program below reads names that are written in a txt-file and stores them in a linked list and prints them back out on the command line.
The list consists of the following names:
Gustav Mahler
Frederic Chopin
Ludwig van Beethoven
Johann-Wolfgang Von-Goethe
But when I run the program, the execution of the program is interrupted, either before printing the list or after.
If I remove the last line it works perfectly, but when I add it back in to the list or replace it with a random combination like "jlajfi3jrpiök+kvöaj3jiijm. --aerjj" it stops again.
Can somebody please explain to me why the program execution gets interrupted?
Thank you in advance ! :)
Here's the Program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct list {
char* name;
struct list *next;
}NODE;
char * getString(char *source);
int main() {
FILE *fpointer = NULL;
char filename[100];
puts("\nEnter the name of the file:\n");
gets(filename);
if((fpointer = fopen(filename, "r")) == NULL ) {
printf("\nThe file you have chosen is not valid.\n");
return 1;
}
char buffer[200];
NODE *head = NULL;
NODE *current = NULL;
while(fgets(buffer, 200, fpointer) != NULL) {
NODE *node = (NODE *) malloc(sizeof(NODE));
node -> next = NULL;
node -> name = getString(buffer);
if(head == NULL) {
head = node;
} else {
current -> next = node;
}
current = node;
}
current = head;
while(current) {
printf("%s", current -> name);
current = current -> next;
}
return 0;
}
char * getString(char* source) {
char* target = (char*) malloc(sizeof(char));
strcpy(target, source);
return target;
}
In getString, you're not allocating enough space for the string you want to copy:
char* target = (char*) malloc(sizeof(char));
This is only allocating space for a single character. You need enough for the length of the string, plus 1 more for the null terminating byte:
char* target = malloc(sizeof(strlen(source) + 1);
You could actually replace the entire function with a call to strdup, which does the same thing.
Also, don't cast the return value of malloc, and never use gets.

C : Load data from file to the linked list

I wrote a program to create a simple dictionary. I want to save the dictionary data to a file and when I run the program next time I want to load that data into the linked list.
This is my code:
struct node{ //structure for dictionary
char word[20];
char meaning[5][100]; //to store max five meanings
struct node *next;
};
//This is how I'm saving data to the file. I guess it's working, because size of the file increases..
void WriteData(struct node *head)
{
FILE *fp = fopen("dictionary.data", "wb");
if(fp == NULL)
{
printf("Error opening file..\n");
return;
}
while(head != NULL)
{
fwrite(head->word, sizeof(head->word), 1, fp);
fwrite(head->meaning, sizeof(head->meaning), 1, fp);
head = head->next;
}
fclose(fp);
}
But how to read file and load the data back into the linked list?
You used fwrite() function, use fread() now :)
Here is a pseudo code. Leaving converting to C/C++ and error handling to you.
node *head - nullptr;
node **tail = &head;
while (not end of file)
{
*tail = allocate_and_nullify_memory();
fread((*tail)->word, size_of_head_word, 1, fp);
fread((*tail)->meaning, size_of_meaning, 1, fp);
//Move the insertion point
tail = &(*tail)->next;
}
in order you have to:
1) read a file with fgets/getline/read/fread functions;
2) for every single line readed you have to add it to your list, example:
while (read(fd, buffer, dim) > 0) {
struct node* tmp = malloc(sizeof(struct node));
strncpy(tmp->word, buffer, dim);
tmp->next = NULL;
last->next = tmp;
last = last-> next;
}
Where buffer contains your line, and last is the pointer to last element of the list.

Circular Doubly Linked List, Print Function

I need to create a circular doubly linked list with a sentinel node which is supposed to read data from a file and insert it in the list, than perform some operations with it. For now I'm stuck on a simple print function which won't print from a list for some reason. The data in the file is in the form of strings,
example: "Popular Sorting Algorithms,
Bubble Sort, Merge Sort, "empty line", etc
Here is my code so far:
Header file contains:
typedef struct NODE {
struct NODE *prev;
char *value;
struct NODE *next;
} NODE;
typedef struct LIST {
int count;
struct NODE *next;
struct NODE *prev;
} LIST;
int InsertEnd(NODE *head, char * value, int *lineCount);
void printLines(int *lineCount);
void Traverse(NODE *head);
Main contains:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "header.h"
int main()
{
int lineCount = 0;
NODE *head;
head = (NODE *)malloc(sizeof(NODE)); /* creates head node dynamically */
head->next = NULL; /* points to first element */
head->prev = NULL; /* points to last element */
head->value = "HEAD"; /* not needed, but it was pretty useful when debugging */
//*********BEGIN OF OPEN FILE FUNCTION
FILE* fp;
char *fname = NULL;
fname = (char *)malloc(200); <<<<<===== I would prefer to set the size dynamically adjusting but I dont know how
printf("Reading file input.txt\n");
//Checks if the file us unable to be opened, then it shows the error message
if ( !(fp = fopen("input.txt", "r")))
{
printf("\nError, Unable to open the file for reading \n");
exit(100);
}
//*********BEGIN OF READ FROM FILE FUNCTION
while (!feof(fp))
{
fgets(fname, 150, fp); //reads the file and stores in buffer
fname[strlen(fname) - 1] = '\0'; // reduces empty strings for input
if (fname != '\0')
{
InsertEnd(head, fname, &lineCount);
//printf("%s\n", head->next->value); <<<<==== If uncomment this print function would work properly but only in this context
}
else
{
printf("Error'\n"); // For debugging
}
}
Traverse(head); // Print Function Should Be Working in Here
printf("Debugging print\n");
printLines(&lineCount); // Shows Line Count
return 0;
}
// Function inserts a new node at the end of the LIST
int InsertEnd(NODE *head, char * value, int* lineCount)
{
int lineCounter = *lineCount;
/* create new node */
NODE *newnode;
newnode = (struct NODE *)malloc(sizeof( struct NODE));
newnode->value = value;
/* placing new node in LIST */
if (head->next == NULL) /* LIST was empty */
{
newnode->next = head;
newnode->prev = head;
head->next = newnode;
head->prev = newnode;
lineCounter++; // Increment line counter
}
else /* LIST wasn't empty */
{
newnode->next = head;
newnode->prev = head->prev;
head->prev->next = newnode; /* adjust node that was previously last */
head->prev = newnode; /* adjust head node */
lineCounter++; // Increment line counter
}
*lineCount = lineCounter;
return lineCount;
}
// This function prints how many lines there are in the LIST, but I need to get rid of the empty spaces
void printLines(int *lineCount)
{
printf("Line counter is %d", *lineCount); // Shows the number of lines, but doesn't skip empty ones.
}
void Traverse(NODE *head)
{
NODE *current = head;
printf("Forward:");
while (current!= head->prev)
{
printf("%s \n", current->value);
current = current->next;
}
printf("\n");
}
Therefore, I have several problems so far:
1) I need to get rid of empty strings in my list most likely. What would be a better approach, to get rid of them while reading or just not displaying when printing? How would I do this exactly?
2) How can I fix my print(traverse) function and whats wrong there?
3) Additionally all of this should be going on through the menu manager which would prompt for a command ( I got this right I think). But there are some functions that I don't know how to implement. For example when used hits "I" it should call Insert functions and prompt the user to enter two more values and , and later insert at the appropriate . How would I do that? Example "I 1 8"
4) Similarly to the previous one, there should be List function which should print lines between specific values. User input format should be "L to " list inclusively. Example "L 2 5"
5) Similarly to previous there should be a delete function with the format "D " inclusively. Example "D 3 7"
6) And the very last is the Save function in the format "S " Example "S output.txt"
Thank You for the help!
I see at least these issues in your code,
In main()
if (fname != '\0')
this should be
if (fname[0] != '\0')
In InsertEnd()
newnode->value = value;
should be
newnode->value = strdup(value);
In you code there should be some correctness which is very help full first as per your request you need to allocate buffer dynamically but not know file length so it can be achived by this one
int sz;
printf("Reading file input.txt\n");
//Checks if the file us unable to be opened, then it shows the error message
if ( !(fp = fopen("sample.txt", "r")))
{
printf("\nError, Unable to open the file for reading \n");
exit(100);
}
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
printf("size of file %d\n",sz);
fname = (char *)malloc(sz);
rewind(fp);
Now for reading content from file you checked fname to \0 which is not correct i corrected your while..loop.
while (!feof(fp))
{
if(fgets(fname,256, fp) != 0)
{
fname[strlen(fname) - 1] = '\0'; // reduces empty strings for input
InsertEnd(head, fname, &lineCount);
}
else
{
printf("Error'\n"); // For debugging
}
}

How to read file content into a struct line by line in C

Here, I am trying to read the contents of a file line by line and create a struct for each line. The problem is when I print the list of words, every single one of them is the last word of the file (which is } in this sample). I believe since line changes frequently and I pass a pointer to a char, value of every struct changes as well. I've been trying to fix this problem for nearly a day without any luck. What's a good way to read every word into a struct and link each struct to the linked list?
Note that there are some helper methods used below. I've tested them multiple times and they are working.
Token struct
typedef struct token
{
char* value;
struct token* next;
}TOKEN;
File content
target1:
dependency1
{
command1,
command2
}
Main
TOKEN *head = NULL;
// represents each formatted line from the script file
char* line = malloc(161*sizeof(char));
FILE* fileRead = openFile("RawRules.txt", "r");
while((line = readLine(line, fileRead)) != NULL)
{
head = add(head, line);
}
displaylist(head);
freeNodes(head);
fclose(fileRead);
Add function Modified from http://cprogramminglanguage.net/singly-linked-list-c-source-code.aspx
TOKEN* add(TOKEN *head, char* value){
TOKEN *tmp;
if(head == NULL){
head=(TOKEN *)malloc(sizeof(TOKEN));
if(head == NULL){
printf("Error! memory is not available\n");
exit(0);
}
head-> value = value;
head-> next = head;
}else{
tmp = head;
while (tmp-> next != head)
tmp = tmp-> next;
tmp-> next = (TOKEN *)malloc(sizeof(TOKEN));
if(tmp -> next == NULL)
{
printf("Error! memory is not available\n");
exit(0);
}
tmp = tmp-> next;
tmp-> value = value;
tmp-> next = head;
}
return head;
}
readline function
// reads a line of a file into buffer
char* readLine(char* buffer, FILE* file) {
buffer = fgets(buffer, 161, file);
return buffer;
}
This did not fix the problem either
while(true)
{
char* ll = malloc(161*sizeof(char));
ll = readLine(ll, fileRead);
f(ll != NULL)
head = add(head, ll);
else
break;
}
Sorry, I progammed in C like billion years ago, so call me a noob!
In the add() function, you're simply assigning a char *, rather than allocating any new memory (and then copying) for each string. So each TOKEN ends up pointing at the original buffer. As you're using a single buffer at the top-level, you're overwriting it over and over again.
In short: You need a separate buffer for each line. One way (not necessarily the best way) is to do the following inside add():
int len = strlen(value);
...
tmp->value = malloc(len+1); /* +1 for null terminator */
strncpy(tmp->value, value, len+1);
Remember that at some point, you'll need to free() all of these extra buffers.

Resources