error in my search linked list implementation - c

My program doesn't seem to be opening the text files properly.
I have a path.txt which is a string representation of all the paths of folders and text files which I have created. When running the program however, it will not output the LINES of the text file the user asked for.
OUTPUT
enter text file
warning: this program uses gets(), which is unsafe.
a1.txt
IT should have output
This is a1
text of a1.txt:
This is a1
text file: path.txt/ this is how my folder is set up with the text files.
a/a1.txt
a/a2.txt
a/b/b3.txt
a/b/b4.txt
a/c/c4.txt
a/c/c5.txt
a/c/d/d6.txt
a/c/d/g
a/c/d/h
a/c/e/i/i7.txt
a/c/f/j/k/k8.txt
code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct sMyPath{
char *element;
struct sMyPath *next;
} tMyPath;
int main(void)
{
FILE *pFile;
pFile = fopen("path.txt", "r");
char inputstr[1024];
tMyPath *curr, *first = NULL, *last = NULL;
//get the text file, and put it into a string inputstr
if (pFile != NULL)
{
while(!feof(pFile))
{
fgets(inputstr, sizeof(inputstr), pFile);
}
fclose(pFile);
}
else
{
printf("Could not open the file.\n");
}
//using tokens to get each piece of the string
//seperate directories and text files, put it into a link list
char *token = strtok(inputstr, "/");
while (token != NULL)
{
if(last == NULL){
//creating node for directory
first = last = malloc (sizeof (*first));
first -> element = strdup (token);
first -> next = NULL;
} else {
last -> next = malloc (sizeof (*last));
last = last -> next;
last -> element = strdup (token);
last -> next = NULL;
}
token = strtok(NULL, "/");
}
//ask user for txt file
char pathU[20];
printf("enter text file\n");
gets(pathU);
//check if text file exist, if yes output entires in text file, else say no
while(first != NULL)
{
if(first -> element == pathU)
{
FILE *nFile;
char texxt[300];
nFile = fopen(pathU, "r");
while (!feof(nFile))
{
fgets(texxt, 300, nFile);
printf("%s", texxt);
}
}
else if(first == NULL)
{
printf("invalid file name\n");
}
else
{
first = first -> next;
}
}
return 0;
}

I understand two possible requirement/implementation.
1) By your implementation, every link-node will contain just filename and directory name and NOT THE PATH-NAME. If you need to store entire pathname, use '\n' as delimiter.
char *token = strtok(inputstr, "\n");
and
token = strtok(NULL, "\n");
This assumes, when your input is a/a1.txt, your current directory contains the directory a and which in-turn contains the file a1.txt.
2) Otherwise, your existing code expects a1.txt to be in current directory, though it contradicts the input file content.
Either way, this below code is culprit,
if(first -> element == pathU)
which compares the pointer and not the string. Replace it as,
if( strcmp( first -> element, pathU ) == 0 )
I could help better solution if your requirement is more clear..

The problem seems to be in the string comparison: first -> element == pathU. Here you are comparing pointers, not the characters of the strings. Use strcmp instead: if (strcmp(first -> element, pathU) == 0) ...

Related

How to read a file in C and at the same time create a variable with the number of lines?

So, i need a code to read a TXT file with 5 contents each line, and store each line values in a variable. but the number of lines in the TXT will vary. so i thought of excecuting something like this:
int counter=0;
char nome="TXT NAME.txt";
FILE *f = fopen(NAME,"r");
do{
if(!feof(p))
counter++;
}while(!feof(f));
fclose(f)
int X[counter][5];
so when declaring like this, X will have the number of lines of the file. but the issue is that to store the values, i would need to open and read the file again. is there a way to save the values while declaring the variable so i dont need to open twice ?
How to read a file in C and at the same time create a variable with the number of lines?
is there a way to save the values while declaring the variable so i dont need to open twice ?
A nice way is to read one line at a time with fgets(), copy the buffer with strdup() and save in a linked list of your creation.
Some pseudo-code to get you going.
f = open file
exit if unable to open
line_count = 0;
list = LL_Create()
char buffer[100];
while (fgets(buffer, sizeof buffer, f)) {
Maybe special handling here to deal with lines longer than 100
char *s = strdup(buffer);
LL_Append(list, s);
line_count++;
}
fclose(f);
// Use the link-list of strings
// When done
while !LL_Empty(list)
s = LL_Pop(list)
free(s);
LL_Destroy(list)
You can use realloc to gradually expand your "array".
But a simpler approach is to use a linked list, that way the list just grows as you read the file.
typedef struct
{
char data[5];
Line* next;
} Line;
Line* first = NULL;
Line* last = NULL;
char line[128];
while (fgets(line, sizeof(line), f)
{
Line* tmp = malloc(sizeof(Line));
memcpy(tmp->data,line,5);
tmp->next = NULL;
if (first == NULL)
{
first = last = tmp;
}
else
{
last->next = tmp;
last = tmp;
}
}
Now to go through the lines
for(Line* p = first; p != NULL; p = p->next)
{
p->data ...
}

How to read a .txt file from user input in C

I know how to hardcore a program to receive a file but when I try a similar tactic with scanf nothing happens. I mean that I have an error check that looks to see if it exist and if it has the right format but everytime I enter the filename it doens't print the printf statement below the scanf. Why is that? I also found out that I am opening the file but the while statement is infinite. Which doesn't make sense. I have tried another solution shown below but same results.
void parseFile(struct student_record_node** head)
{
FILE*input;
const int argCount = 4;
char filename[100]="";
const char rowformat[] = "%20s %20s %d %d";
struct student_record record;
struct student_record_node* node = NULL;
printf("\n Please Enter the FULL Path of the .txt file you like to load. \n");
scanf("%s",filename);
input = fopen(filename, "r");
printf("I am here");
if(input == NULL)
{
printf("Error: Unable to open file.\n");
exit(EXIT_FAILURE);
}
while(!feof(input))
{
/* creating blank node to fill repeatedly until end of document*/
memset(&record, 0, sizeof(struct student_record));
if(fscanf(input, rowformat, record.first_name_,record.last_name_,&record.student_id_,&record.student_age_) != argCount)
{
continue;
}
/* set node into the doubly linked list */
node = student_record_allocate();
/* copies values from the blank node reading from document into node in my linked list */
strcpy(node->record_->first_name_, record.first_name_);
strcpy(node->record_->last_name_, record.last_name_);
node->record_->student_id_ = record.student_id_;
node->record_->student_age_ = record.student_age_;
/* check if node right after absolute head is empty if so fills it */
if(*head == NULL)
{
*head = node;
}
else
{
printf(" stuck in loop\n");
/* if current isn't null start linking the node in a list */
appendNode(head,node);
}
}
fclose(input);
printf(" end of parsefile");
}
When I got to the parsefile() function and enter NEW.txt which is in the correct format and inside the same folder as the program itself. I know that my check is working when I enter a .txt file that doesn't exist or that is empty it gets caught like it should.
The expected behavior is that the program should load this list from new.txt and load it into a doubly linked list. Then return to a menu that gives user options. The doubly linked listed can then be manipulated such as add students manipulate data, deleting, saving and printing current roster. I have trouble using gdb with this program since I receive new.txt from parsefile.
Sample of New.txt contents. (Its just First Name, Last Name, Id, Age)
Belinda Homes 345 50
Scott Crown 456 18
Failed Solution: Using fgetc instead of feof
int c = fgetc(input);
while(c != EOF)
{
printf("\n in loop \n");
/* creating blank node to fill repeatedly until end of document*/
memset(&record, 0, sizeof(struct student_record));
if(fscanf(input, rowformat, record.first_name_,record.last_name_,&record.student_id_,&record.student_age_) != argCount)
{
continue;
}
/* set node into the doubly linked list */
node = student_record_allocate();
/* copies values from the blank node reading from document into node in my linked list */
strcpy(node->record_->first_name_, record.first_name_);
strcpy(node->record_->last_name_, record.last_name_);
node->record_->student_id_ = record.student_id_;
node->record_->student_age_ = record.student_age_;
/* check if node right after absolute head is empty if so fills it */
if(*head == NULL)
{
*head = node;
}
else
{
printf(" stuck in loop\n");
/* if current isn't null start linking the node in a list */
appendNode(head,node);
}
c = fgetc(input);
}
This is the easiest way to read from file and print out. I assume you want to print the file or do something with it.
int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) {
while ((c = getc(file)) != EOF){
putchar(c);
}
fclose(file);
}

fread/fwrite not working as expected

So I am trying to write a c file that has a student record database, once the file runs it is supposed to read student records from a binary file and add them to a linked list: the student struct looks like this :
typedef struct student {
char lname[10], initial, fname[10];
unsigned long SID;
float GPA;
struct student* next; /* pointer to the next student record */
} studentList;
I am using single linked lists for my data, and my code runs fine if I read and write the data with fscanf. However, once I started using fwrite and fread, everytime my program loads it would not load up the data from the text file correctly, when I check the binary file it seems it has data in it. Here is what I have for my load and write data functions:
void printRecords() {
FILE *fPointer = fopen("data.bin", "w");
studentList *newStudent = head;
while (newStudent != NULL) { /*Loop through linked list starting from head node*/
fwrite(&newStudent, sizeof(newStudent), 1, fPointer);
newStudent = newStudent->next;
}
}
void loadRecords() {
studentList *cStudent;
FILE *fPointer = fopen("data.bin", "r");
int counter = 0;
int x = 0;
int n = 0;
while (n != 0) {
printf("test\n");
if (fPointer == NULL) {
break;
}
cStudent = (studentList *)malloc(sizeof(studentList));
n = fread(&cStudent, sizeof(cStudent), 1, fPointer);
x = cStudent->GPA;
printf("%d\n", x);
if (feof(fPointer)) { break; }
if (counter == 0) {
head = cStudent;
temp = (studentList *)malloc(sizeof(studentList));
temp = cStudent;
counter++;
}
temp->next = (studentList *)malloc(sizeof(studentList));
temp->next = cStudent;
temp = temp->next;
}
fclose(fPointer);
}
so what am I doing wrong as right now it does not read anything into my list, it seems like it writes but not sure if it even writes the correct data, I have spent a long time trying to figure this out and have been stuck on it for a while now, thanks in advance.
To read write to binary files in c.
FILE *fPointer=fopen("data.bin","wb"); //to write file
FILE *fPointer=fopen("data.bin","rb"); //to read file
There are multiple problems with your code.
printRecords has problems:
you should use binary mode.
you write the contents of the pointer and some indeterminate contents instead of whet the pointer points to. This is actually undefined behavior.
you forgot to close the file.
the value written to the file for the next member is meaningless. Writing the same records may produce different file contents for different runs.
Here is a corrected version that returns the number of records written or -1 if the file could not be opened:
int printRecords(void) {
FILE *fPointer = fopen("data.bin", "wb");
studentList *sp;
int n = 0;
if (fPointer == NULL)
return -1;
/* Loop through linked list starting from head node */
for (sp = head; sp != NULL; sp = sp->next) {
n += fwrite(sp, sizeof(newStudent), 1, fPointer);
}
fclose(fPointer);
return n;
}
loadRecords has even more problems:
binary mode should be used too.
the way you test for end of file is incorrect.
the way you link records does not work either.
Here is a corrected version of loadRecords that returns the number of records read or -1 if the file could not be opened:
int loadRecords(void) {
studentList student, *newsp, *sp;
FILE *fPointer = fopen("data.bin", "rb");
int n = 0;
if (fPointer == NULL)
return -1;
while (fread(&student, sizeof(student), 1, fPointer) == 1) {
n++;
student.next = NULL; // value read from file is meaningless
newsp = malloc(sizeof(studentList));
*newsp = student;
if (head == NULL) {
head = newsp;
} else {
/* append the new record to the list */
for (sp = head; sp->next; sp = sp->next)
continue;
sp->next = newsp;
}
}
fclose(fPointer);
return n;
}
Be aware that storing binary data to the file system this way is not portable. The representation of integers and floats may differ from one platform to another, as well as alignment of the structure members, especially the next pointer which is useless in the file anyway. This file should only be read back on the same platform, with the same program that wrote it, making it a poor choice for backup or persistent storage.
There are problems in the file write function, mainly the data pointer and size:
void printRecords(){
FILE *fPointer = fopen("data.bin","wb"); // add "b" mode (MSVC)
if (fPointer == NULL) // check file opened
exit(1); // or report "Cannot open file"
studentList *newStudent = head;
while(newStudent !=NULL)
{
fwrite(newStudent, sizeof(*newStudent), 1, fPointer); // remove the & and add a *
newStudent = newStudent->next;
}
if (fclose(fPointer)) // close the file
exit(1); // or report "Failed to close file"
}

Using an array of filenames stored as strings

My program iterates through a single directory (non-recursively) and stores the names of all the files in that directory inside an array. Then, it uses that array in the second part of my program and returns some information about each file. I can iterate through the directory, and I can process a single file, but I'm having trouble combining the two parts of the program. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
int getArraySize(char* arr[]);
int getArraySize(char* arr[]) {
return sizeof(&arr);
}
char *filesArray[200];
int main (int argc, char* argv[])
{
DIR *dir;
struct dirent *ent;
int filesCtr = 0;
if ((dir = opendir ("/home/dshah/Documents/CECS 420/Project 3")) != NULL) {
while ((ent = readdir (dir)) != NULL) { /* print all the files and directories within directory */
if (strcmp(ent->d_name, ".") == 0) {
continue;
} else if (strcmp(ent->d_name, "..") == 0) {
continue;
} else if (ent->d_type == 4) { // if a directory
continue;
} else {
filesArray[filesCtr] = ent->d_name;
printf("%s\n", filesArray[filesCtr]);
filesCtr++;
}
}
closedir (dir);
} else { /* could not open directory */
perror ("Could not open directory");
}
int i;
for (i = 0; i < getArraySize(filesArray); i++) {
char* filename = filesArray[i];
FILE *file = fopen (filename, "r");
if (file != NULL) {
char line [128]; /* or other suitable maximum line size */
int ctr = 1;
while (fgets(line, sizeof line, file) != NULL) { /* read a line */
if (strstr(line, "is") != NULL) {
printf("%s:%d:%s", filename, ctr, line);
}
ctr++;
}
fclose (file);
} else {
perror (filename); /* why didn't the file open? */
}
}
return 0;
}
The line I am having trouble with is:
char* filename = filesArray[i];
Is this line of code correct? It works when I set filename to a string like "file.txt", so shouldn't this also work when I do printf("n %s\n", filesArray[i]);? Is filesArray[i] in this line of code a string?
EDIT:
Thanks, that fixed the problem. One more quick question: I'm trying to append the full path on
FILE *file = fopen (filename, "r");`
line by changing it to
FILE *file = fopen (strcat("/home/dshah/Documents/CECS 420/Project 3/", filename), "r");
but it gives me a segmentation fault. Shouldn't this work cause I'm just specifying the path?
When you pass an array to a function, it decays to a pointer, so when you do e.g. &arr you actually get a pointer to that pointer, and the size of a pointer is most likely not the size of the original array. If (and I mean really if) the array is actually a string, you can use strlen to get the length of the string (not including the string terminator character).
In your case, you don't actually need the getArraySize function, as you already have a counter telling you how many strings there is in the filesArray array: The filesCtr variable.
Also, when using a function such as readdir the d_name field of the returned entry may actually be pointing to a static array so you can't really just copy the pointer, you have to copy the complete string. This is done with the strdup function:
filesArray[filesCtr] = strdup(ent->d_name);
Remember that when done you have to free this string.
Oh, and avoid using "magic numbers" in your code, for example when checking if the directory entry is a sub-directory (ent->d_type == 4). Use the macros available to use (end->d_type == DT_DIR).
And a final thing, the d_name field of the readdir entry only contains the actual filename, not the full path. So if you want the full path you have to append the path and the filename.

Segmentation faults in C with fopen?? (not exactly sure what's causing the error)

So here's my code for my main()
int main(int argc, char **argv) {
if (argc != 3) {
puts("Invalid number of args in the input. Sorry.");
return 0;
}
if (doesItExist(argv[2]) == 0) {
return 0;
}
FILE *fpoint;
char yesorno[2];
tail = (WordN) malloc(sizeof(struct WordNode));
tail->word = "";
tail->first = (FileN) malloc(sizeof(struct FileNode));
(tail->first)->freq = -1;
ftw(argv[2], tokeForMe, 1);
/**
fpoint = fopen(argv[1], "r");
if(fpoint != NULL) { // file exists give user option to overwrite or rename
getInput("Do you want to overwrite the file? Enter only either Y or N nothing else\n", yesorno, 2);
if(yesorno[0] == 'N' || yesorno[0] == 'n') {
puts("All right. Not going to proceed with the program");
return 0;
}
else if(yesorno[0] != 'Y' && yesorno[0] != 'y') {
puts("You inputted some other character, try again \n");
getInput("Do you want to overwrite the file? Enter Y or N. Do not enter anything other than 1 Y or 1 N \n", yesorno, 2);
}
}
fclose(fpoint);
**/
FILE *index;
index = fopen(argv[1], "w");
//puts("here");
writeToIndex(index, tail->next); //tail is pointing to the first word node
puts("there");
if (doesItExist(argv[1]) == 0) {
return 0;
}
fclose(index);
TailTerminate();
return 0;
}
The code seg faults when I uncomment the part when I make a file pointer to read argv[1] to find out whether the user wants to overwrite the file specified in argv[1].
The program itself is just a program that makes a sort of indexer out of a directory of files and then prints it out. The directory path is specified in argv[2] and the path to print out the index is specified in argv[1].
Can someone help me with this? The rest of the program (the Tail nodes and such) are only to make a sort of list the words and frequencies that appear in the program.
You don't show all the functions called from main (getInput?), and they may content errors. From your commented code, I can only say that you wrote something like this:
FILE *f = fopen(...);
if (f != NULL)
{
/* use f */
}
fclose(f);
But it should be like this:
FILE *f = fopen(...);
if (f != NULL)
{
/* use f */
fclose(f);
}
That is, do not call fclose() with a NULL pointer.

Resources