Reading a text file with data into a linked list - c

I am working on a project where I have to read the .txt file where I have data of four elements for each component, ex:
5 2 7 0.99 (which are the components id, inside node, outside node and reliability)
7 3 5 0.95
...
I want to read and write data into a linked list, over which I will later on be able to search for values and sign them to a new linked list. It's about a method of minimal paths in mechanical engineering used for calculating reliability of systems.
To test the code I just want to print out all the components that were put into the linked list. I get the right value for number of lines, but for components I just get out one random not all of them. Any kind of help will be much appreciated :)
Here is the code:
#include <stdlib.h>
#include <stdio.h>
struct Components_element {
int id;
int in_node;
int out_node;
float reliability;
struct Components_element *next_c;
};
struct Components_element *head_c = NULL;
int count_lines(char *filename)
{
int counter = 0;
char c;
FILE *ptr_sistem;
ptr_sistem = fopen(filename, "r");
if(ptr_sistem == NULL)
return 0;
while((c = fgetc(ptr_sistem)) != EOF)
if(c == '\n')
counter++;
fclose(ptr_sistem);
if(c != '\n')
counter++;
return counter;
}
struct Components_element *add_comp(struct Components_element *head_c, int id, int in_node, int out_node, float reliability)
{
struct Components_element *new;
struct Components_element *tail_c;
new = (struct Components_element*) malloc(sizeof(struct Components_element));
new->id = id;
new->in_node = in_node;
new->out_node = out_node;
new->reliability = reliability;
if(head_c == NULL)
return(new);
tail_c = head_c;
while(tail_c->next_c != NULL)
tail_c = tail_c->next_c;
tail_c->next_c = new;
return(head_c);
}
void write_out(struct Components_element *p)
{
while(p != NULL) {
printf("%d %d %d %f", p->id, p->in_node, p->out_node, p->reliability);
p = p->next_c;
}
printf("\n");
}
struct Components_element *empty(struct Components_element *p)
{
struct Components_element *tail_c;
while(p != NULL) {
tail_c = p;
p = p->next_c;
free(tail_c);
}
return(p);
}
main()
{
int i, id, in_node, out_node;
int n_lines;
float reliability;
struct Components_element *components;
FILE *ptr_file;
ptr_file = fopen("system.txt", "r");
if(ptr_file == NULL) {
printf("Cannot open file.\n");
return 0;
} else {
n_lines = count_lines("system.txt");
for(i = 0; i < n_lines; i++) {
fscanf(ptr_file, "%d %d %d %f", &id, &in_node, &out_node, &reliability);
components = add_comp(head_c, id, in_node, out_node, reliability);
++i;
}
}
printf("Number of lines: %d.\n", n_lines);
write_out(components);
empty(components);
fclose(ptr_file);
}

Writing the code is only a small fraction of what programming is about. The really hard part, is getting it to work as required.
To do this, we use various tools to aid with testing the code to make sure it works the way the programmer intended.
The most useful tool is the debugger. Learn how to use one and you can find your problem in a couple of minutes.
Sometimes, however, debuggers aren't always available and this is where things can get quite tricky and requires logging information (printf / fwrite / etc) and a lot of deductive reasoning.
But here, running the code through a debugger will show you your problem. I'll leave it as an exercise for you as you'd learn a lot more that way rather than having the answer spoon fed to you.

Related

Im trying to read a txt file into a linked list, that contains both integers and strings in a line, separated by commas

Im making a database, where the information about books and readers in a library are contained in two linked lists, and each line of the txt file contains the data of one book/peopleinlibrary.
Data of the books:
id;year;title;writer;isborrowed;\n
...
The id and the isborrowed(1or0) are integers, the rest are strings.
I know the atoi can convert the input lines to numbers, but when I use it while separating each line by commas, it just doesn't work, the printig is wrong and also i can't return with the *start/*begin pointer, where the list starts. By now im totally clueless because it's not the first method that I tried.
The part of the code:
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <stdlib.h>
typedef struct Konyv{
int id;
char *year;
char *title;
char *writer;
int ki;
struct Konyv*next;
}Konyv;
int main(){
Konyv*start=NULL;
FILE*f;
const char s[1] = ";";
int i;
f=fopen("konyv.adat.txt","r+");
Konyv*u;
if(f != NULL){
char line[1000];
while(fgets(line, sizeof line, f) !=NULL){
u= (Konyv*)malloc(sizeof(Konyv));
u->id = strtok(line, s);
u->id=atoi(u->id);
printf("%d ",u->id);
u->year = strtok(NULL,s);
printf("%s ",u->year);
u->title = strtok(NULL,s);
printf("%s ",u->title);
u->writer = strtok(NULL,s);
printf("%s ",u->writer);
u->ki = strtok(NULL,s);
u->ki=atoi(u->ki);
printf("%d",u->ki);
printf("\n");
u->next=NULL;
if(start==NULL){
start=u;
}
else{
Konyv *mozgo = start;
while (mozgo->next!= NULL){
mozgo = mozgo->next;
}
mozgo->next= u;
}
}
else{
printf("Error while opening.\n");
return 0;
}
printf("\n");
//test if start pointer is right by printig the list again(HELP)
Konyv* temp;
temp=start;
while(temp!=NULL) {
printf("%d ",temp->id);
printf("%s ",temp->year);
printf("%s ",temp->title);
printf("%s ",temp->writer);
printf("%d ",temp->ki);
printf("\n");
temp = temp->next;
}
free(u);
fclose(f);
return 0;
}
#WhozCraig is correct, and borrowing from this post you can see that we simply need to copy the data at the pointer to a new chunk of memory. For this we can use the strdup function included in string.h.
For example:
u->year = strtok(NULL,s);
becomes
u->year = strdup(strtok(NULL,s));
You can find documentation on strdup here for further reference.
Also, I don't want to leave you hanging here, the code you handed over didn't compile cleanly -- I had to complete some brackets.
One final thing storing anything other than an int in your id field is problematic. So,
u->id = strtok(line, s);
is an issue. Simple fix is,
u->id = atoi(strdup(strtok(line, s)));
but that is kind of dirty and hard to read for another programmer coming in to maintain code. I would advise taking the time to declare a variable just to temporarily store the token you are eventually going to duplicate into your struct.

Sorting structures from files

I recently got an assignment to sort members in a struct by last name and if they are the same to sort by first name. What i have so far only reads their name and age from the file but I am not properly grapsing how I would be able to sort it. So far I gathered the data from the file but im at a loss from there. I followed a code I saw but i didnt get a proper grasping of the process so i reverted back to step one.
struct Members{
int id;
char fname[50];
char lname[50];
int age;
}bio;
int main(){
int i=0;
FILE *fptr;
file = fopen("Members Bio.txt", "r");
while ( fscanf(file, "%d%s%s%d", &bio[i].id,bio[i].fname,bio[i].lname,&bio[i].age) != EOF)
{
printf("%d %s %s %d %d\n", bio[i].id,bio[i].fname, bio[i].lname, bio[i].age);
i++;
}
fclose(fptr);
}
Can anyone help me out on this one?
Code goes something like this for your case.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Members{
int id;
char fname[50];
char lname[50];
int age;
};
typedef int (*compare_func)(void*, void*);
int struct_cmp(void* s1, void* s2)
{
int l_result = strcmp(((struct Members*) s1)->lname, \
((struct Members*) s2)->lname);
if (l_result < 0)
return 1;
else if (l_result > 0)
return 0;
else
return (strcmp(((struct Members*) s1)->fname, \
((struct Members*) s2)->fname) < 0 ? 1 : 0);
}
void sort(void* arr,long ele_size,long start,long end,compare_func compare)
{
// Generic Recursive Quick Sort Algorithm
if (start < end)
{
/* Partitioning index */
void* x = arr+end*ele_size;
long i = (start - 1);
void* tmp=malloc(ele_size);
for (long j = start; j <= end - 1; j++)
{
if ((*compare)(arr+j*ele_size,x))
{
i++;
// Swap is done by copying memory areas
memcpy(tmp,arr+i*ele_size,ele_size);
memcpy(arr+i*ele_size,arr+j*ele_size,ele_size);
memcpy(arr+j*ele_size,tmp,ele_size);
}
}
memcpy(tmp,arr+(i+1)*ele_size,ele_size);
memcpy(arr+(i+1)*ele_size,arr+end*ele_size,ele_size);
memcpy(arr+end*ele_size,tmp,ele_size);
i= (i + 1);
sort(arr,ele_size,start, i - 1,compare);
sort(arr,ele_size,i + 1, end,compare);
}
}
int main()
{
FILE* fp;
int bio_max = 3;
struct Members bio[bio_max]; // Define bio to be large enough.
/* Open FILE and setup bio matrix */
/* For testing */
bio[0].id = 0;
strcpy(bio[0].fname, "");
strcpy(bio[0].lname, "Apple");
bio[0].age = 0;
bio[1].id = 1;
strcpy(bio[1].fname, "");
strcpy(bio[1].lname, "Cat");
bio[1].age = 1;
bio[2].id = 2;
strcpy(bio[2].fname, "");
strcpy(bio[2].lname, "Bat");
bio[2].age = 2;
/* Sort the structure */
sort(bio, sizeof(struct Members), 0, bio_max - 1, struct_cmp);
/* Print the sorted structure */
for (int i = 0; i < bio_max; i++) {
printf("%d %s %s %d\n", bio[i].id, bio[i].fname, \
bio[i].lname, bio[i].age);
}
}
Output
0 Apple 0
2 Bat 2
1 Cat 1
If the strings are not sorting in the way you want, you can redefine the struct_cmp function. Code is self explanatory, the base logic in the code is pass an array and swap elements using memcpy functions. You cant use simple assignment operator if you want to be generic, so that is why the element size is explicitly passed.
Edit
The code was not handling the condition, if lname are same. I missed it thanks for #4386427 for pointing this out.
I think you should define bio to be an array. And google sort algorithms please. Also recommend you google how to use libc function qsort.

Passing a stack in C while using Threads

I am trying to make a C program to count the number of frequency of words in a document and to split the document up into N parts with each part being counted by a different thread. Everytime I run the program, I get back nonsensical data, but if I run it without the threads I get back the data that I expect.
Here is the Struct named BinarySearchTree.h
typedef struct {
char *num; /*! The contents of the <tt>Item</tt>.<br>Type: <tt>int</tt>*/
int count;
} Item; /*! \typedef Item
* \struct A struct represent one item.
*/
typedef struct node {
Item info; /*! A struct with a one <tt>int</tt> on it. */
struct node * left;
struct node * right;
} Tree;
typedef struct passargs {
char *File;
Tree *t;
int splitStart;
int splitEnd;
int i;
int a;
char words[512][512];
} pass;
Here is the main code named **BinarySearchTree.c*:
#include "BinarySearchTree.h"
#include <pthread.h>
#include <string.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
// Code for integer based BST from: https://gist.github.com/ArnonEilat/4611213
Tree * add(Tree* nod, char *number) {
if (nod == NULL) {
printf("Thread number %ld\n", pthread_self());
printf("\nMaking new node for %s\n", number);
nod = (Tree*) malloc(sizeof (Tree));
if (nod == NULL) {
return NULL;
}
nod->info.num = number;
nod->info.count=1; //Increment the value if the same word is found
nod->left = NULL;
nod->right = NULL;
return nod;
}
if (strcmp(nod->info.num, number)==0) {
printf("%s==%s ",nod->info.num,number);
printf("\t%s:%d ",nod->info.num,nod->info.count);
// ++nod->info.count;
nod->info.count++;
printf("->%d\n ",nod->info.count);
return nod;
}
if (strcmp(nod->info.num, number)>0){
printf("%s>%s ",nod->info.num,number);
nod->left = add(nod->left, number);
} else {
printf("%s<=%s ",nod->info.num,number);
nod->right = add(nod->right, number);
}
return nod;
}
void printInorder(Tree* nod) {
if (nod == NULL) {
return;
}
printInorder(nod->left);
printf(" %s: %d ", nod->info.num, nod->info.count);
printInorder(nod->right);
}
void freeTree(Tree *root) {
if (root == NULL) {
return;
}
freeTree(root->left);
freeTree(root->right);
free(root);
}
int newLines(char* File){ //Count the number of new lines in the file. Split based on those not word count
int newLines=0;
char buffTemp[5120];
FILE *fp1 = fopen(File, "r");
while(1)
{
if(fgets(buffTemp, 512, fp1) ==NULL)
break;
else{
newLines++;
}
}
fclose(fp1);
return newLines;
}
//This function reads the file and adds each entry to the tree, or increments if same word is present
Tree* read(pass* info){
const char *delims = " \n"; //Deliminate by newlines and spaces
char *token;
int splitCounter=0;
FILE *fp = fopen(info->File, "r");
char buff[512];
int aT=info->a; //Putting variables into local ones
int iT=info->i;
int splitEnd = info->splitEnd;
int splitStart = info->splitStart;
char words[512][512];
memcpy(words, info->words, 512);
Tree* nod=info->t;
while(1 && splitCounter<splitEnd) //While the file does not end and we have not reached the end of the split
{
fgets(buff, 512, fp);
splitCounter++;
if(buff == NULL)
break;
else if (splitCounter>splitStart){
printf("\t %s\n", buff);
token = strtok(buff, delims); //Split via the tokens and put them into buff
while (token!=0) {
strcpy(words[iT], token);
iT += 1;
token = strtok(NULL, delims);
}
}
}
for (;aT<iT;aT++){
nod=add(nod, (char *)words[aT]); //Add each word to the tree
}
printf("\n");
fclose(fp);
info->t=nod;
memcpy(info->words,words,512);
info->i=iT;
info->a=aT;
return nod;
}
int main() {
Tree* t = NULL;
pass args;
args.t=t;
args.i=0;
args.a=0;
args.splitStart=0; //These splits are used to tell each thread where to look in the file
args.splitEnd=0;
args.File="/home/dib/CLionProjects/deleteme/readFile"; //Address of the file to be read
int numLines=newLines(args.File);
printf("\nnewLines %d\n",numLines);
int split;
printf("How many splits/threads would you like to create?\n");
scanf("%d", &split);
int iterator=numLines/split;
printf("iterator %d:", iterator);
const int NUMTHREADS= numLines/iterator + (numLines % iterator != 0);
unsigned int p;
// This for loop shows how I hope the threads would work. Uncomment the below block to see it work
for (p=0; p<NUMTHREADS; p++){
args.splitStart=args.splitEnd;
if (args.splitEnd+iterator>numLines) args.splitEnd=numLines;
else args.splitEnd+=iterator;
read(&args);
}
pthread_t th[NUMTHREADS];
int threads[NUMTHREADS];
//This is my attempt at using threads to solve the same problem as the above for loop
// for(p=0; p< NUMTHREADS; p++){
// threads[p] = p;
// args.splitStart=args.splitEnd;
// if (args.splitEnd+iterator>numLines) args.splitEnd=numLines;
// else args.splitEnd+=iterator;
// printf("split end: %d",args.splitEnd);
// pthread_create(&th[p], NULL, &read, &args);
// }
printInorder(args.t);
freeTree(args.t);
exit(0);
}
And finally the document I have been using as a test case named readFile:
five five five one two
two three three four four
three six seven six six
four six five seven seven
seven seven seven seven six
five four six
I tried implementing the solution found here : passing struct to pthread as an argument
but did not know what thread_handles was, and could not get it to work though the problem faced in that link is similar. So am I also just having a problem with memory allocation or is it something completely different?

Get the variable value from a previous struct in arrays of structs

I have the following struct:
struct V {
int d;
int tip;
char naziv[10];
int veza;
int tezina;
};
typedef struct V Vertex;
The following declaration:
void kreiranje(Vertex cvorovi[]);
The following main function:
int main(){
int n=15;
Vertex cvorovi[n];
Vertex *pokazivac = &cvorovi[n];
kreiranje(pokazivac);
}
And this is the code behind the kreiranje function:
void kreiranje(Vertex cvorovi[])
{
int x,y,d,f,g;
int i = 0;
int brojac = 0;
char z[10];
char line[50];
char lined[50];
FILE *fr;
FILE *fp;
fr = fopen ("nodes.txt", "rt");
fp = fopen ("edges.txt", "rt");
while(fgets(line, 50, fr) != NULL)
{
sscanf (line, "%d|%d %s", &x, &y, &z);
cvorovi[n].d = x;
cvorovi[n].tip = y;
strcpy(cvorovi[n].naziv, z);
if(brojac < n){
fgets(lined, 50, fp) != NULL;
sscanf (lined, "%d-%d,%d", &d, &f, &g);
if (cvorovi[n-1].d == d){
printf ("\n Cvor prosli je %d\n", cvorovi[n-1].d);
printf ("\n %d \n", d);
cvorovi[n].veza = y;
cvorovi[n].tezina = g;
}
else {
cvorovi[n].veza = y;
cvorovi[n].tezina = g;
}
}
brojac++;
}
fclose(fr);
fclose(fp);
}
The files are like this:
edges.txt
1-2,4
1-3,5
nodes.txt
1|1 EL_01
2|2 TF_01
In the first line of nodes.txt, first number presents the ID, that is used in edges.txt. My problem is the following - how can I check and assign if the ID has more than one of the connections that are given in the edges.txt (just like the example shows)?
The file values read okay, however I am not sure how can I get the previous member's of the struct values to use with comparison.
My problem is the following - how can I check and assign if the ID has more than one of the connections that are given in the edges.txt (just like the example shows)?
There are several ways to represent a graph in programs. I think in your case the easiest would be Adjacency Matrix or Incidence Matrix:
int AdjacencyMatrix[n][n];
somewhere in main():
memset(AdjacencyMatrix, 0, sizeof(AdjacencyMatrix));
In your reader function (kreiranje):
fgets(lined, 50, fp) != NULL;
sscanf (lined, "%d-%d,%d", &d, &f, &g);
int From=d;
int To=f;
AdjacencyMatrix[From][To]=1
AdjacencyMatrix[To][From]=1; <<- Only if this is undirected graph
then,
if you want to check node's X connections, use this:
for(i=0;i<n;++i) {
if(AdjacencyMatrix[X][i]==1) {
printf("Node %d links to %d\n", X, i);
}
}
You appear to want something a bit more like this:
/* ... data type and function declarations ... */
#define MAX_VERTICES 15
int main() {
Vertex cvorovi[MAX_VERTICES];
int n;
/* arguments are a pointer to the array _start_, plus its size: */
/* the number of vertices actually read is returned */
n = kreiranje(cvorovi, MAX_VERTICES);
/* ... */
return 0;
}
and
int kreiranje(Vertex cvorovi[], int n) {
int brojac = 0;
/* ... other local variable declarations ... */
while ((brojac < n) && (fgets(line, 50, fr) != NULL)) {
/* ... loop body ... */
brojac++;
}
return brojac;
}
I suggest having function kreiranje() return the number of vertices actually read, as shown, because you will need to know that to do anything further with them.
As to checking for multiple connections attributed to the same ID, you keep track of what you've read so far, and you read until you've seen enough to be confident that reading more is unnecessary. I know that's very abstract, but the details depend on the assumptions you can make about the form and organization of your data, and on the data structures you use in your program. If you're asking about a general approach to your problem rather than about details of how to implement it, then your question is probably inappropriate for SO.

Invalid pointer in C

I apologize in advance, but this is kind of a long one.
In my program, I read in the student's information, but when I go to output it, it comes out scrambled, and then gets a pointer error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student{
char *firstName;
char *lastName;
char id[10];
char gender;
int age;
double gpa;
};
void main()
{
int n;
struct student *classroom;
printf("How many students?");
scanf("%d",&n);
classroom = (struct student*) malloc(n*sizeof(struct student));
if (classroom == NULL)
exit(1);
readStudentsInformation(classroom,n);
outputStudents(classroom,n);
printf("The average age is %.2f.\n",averageAge(classroom,n));
printf("The average GPA is %.2f.\n",averageGpa(classroom,n));
sortByLastName(classroom,n);
outputStudents(classroom,n);
sortByID(classroom,n);
outputStudents(classroom,n);
sortByAge(classroom,n);
}
void outputStudents(struct student classroom[], int size)
{
int i;
for (i = 0; i < size; i++)
{
printf("%15s",classroom[i].firstName);
printf("%15s:",classroom[i].lastName);
printf("%14s,",classroom[i].id);
printf("%3c",classroom[i].gender);
printf("%5d",classroom[i].age);
printf("%5.2f",classroom[i].gpa);
}
}
Input:
How many students?2
First Name?Thom
Last Name?Arron
ID?2
Gender?M
Age?26
GPA?3.9
First Name?Frank
Last Name?Roberts
ID?1
Gender?F
Age?24
GPA?3.4'
Output:
Roberts Roberts: 2, M 26 3.90 : 1, F 24 3.40The average age is 25.00.
The average GPA is 3.65.
* glibc detected * ./lab12: munmap_chunk(): invalid pointer: 0x00007fff30319a90 *
: 2, M 26 3.90Aborted (core dumped)
The full code is here, but I didn't want to copy 200 lines to stack overflow: http://codepad.org/LYpS6t5z
Any idea what would cause this?
This is one conceptual error, you have it in a couple of places
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
classroom[i].firstName = temp;
What you want here instead is
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
strcpy(classroom[i].firstName, temp); // note this
Or, cleaned up a bit:
classroom[i].firstName = malloc(1+strlen(temp)); // note clean up here
if (classroom[i].firstName == NULL)
exit(1);
strcpy(classroom[i].firstName, temp);
Or even just
classroom[i].firstName = strdup(temp); // this takes place of all the lines above
These errors explain why your free's are failing.
Nothing else jumps out at me.
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
classroom[i].firstName = temp;
Your second assignment here overwrites the address, leaking the mallocd memory and making the pointer invalid as soon as that for iteration finishes. The same buffer is reused (with the same error) for lastName, which is why you see Roberts Roberts instead of the actual first and last name. When you go to free them, they are (1) invalid and (2) not made by malloc, so you get the crash you see.
Just like other arrays, you can't copy them by assignment, you have to copy byte-by-byte:
size_t len = strlen(temp);
classroom[i].firstName = malloc(1+len);
if (classroom[i].firstName == NULL)
exit(1);
strncpy(classroom[i].firstname, temp, len);
classroom[i].firstname[len] = '\0';
And don't cast the result of malloc.
Weirdness happening in readStudentsInformation, in particular lines like
classroom[i].lastName = temp;
are causing issues later on when you try to free this memory with
free(classroom[i].firstName);
Appropriate memory handling below:
void readStudentsInformation(struct student classroom[], int size)
{
int i;
char temp[50];
for (i = 0; i < size; i++)
{
printf("First Name?");
scanf("%s",temp);
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
/* after mallocing good memory can write in the data.. */
strcpy(classroom[i].firstName, temp);
/* classroom[i].firstName = temp; */
printf("Last Name?");
scanf("%s",temp);
classroom[i].lastName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].lastName == NULL)
exit(1);
/* classroom[i].lastName = temp; */
strcpy(classroom[i].lastName, temp);
printf("ID?");
scanf("%s",classroom[i].id);
fflush(stdin);
__fpurge(stdin);
printf("Gender?");
scanf("%c",&classroom[i].gender);
printf("Age?");
scanf("%d",&classroom[i].age);
printf("GPA?");
scanf("%lf",&classroom[i].gpa);
}
}

Resources