Reading/Writing/Modifying a struct in C - c

I am taking some information from a user (name, address, contact number) and store it in a struct. I then store this in a file which is opened in "r+" mode. I try reading it line by line and see if the entry I am trying to enter already exists, in which case I exit. Otherwise I append this entry at the end of the file. The problem is that when I open the file in "r+" mode, it gives me Segmentation fault!
Here is the code:
struct cust{
char *frstnam;
char *lastnam;
char *cntact;
char *add;
};
Now consider this function. I am passing a struct of information in this function. Its job is to check if this struct already exists else append it to end of file.
void check(struct cust c)
{
struct cust cpy;
FILE *f;
f=fopen("Customer.txt","r+");
int num=0;
if (f!= NULL){
while (!feof(f)) {
num++;
fread(&cpy,sizeof(struct cust),1,f);
if ((cpy.frstnam==c.frstnam)&(cpy.lastnam==c.lastnam)&(cpy.cntact==c.cntact)&(cpy.add==c.add))
{
printf("Hi %s %s. Nice to meet you again. You live at %s and your contact number is %s\n", cpy.frstnam,cpy.lastnam,cpy.add,cpy.cntact);
return;
}
}
fwrite(&c,sizeof(struct cust),1,f);
fclose (f);
}
printf("number of lines read is %d\n",num);
}

The problem is that your structure contains pointers to strings and not strings themselves. So freading and fwriting will not work because the pointer values will be read and written but aren't valid between runs of the application.
A simple fix would be to change the structure to:
struct cust{
char frstnam[25];
char lastnam[25];
char cntact[25];
char add[25];
};
It's not a great fix, but it is a fix and might work for you.

Also, comparing strings that way won't work - that just compares the pointers.
You might want something more like this:
if ( strcmp(cpy.frstnam,c.frstnam) == 0 && strcmp(cpy.lastnam,c.lastnam) == 0 ...
{
printf("Hi ...
return;
}
That will compare the actual contents of the string arrays, not the pointers.
Also "&" is a bitwise AND and "&&" is the logical AND you want here.

If you find a matching contact, your current code will return without first closing the file. Eventually you may run out of available file descriptors and calls to fopen will fail.

Related

Problems with reading text file in C [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed 4 years ago.
I have the following code, and I can't seem to find why my IF clause is not working within the while cycle. otherwise, I am able to pull the records from the text file. It appears that when i compare the searched value against the fetched value from the text nothing happens. I am trying to print out only those records by condition contained within the file.
/*
* File: main.c
* Author:
*
* Created on 06 February 2019, 10:27
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*
*
*/
struct employee
{
char Name[100];
char Emp_ID[10];
char Sex[10];
char Department[50];
};
int main(int argc, char** argv) {
FILE *fp;
clearScreen();
struct employee emp;
fp=fopen("C:\\Users\\vdimitrov\\Documents\\NetBeansProjects\\KevinEngineering\\build\\Debug\\Cygwin-Windows\\employee.txt","r");
if (!fp) {
printf("Unable to open file!");
return -1;
}
printf("Display Employee Data Report");
while (fread(&emp,sizeof(struct employee),1,fp) != NULL) {
if(emp.Name == "Something contained in Name"){
printf("\n");
printf("\nName: %s\n", emp.Name);
printf("Emp_ID: %s\n", emp.Emp_ID);
printf("Sex: %s\n", emp.Sex);
printf("Department: %s\n", emp.Department);
}
}
printf("\nEnd Of Report\n");
fclose(fp);
return 0;
}
void clearScreen()
{
system("#cls||clear");
}
First, the if condition won't work. You compare pointers, not strings. In C, it's strcmp
Second, if your input file is a text file, you have to read strings, not a buffer of a given size (the size of struct employee). Look at fgets.
In C, you do not compare strings with ==:
if (emp.Name == "Something contained in Name")
The strcmp function is what you need for equality checking. However, if (as seems to be the case), you want a partial match (like looking for any name containing Smith), you'll need some other functions defined in string.h.
I would suggest looking into strstr, for example, one that searches a string to see if it contains another string.
In C, you can't compare arrays (including strings, which are char arrays) with the == operator. (Or rather, you can, but it checks if they reside at the same place in memory, not whether their contents are the same.)
If you want to compare one array against another, the standard method is to use memcmp, which compares two raw blocks of memory. An alternative for strings is strcmp, which stops comparing when it finds a null byte. Both of these functions return 0 if the arrays are the same, so you would want:
if(strcmp(emp.Name, "my string here") == 0)
(Also, your file-reading code is probably wrong: you're reading raw bytes instead of text. But I don't know the actual requirements so I don't know how to fix that.)

Reading in char from file into struct

For my assignment, I have to read in a text file with a varying amount of lines. They follow the following format:
AACTGGTGCAGATACTGTTGA
3
AACTGGTGCAGATACTGCAGA
CAGTTTAGAG
CATCATCATCATCATCATCAT
The first line is the original line I will testing the following ones against, with the second line giving the number of remaining lines.
I'm having trouble trying to save these to a struct, and can't even get the first line to save. I tried using the void function with an array and it seems to work, but can't seem to transfer it over to structs.
Here's my code so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LENGTH 25
struct dna {
char code[LENGTH];
};
int main(){
char filename[] = "input1.txt";
FILE *input = fopen("input1.txt","r");
char firstDna[LENGTH]="";
struct dna first;
struct dna first.code[]= "";
makeArray(input,first);
// printf("%s",filename);
system("pause");
return 0;
}
void makeArray(FILE *input,struct dna first){
int i=-1;
//nested for loops to initialze array
//from file
while(i != '\n'){
fscanf(input,"%c",first[i].code);
printf("%c", first[i].code);
i++;
}//closing file
fclose(input);
}
Since this is for a class assignment, I want to preface this by saying that a good way to tackle these types of assignments is to break it up into tasks, then implement them one by one and finally connect them. In this case the tasks might be something like:
parse the first line into a (struct containing a) char array.
parse the number into an int variable
parse each remaining line in the file like you did with the first line
test the first line against the other lines in the file (except the number)
You also mentioned in a comment that the struct is for extra credit. For that reason, I'd recommend implementing it using just a char array first, then refactoring it into a struct once you have the basic version working. That way you have something to fall back on just in case. This way of developing might seem unnecessary at this point, but for larger more complicated projects it becomes a lot more important, so it's a really good habit to get into as early as possible.
Now, let's look at the code. I'm not going to give you the program here, but I'm going to identify the issues I see in it.
Let's start with the main method:
char filename[] = "input1.txt";
FILE *input = fopen("input1.txt","r");
This opens the file you're reading from. You're opening it correctly, but the first line is in this case unnecessary, since you never actually use the filename variable anywhere.
You also correctly close the file at the end of the makeArray function with the line:
fclose(input);
Which works. It would, however, probably be better style if you put this in the main method after calling the makeArray function. It's always a good idea to open and close files in the same function if possible, since this means you will always know you didn't forget to close the file without having to look through your entire program. Again, not really an issue in a small project, but a good habit to get into. Another solution would be to put the fopen and fclose functions in the makeArray function, so main doesn't have to know about them, then just send the char array containing the filepath to makeArray instead of the FILE*.
The next issue I see is with how you are passing the parameters to the makeArray function. To start off, instead of having a separate function, try putting everything in the main method. Using functions is good practice, but do this just to get something working.
Once that's done, something you need to be aware of is that if you're passing or returning arrays or pointers to/from functions, you will need to look up the malloc and free functions, which you may not have covered yet. This can be one of the more complex parts of C, so you might want to save this for last.
Some other things. I won't go into detail about these but try to get the concepts and not just copy paste:
struct dna first.code[]= ""; should probably be first.code[0] = \0;. \0 is used in C to terminate strings, so this will make the string empty.
Passing %c to fscanf reads a single character (you can also use fgetc for this). In this case, it will probably be easier using %s, which will return a word as a string.
Assuming you do use %s, which you probably should, you will need to call it twice before the loop - once to get the first DNA sequence and another time to get the number of other DNA sequences (the number of iterations).
Each iteration of the loop will then test the original DNA sequence against the next DNA sequence in the file.
I hope that helps!
sample to fix
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LENGTH 25
struct dna {
char code[LENGTH];
};
struct dna *makeArray(FILE *input, int *n);//n : output, number of elements
int main(void){
char filename[] = "input1.txt";
FILE *input = fopen(filename,"r");
struct dna first = { "" };
fscanf(input, "%24s", first.code);//read first line
printf("1st : %s\n", first.code);
int i, size;
struct dna *data = makeArray(input, &size);//this does close file
for(i = 0; i < size; ++i){
printf("%3d : %s\n", i+1, data[i].code);
}
free(data);//release data
system("pause");
return 0;
}
struct dna *makeArray(FILE *input, int *n){//n : output, number of elements
int i;
fscanf(input, "%d", n);//read "number of remaining lines"
struct dna *arr = calloc(*n, sizeof(struct dna));//like as struct dna arr[n] = {{0}};
for(i = 0; i < *n; ++i){
fscanf(input, "%24s", arr[i].code);
}
fclose(input);
return arr;
}
a simple fix might be :
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LENGTH 25
struct dna {
char code[LENGTH];
};
void makeArray(FILE *input,struct dna *first){
int i=0;
fscanf(input,"%c",&first->code[i]);
printf("%c",first->code[i]);
while(first->code[i] != '\n' && i < LENGTH){
i++;
fscanf(input,"%c",&first->code[i]);
printf("%c",first->code[i]);
}
}
int main() {
struct dna first;
char filename[] = "input1.txt";
FILE *input = fopen(filename,"r");
makeArray(input,&first);
fclose(input);
printf("%s",first.code);
return 0;
}
PS: i tried to not change your original code
in order to change the code[Length] in the makeArray function you will have to pass it's adresse this is why i call mkaeArray function this way : makeArray(input,&first);.

Stuck on Grabbing a title from a text file

I am attempting to grab a title from a text file in a way that is completely new to me. My code is set up as follows:
struct sequence
{ char *name;
char *sequence;
int sequencelen;
};
int main(int argc, char *argv[])
{
struct sequence *newseq;
getsequence("test.txt", newseq);
}
void getsequence(const char *file, struct sequence *seq)
{
FILE *fp = fopen(file, "r");
struct sequence *seqptr = malloc(sizeof(*seq));
char c;
if (!fp)
{
exit(1);
}
while ((c = fgetc(fp)) != "\n")
{
if (c == '>')
continue;
strcat(seqptr -> name, c);
}
printf("Name: %s", seqptr -> name); //Expected output: this_is_a_new_sequence_title
}
The structure for the text file is as follows:
>this_is_a_new_sequence_title
Using structs in this way is, like I said, new to me, but seeing how it is another way to use them I would like to know how to do it. I am unsure, however, if I'm using them correctly, especially with regards to the strcat function.
Do I have to dynamically allocate memory for the struct's member variables, and, if so, how would I go about doing that? Or do I just have everything horribly wrong?
You're never allocating memory for the string. So when you call strcat(), the destination string is uninitialized memory, leading to undefined behavior.
Also, the 2nd argument to strcat() is a string, not a character. That's more undefined behavior as the library function interprets the single character as the address of a string.
You need to initialize storage space for sequence when you allocate it. Also, for code like this (dynamic strings) it's good to separate "allocated room" from "string length", and store both.

Using strings from a text file is working different than using literal strings?

I have a C program that is reading strings from a text file and using these to create structs which are being placed in a hash table (d_table). It reads from a file line by line in a loop. The runtime problem I get is that every entry added using strings taken from a file all seem to still be pointing to whatever was most recently read (so char word[] would just be "."), BUT if it was to hard code in some entries (like the second ht_insert) then it works fully as expected. This makes me think that I am missing something obvious in how C handles strings...
A line of the text file looks like: "Test This is a test line", with the last line being a "."
entry struct:
struct d_entry {
char *word;
char *desc;
};
I don't think the problem is caused at create_entry or beyond in the program, otherwise why would hard coded entries work perfectly fine? As far as I know, beyond this point entries should be indistinguishable whether the strings were hard coded or taken from a file:
struct d_entry * create_entry(char *word, char*desc) {
struct d_entry * e;
e = (struct d_entry *) malloc(sizeof(struct d_entry));
e->word = word;
e->desc = desc;
return e;
}
This is the necessary code taken from the function reading from file. I have tested plenty and the file handling and the extracting of strings from files seems to be working correctly: The first ht_insert call causes the runtime problem, the second call is an example of hard coding an entry which works perfectly correctly:
int read_from_file(const char * filename) {
char word[40];
char desc[200];
FILE * fp;
fp = fopen(filename, "r");
fscanf(fp, "%s %[^\n]]", word, desc);
while (word[0] != '.') {
ht_insert (d_table, create_entry(word, desc)):
ht_insert (d_table, create_entry("test", "test desc"));
fscanf(fp, "%s %[^\n]", word, desc);
}
fclose(fp);
return 1;
}
Any help would be greatly appreciated, thanks.
create_entry(strdup(word), strdup(desc))
Use strdup to make a heap copy of each string you store in your data structure.
Do that for both strings read from file (into temp buffer) and static strings.

Enter custom file name to be read?

I want to allow users to type the name of any .txt file to be read/written.
This is my code :
printf("Enter .txt file name\n");
scanf("%s",&fname);
FILE *inputf;
inputf=fopen(&fname,"w");
Problem is this method does not work (having &fname) as a parameter.
I can imagine its because C needs "filename.txt" for it work ... even if I enter for example : "custom.txt", the program returns an error of "Storage block not big enough for this operation"
What is the correct method to accomplish this ?
Im using C and im pretty much using basic commands .. (not too advanced)
Thanks alot !!!
The scanf statement will try to store the filename entered as input into the memory, starting from the address passed as its 2nd argument. So you have to allocate/reserve some memory and pass its address to scanf.
As you have not mentioned the type of fname, let me list the possibilities and then answer you.
char fname;
The 2nd argument of scanf and the 1st argument of fopen, both need to be char *. So, passing address of fname or &fname is valid. But it has a problem.
When you declare 'char fname' you are reserving memory for only 1 char. When scanf tries to store the input filename, it will have to write more than 1 char. So eventually you end up overwriting some other memory.
char *fname;
In this case pass fname to both scanf and fopen, instead of '&fname'.
But you have to allocate some memory (e.g. using malloc), before using fname. Otherwise fname will contain some garbage address and scanf will try to overwrite some random memory.
So either declare fname as char fname[N] or char *fname = malloc(N+1); (where N is the maximum possible length of filename you would be entering).
And then, pass fname to both scanf and fopen as follows:
scanf("%s",fname);
inputf = fopen(fname,"w");
Defining fname as a char array, and assuming you expect the filename (without extension) as input (which means you need to append the extension to it):
char fname[128];
printf("Enter .txt file name\n");
scanf("%123s",fname);
strcat(fname,".txt");
FILE *inputf;
inputf=fopen(fname,"w");
Note that an input length check is added to avoid buffer overflow errors in scanf.
I think this can help
#include <stdio.h>
void read_name(char *);
int main(void)
{
char name[BUFSIZ];
char line[BUFSIZ];
FILE *f;
printf("Name ");
read_name(name);
if ( (f=fopen(name,"r"))==NULL)
return -1;
else
return 0;
fclose(f);
}
void read_name(char *s)
{
int i;
fgets(s,BUFSIZ,stdin);
for (i=0; s[i]!='\n'; i++);
s[i]='\0';
return;
}
Try inputf = fopen(fname,"w");.
Also if you want to just read a filename, you can just do sscanf(file,"%s",t) and it will store the filename into t !

Resources