C : Load data from file to the linked list - c

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.

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.

C: Adding data from text file into a singly linked list

I'm recently started learning C programming. I have some java experience so I know my way around codes, I like to think..
This little thing I'm working on is killing me.
I'm trying to make a program that read lines from a text file -> store it in a singly linked list -> print out the singly linked list
This is my code so far:
typedef struct node {
char *data;
struct node *next;
} node;
node *start = NULL;
node *current;
void add(char *line) {
node *temp = malloc(sizeof(node));
// This line under I believe where my problem is...
temp->data = line;
temp->next = NULL;
current = start;
if(start == NULL) {
start = temp;
} else {
while(current->next != NULL) {
current = current->next;
}
current->next = temp;
}
}
This is my function for reading the file and sending characters to the add function
void readfile(char *filename) {
FILE *file = fopen(filename, "r");
if(file == NULL) {
exit(1);
}
char buffer[512];
while(fgets(buffer, sizeof(buffer), file) != NULL) {
// I've tried to just send i.e: "abc" to the add function
// which makes the program work.
// like: add("abc");
// my display method prints out abc, but when I'm sending buffer
// it prints out nothing
// Thing is, I've spent way to much time trying to figure out what
// I'm doing wrong here...
add(buffer);
}
fclose(file);
}
I'm sure this is a fairly simple problem, but I've spent way too much time with this problem.
And if there is anything other that look off/could be better I appreciate feeback on that aswell :)
Try:
temp->data = strdup(line);
to duplicate (make a copy of) what line points at.
Otherwise every line points at the buffer which is getting overwritten with each new line.
You need to allocate memory for the string - each line is read into buf and so you need to copy it out or it will be overwritten with subsequent lines. I suggest one of two approaches, the first being the simplest with what you already have, but the second being better as you only need to do one free() for each object.
The first one is just a single change to your add() function:
temp->data = malloc(strlen(line)+1);
strcpy(temp->data, line);
Now, when you want to free an object in your linked list you must first call free() on data and then free() on the object itself.
However you can change the structure slightly and then you can allocate the whole object in one go:
typedef struct node {
struct node *next;
char data[0];
} node;
Then your add() function would look like this:
void add(char *line) {
node *temp = malloc(sizeof(node)+strlen(line)+1);
strcpy(temp->data, line);
temp->next = NULL;
current = start;
if(start == NULL) {
start = temp;
} else {
while(current->next != NULL) {
current = current->next;
}
current->next = temp;
}
}
Note that of course you should do error checking after each malloc() in production code. When you are done with an object a single free() is enough to free the whole structure.
Edit: The "array length 0" feature is a GCC specific extension, as noted by #crashmstr in the comments. If you use an array length of 1, it should work in any compiler though:
typedef struct node {
struct node *next;
char data[1];
} node;
Since an extra byte is already allocated in this case, the malloc() call in the add() function would then become:
node *temp = malloc(sizeof(node)+strlen(line)+1-1);
(of course the +1-1 can be omitted, but it is just to show that we still need space for the null terminator but an extra byte is already included in the sizeof).

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

Creating a linked list from a text file

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;

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