How to load data from file into linked list c - c

The program should read a simple text file and store the data ( name and id) into a linked list.
This is the linked list:
struct Prova
{
char nome[16];
int id;
};
typedef struct Node {
struct Prova struttura;
struct Node * next;
}TNodo;
typedef TNodo* Nodo;
This function creates the linked list:
void NewList(struct Prova p, Nodo * pp)
{
Nodo temp;
temp = (Nodo)malloc(sizeof(struct Node));
temp->struttura = p;
temp->next = *pp;
*pp = temp;
}
This is the function i wrote to read the file:
void Load(Nodo *pp)
{
FILE *f;
struct Prova p;
char * buffer;
if(!(f = fopen(PATH, "r")))
{
perror("Error");
exit(-1);
}
buffer = malloc(sizeof(struct Prova));
while(fgets(buffer, sizeof(buffer), f))
{
if(sscanf(buffer, "%s%d", p.nome, &p.id) > 0)
{
NewList(p, pp);
}
}
free(buffer);
fclose(f);
}
The text file i'm trying to read is this:
Stefano 31
Paperino 23
Pippo 1
Pluto 14
The functions for displaying the list are these:
void View(struct Prova p)
{
printf("%s %d\n", p.nome, p.id);
}
void ViewList(Nodo nodo)
{
while(nodo != NULL)
{
View(nodo->struttura);
nodo = nodo->next;
}
The program compiles fine, but it outputs the data in a strange order.
Let me know if you need more info, i think it's all related to the function Load() which is the newest.
Main function is this:
int main()
{
int scelta;
struct Prova test;
Nodo lista = NULL;
do {
scelta = Menu();
switch (scelta)
{
case 1: Load(&lista); break
case 2: ViewList(lista); break;
default: scelta = 0;
}
} while (scelta != 0);
return 0;
}
For now the output is this:

The problem is occurring because of passing sizeof(buffer) to fgets(). buffer is a pointer and sizeof(buffer) will return either 4 or 8 based on underlying architecture 32 bit or 64 bit. Change this to:
while(fgets(buffer, sizeof(struct Prova), f))
as you are allocating sizeof(struct Prova) size to buffer.
This is also not the correct solution of the problem. Reason is-
Say, your file is having this data:
abcdefghijklmno 123456789
The name part is 15 character long which nome member can hold and id member can also hold the number 123456789 as it is less than INT_MAX. fgets read characters from the stream and the size of above-given data is 25 characters. In your code, you are allocating sizeof(struct Prova) size memory to buffer and size of struct Prova is 20 byte. Hence for the above-given data, buffer is not allocated enough memory to read the whole line in one go because the fgets reads until size-1 characters have been read or either a newline or the end-of-file is reached, whichever happens first. So, in this case, the partial line will be read and passed to sscanf() and rest of the line will be read in next iteration and passed to sscanf() which will give the incorrect results.
You should not allocate the memory to buffer based on the size of struct Prova. Instead, I would suggest taking buffer of some bigger size, like this (no need to allocate it dynamically):
char buffer[100];
ensure it should be bigger enough to accommodate a line of the file in one read of fgets or modify your code to not to fill p.nome and p.id until fgets hits newline character or EOF i.e. to make sure to read the whole line and then only sscanf() the p.nome and p.id.

Related

change all the list values to the last value

so i have a code that a little bit confused, so i build a struct :
typedef struct{
char *team_name;
}Team;
and other struct :
typedef struct{
Team** teams;
int num_teams;
}League;
and a function that create the struct Team:
Team* TeamCreate(char* team_n)
{
Team* t=(Team*)malloc(sizeof(Team));
t->team_name =team_n;
return t;
}
also for League struct:
League* LeagueCreate()
{
League* l=(League*)malloc(sizeof(League));
return l;
}
and the worst part is this function that should insert a words of text file into 'Team** teams'
so in every value of teams there is Team value that contain this word as char *team_name.
and the function make all teams list value equal to the last value of teams list.
the function:
void read_teams(League* l,char* text)
{
FILE *fp=fopen(text,"r");
char* tname=NULL;
size_t tname_size=0;
l->num_teams=0;
l->teams=NULL;
while(getline(&tname,&tname_size,fp)!=EOF)
{
char *p= strchr(tname,'\n');
if (p)
tname[p-tname]='\0';
l->teams=(Team**)realloc(l->teams,sizeof(Team*)*(l->num_teams+1));
l->teams[l->num_teams]=TeamCreate(tname);
l->num_teams++;
}
fclose(fp);
}
so for example my text file is :
Napoli
Salzburg
Liverpool
Genk
and when i write :
printf("\n%s\n",l->teams[0]->team_name);
printf("\n%s\n",l->teams[1]->team_name);
printf("\n%s\n",l->teams[2]->team_name);
printf("\n%s\n",l->teams[3]->team_name);
it print this:
Genk
Genk
Genk
Genk
and thanks for your time :).
The first time you call getline, sufficient space to hold the line will be allocated for you. Unless subsequent calls realloc the memory, you're passing the same exact pointer TeamCreate every time. Therefore, all of your Teams have the same char* and therefore the same team_name. Every time you update the string via getline, all of the team_names will change since they reference the same string.
What you need to do in TeamCreate is use the strdup function from string.h so that each Team gets its own string. I.e.,
t->team_name = strdup(team_n);
Read a .txt file line by line.
#define MAXCHAR 1000
char* filename = "G:\\test.txt";
fp = fopen(filename, "r");
if (fp == NULL){
printf("Could not open file %s",filename);
return 1;
}
while (fgets(str, MAXCHAR, fp) != NULL)
printf("%s", str);
fclose(fp);

how to make a string array in c with unknow array length and unknow string length? suppose memory is enough

if I want to store string array in C program from stdin, whose array length is not known in advance and the string length is unfixed or unlimited. That means I can not define such thing as char buf[10][100]; in the program. Is there any good solution for this case?
The C standard doesn't have such a function but getline() which is POSIX does what you want. This may or may not be what you're looking for, depending on what OS you're planning to run this on.
You just do something like:
char *inf_line = NULL;
size_t n = 0;
ssize_t input = getline(&inf_line, &n, stdin);
Alternatively, you could try filling up an array with getchar() in some loop, dynamically reallocating memory as you reach the end of the array using malloc(), for example.
See the following code as an example how to read input until EOF is reached (in terminal, try Ctrl-Z or Ctrl-D to emulate an EOF, depending on your OS), by using fixed size chunks and creating a full string after the last chunk was read.
#define CHUNK_SIZE 4 // testing size
//#define CHUNK_SIZE 1024 // my suggested production size
struct node
{
char data[CHUNK_SIZE];
struct node* next;
};
int main()
{
// will be allocated and filled after reading all input
char* full_text = NULL;
// head node
struct node* start = NULL;
// iterator node
struct node* current = NULL;
// for tail allocation
struct node** next = &start;
// count the number of chunks (n-1 full and one partially filled)
size_t count = 0;
// size of the last read - will be the count of characters in the partially filled chunk
size_t last_size;
// will be initialized to the full text size (without trailing '\0' character)
size_t full_size;
while (!feof(stdin))
{
// casting malloc result is bad practice, but working with VS here and it's complaining otherwise
// also, you may want to check the result for NULL values.
*next = (struct node*)calloc(1, sizeof (struct node));
last_size = fread_s((*next)->data, CHUNK_SIZE, 1/* sizeof char */, CHUNK_SIZE, stdin);
next = &((*next)->next);
++count;
}
// calculate the full size and copy each chunk data into the combined text
if (count > 0)
{
full_size = CHUNK_SIZE * (count - 1) + last_size;
// one additional character for the null terminator character
full_text = (char*)malloc(full_size + 1);
full_text[full_size] = '\0';
count = 0;
current = start;
while (current && current->next)
{
memcpy(&full_text[count * CHUNK_SIZE], current->data, CHUNK_SIZE);
current = current->next;
++count;
}
if (current)
{
memcpy(&full_text[count * CHUNK_SIZE], current->data, last_size);
}
}
else
{
full_text = (char*)calloc(1, 1);
}
// full_text now contains all text
// TODO free the node structure
return 0;
}
side note: I use calloc instead of malloc so I get zero-initialized storage.
side note: I use the binary fread_s instead of fgets, which doesn't zero-terminate the read data (would need some different handling otherwise) and which may not play nice with non-ASCII input. So make sure you understand your input format when using this 1:1

Stack smashing detected, on Eustis

When I compile and run this program on Windows/Eustis I am not getting any errors or warnings.
The basic function of the program is to read in an input text file(source code) and strip out C-style comments /*........*/ to a file cleaninput.txt.
It works on Windows. However, when run on Eusis I get this:
gcc -o out.x a2.c
./out.x
*** stack smashing detected ***: ./out.x terminated
Aborted
I have tried compiling with the command
gcc -fno-stack-protector -o out.x a2.c
./out.x
*** Error in './out.x': double free or corruption (out): 0x09670440 ***
Aborted
Is there a program or utility I can use to figure this out?
#include<stdlib.h>
#include<stdio.h>
#include <string.h>
#define MAX_DEC_LENGHT 11
#define MAX_INTEGER 99999
#define MAX_LINE_WIDTH 100 //used in the struct below defines how wide a instruction can be per line
FILE *fp;
typedef struct input{
char line[MAX_LINE_WIDTH];
struct input *next;
}input;
void scanFile();
struct input *Push(struct input *temp2, char Buff[MAX_LINE_WIDTH]);
void printClean(struct input *temp);
int Get_No_Lines();
void freeme(struct input* ptr);
const char *strip_comments(char input[MAX_LINE_WIDTH]);
int main(){
int x = Get_No_Lines();
scanFile(x); //will read in the input and produce cleaninput.txt, get_no_lines is used to tell the program later how long the input text file is
return(0);
}
/*
Take in an integer N, which represents how many lines there are in the text file, which is used in scanning into a linked list of each line
calls for the print clean input function
Currently not returning anything
*/
void scanFile(int n){
fp = fopen("input.txt", "r");
struct input*temp = NULL;
int i;
for(i=0;i<n;i++){
char Buff[MAX_LINE_WIDTH];
fgets(Buff,MAX_LINE_WIDTH,fp); //fgets is by far the easiest solution i found to scanning in line by line, the linked list is there for unlimited input lenght
temp = Push(temp,Buff); //push the value of fgets onto the linked list
}
printClean(temp);
freeme(temp); //at this point we call the printClean input function
fclose(fp);
}
/*
Take in the the pointer to a linked list, and place the value buff into the structs line segment,
Basic function is linked list push to end
*/
struct input *Push(struct input *temp2, char Buff[MAX_LINE_WIDTH]){
struct input *temp=(struct input*)malloc(sizeof(struct input));
struct input *current = temp2;
strcpy(temp->line,Buff);
temp->next= NULL;
if(temp2==NULL){
return(temp); //base case if null
}
while(current->next!=NULL){
current = current->next; //insert to back of list
}
current->next = temp; //link the new node
return(temp2);
}
/*
The main driver function for stripping the comments out of the input text file,
calls the strip_comments function and places the result back into the struct, then prints to the file stream
currently not returing anythig, shouldnt need to, will be checked later -MM
*/
void printClean(struct input *temp){
fp = fopen("cleaninput.txt", "w");
while(temp!=NULL){
char t[MAX_LINE_WIDTH];
strcpy(t,strip_comments(temp->line)); //copy the contents of strip_comments into t, and then copy the contents of t into temp->line
strcpy(temp->line, t); //yeah im not sure why i need to strcpy's but it wouldnt work without them \_(?)_/¯
fputs(temp->line, fp);
temp=temp->next;
}
fclose(fp);
}
const char *strip_comments(char input[MAX_LINE_WIDTH]){
char output[100];
int i=0;
int x =0;
int inComment = 0; // a boolean variable to let the case statment change behavior in the while loop
while(i<=MAX_LINE_WIDTH){
if(inComment==1){
if(input[i]=='*' && input[i+1]=='/'){
inComment=0;
i++; //end the comment, and increment the counter up two to clear hte comment
i++;
}
}
if(inComment==0){
if(input[i]=='/'&& input[i+1]=='*'){ //condtion to enter the comment switch
inComment=1;
}else{
output[x] = input[i]; //if not in the comment and last check was not in comment push the value of input[i] to output[x] increment x
x++;
}
}
i++;
}
char *rtn = output;
return(rtn); //return the input free of comments, currently cannot handle multi_line comments
}
/*
Used for the scanning function, its sole purpose is to let the program know how many lines there are in the input text file
*/
int Get_No_Lines(){
fp = fopen("input.txt", "r");
int ch, number_of_lines = 0;
do
{
ch = fgetc(fp);
if(ch == '\n')
number_of_lines++; //if the scanner gets to a \n charecter increment
} while (ch != EOF);
if(ch != '\n' && number_of_lines != 0) // needs to be checked, method was adopted from the internet
number_of_lines++;
if(number_of_lines==0){ //i added this case in when we get a one line input that the user has not hit the /n or enter key
number_of_lines =1;
}
fclose(fp);
return(number_of_lines);
}
void freeme(struct input* ptr){
struct input *temp;
while(ptr!=NULL){
temp=ptr->next;
strcpy(ptr->line,"");
free(ptr);
ptr=temp;
}
}
You are returning a pointer to the local variable char output[100];, which invokes undefined behavior. Returning a pointer to a local variable is always a severe bug.
Instead, pass the output buffer as a parameter to the function and leave the allocation to the caller.
Can a local variable be accessed outside its scope?

Scanf leads to segfault

I have a text file:
In 0 John 66
In 1 May 77
In 0 Eliz 88
Out 0
Out 0
I'm trying to parse this text file using scanf, and at the moment send the values after "In" to the add function, however I'm getting a seg fault when trying to do this.
I have some code here:
A struct in a seperate header file:
typedef Officer test;
typedef struct {
test tests[6];
int s;
} copList;
And this one:
typedef struct {
char name[25];
int id;
} Officer;
Then I have my main method
int main(void) {
FILE * ptr;
char buffer [500];
char * temp;
int pos;
int grade;
char * name;
copList * L;
ptr = fopen("test.txt","r");
if(ptr == NULL)
exit(1);
temp = malloc(sizeof(char)*10);
name = malloc(sizeof(char)*10);
L = malloc(sizeof(copList));
while(fgets(buffer,500,ptr) != NULL) {
sscanf(buffer,"%s %d %s %d\n",temp,&pos,name,&grade);
add(L->tests[pos],pos,L); //this gives me a seg fault
}
free(name);
free(temp);
free(L);
fclose(ptr);
return 0;
}
In a separate c file I have the add function.(Can't be changed)
void add(Test b, int pos, copList * L) {
//code to be added here later...
}
I've tried allocating different amounts of memory, but that didn't help. Also I noticed if I set a value to pos, in the while loop, before the add function call, I don't get a seg fault, but obviously that's not what I want, because the value wouldn't change. Any help would be much appreciated.
The main problem I see with your code is that it does not check the return value of sscanf -- if sscanf returns something other than 2 or 4, that means your input is something other than what you say it is. In addition, the arrays temp and name might overflow (on inputs other than what you show), which would cause undefined behavior. Finally, the spaces and \n in the sscanf format are wrong and should be removed (though they shouldn't actually cause any problems in this case.)
So you code should be something like:
while(fgets(buffer,500,ptr) != NULL) {
int count = sscanf(buffer,"%9s%d%9s%d",temp,&pos,name,&grade);
if (count != 2 && count != 4) {
fprintf(stderr, "Invalid input line: %s", buffer);
continue; }
... do stuff with temp and pos (only use name and grade if count == 4)
in this line:
add(L->tests[pos],pos,L);
the first parameter is a copy of the 'test' struct.
It is almost always a bad idea to pass a whole struct. Much better to just pass a pointer to the struct:
add( &(L->tests[pos]), pos, L );
Then, this line has a couple of problems:
void add(Test b, int pos, copList * L) {
1) 'Test' is a non-existent type, perhaps you meant: 'test'
2) 'b' is expecting a passed struct. as mentioned above,
it is (almost) always better to pass a pointer to a struct.

Creating a Dictionary in C

I am currently working on creating a dictionary using a binary search tree-like structure we designed in class.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
struct entry
{
char* word;
unsigned int n; /* n is the number of times the word appears in the source. */
struct entry *left;
struct entry *right;
};
/*input_from_args: if no additional argument is given, return stdin. Else, open the text file and read it.*/
FILE*
input_from_args(int argc, const char *argv[]){
if(argc==1){
return stdin;
}else{
return fopen(argv[1],"r");
}
}
Below is the insert function that we also wrote in my class. Given the new word we are looking at, if it is
struct entry*
insert(struct entry *table, char* str)
{
if(table == NULL){
table = (struct entry *)malloc(sizeof(struct entry));
strcpy(table->word,str);
table -> n = 1;
table -> left = NULL;
table -> right = NULL;
}else if(strcmp(str, table->word) == 0){
table -> n = (table ->n)+1;
}else if(strcmp(str, table->word) <0){
table->left = insert(table->left, str);
}else if(strcmp(str, table->word) >0){
table ->right = insert(table->right, str);
}
return table;
}
Below is a print function which I wrote myself which is to print every word in table and N, the number of times it occurs.
void
print_table(struct entry *table){
if(table!=NULL){
print_table(table->left);
printf("%s ", table->word);
printf("%d \n", table->n);
print_table(table->right);
}
}
And finally, below is the main function.
int
main(int argc, const char *argv[])
{
FILE *src = input_from_args(argc, argv);
if(src == NULL){
fprintf(stderr, "%s: unable to open %s\n", argv[0], argv[1]);
exit(EXIT_FAILURE);
}
char str[1024];
struct entry *table;
int c;
while((fscanf(src, "%s", str))!= EOF){
table = insert(table, str);
}
print_table(table);
return 0;
}
I'm having some very odd behavior when I run this function. It seems to only be happening when I run it with longer input.
When I run it with this input(in a .txt file):
This is a test.
This is a test.
This is a test.
I get the following output:
This 3
a 3
is 3
test 3
This is what I should be getting. However, when I give it slightly longer input, such as:
Apple Apple
Blue Blue
Cat Cat
Dog Dog
Elder Elder
Funions Funions
Gosh Gosh
Hairy Hairy
I get the following output:
Appme 2
Blue 2
Cat 2
Dog 2
Elder 2
Funions 2
Gosi 2
Hairy 2
Which is clearly correct as far as the numbers go, but why is it changing some of the letters in my words? I gave it Apple, it returned Appme. I gave it Gosh, it gave me Gosi. What's going on with my code that I am missing?
This line in the insert function is very problematic:
strcpy(table->word,str);
It's problematic because you don't actually allocate memory for the string. That means that table->word is uninitialized and its value will be indeterminate, so the strcpy call will lead to undefined behavior.
The simple solution? Use strdup to duplicate the string:
table->word = strdup(str);
The strdup function is not actually in standard C, but just about all platforms have it.
In your insert function, you do not allocate/malloc() space for the word pointer you are trying to strcpy() to:
if(table == NULL){
table = (struct entry *)malloc(sizeof(struct entry));
strcpy(table->word,str);
table -> n = 1;
table -> left = NULL;
table -> right = NULL;
}
Usually this code would exit with a segmentation fault, because you are copying data to memory you don't own, but this is easy to fix:
table->word = malloc(strlen(str) + 1);
strcpy(table->word, str);
You'll want to allocate one extra byte above the string length, to allow for the null terminator.
You do not need or want to cast the result of malloc(). In other words, this is fine:
table = malloc(sizeof(struct entry));
Get into the habit of using free() on any pointers you have malloc()-ed, when you are done with them. Otherwise, you end up with a memory leak.
Also, compile with -Wall -Weverything flags to enable all warnings.
Note: If one absolutely must use strdup(), it is easy to write a custom function to do so:
char* my_very_own_strdup(const char* src)
{
char* dest = NULL;
if (!src)
return dest;
size_t src_len = strlen(src) + 1;
dest = malloc(src_len);
if (!dest) {
perror("Error: Could not allocate space for string copy\n");
exit(EXIT_FAILURE);
}
memcpy(dest, src, src_len);
return dest;
}
On the line strcpy(table->word,str); where is table->word allocated?
So It copies only 4 bytes to table->word because pointer size is 4-bytes in your machine. So Be careful, you must allocate table->word there,
I would use this one instead of that table->word = strdup(str);

Resources