C parsing fgets() char array into multiple variables with sscanf using structures - c

In this program it compiles but i get segmentation fault, sscanf(str, "%d %s %s %d %f", &pos, z[pos-1].city, z[pos-1].state, &z[pos-1].population, &z[pos-1].growth); its because this line z[pos-1].city, z[pos-1].state, doesnt have & but when i add that i get warning: format â%sâ expects type âchar *â, but argument 4 has type âchar (*)[200]â.
i'm sure there is another way of doing this, i need to use structure, read in file store info into an array of a structure then displaying the array. the printf works just storing the city and state into char array.
I commented the area where i tried to initialize to array and had an incompatable type with the char array value all three: a, 'a', "a".
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
int rank;
char city[200];
char state[50];
int population;
float growth;
}city_t;
void read_data(city_t *z)
{
FILE *inp;
inp = fopen("cities.dat", "r");
char str[256];
int pos = 0;
if(inp==NULL)
{
printf("The input file does not exist\n");
}
else
{
/*reads and test until eof*/
while(1)
{
/*if EOF break while loop*/
if(feof(inp))
{break;}
else
{
/*read in a number into testNum*/
//fscanf(inp, "%d", &testNum);
fgets(str,sizeof(str),inp);
sscanf(str, "%d %s %s %d %f", &pos, z[pos-1].city, z[pos-1].state, &z[pos-1].population, &z[pos-1].growth);
z[pos-1].rank = pos;
}
}
}
fclose(inp);
}
void print_data(city_t *z, int size)
{
int i;
printf("rank\tcity\t\tstate\tpopulation\t\tgrowth\n");
for(i=0;i<size;i++)
{
printf("%d\t%s\t\t%s\t\%d\t\t%f\n", z[i].rank, z[i].city, z[i].state, z[i].population, z[i].growth);
}
}
int main()
{
int i;
city_t cities[10];
/*for(i;i<10;i++)
{
cities[i].rank = 0;
cities[i].city = "a";
cities[i].state = "a";
cities[i].population = 0;
cities[i].growth = 0.00;
}*/
read_data(&cities[0]);
print_data(&cities[0], 10);
return(0);
}
{
dat file
1 New_York NY 8143197 9.4
2 Los_Angeles CA 3844829 6.0
3 Chicago IL 2842518 4.0
4 Houston TX 2016582 19.8
5 Philadelphia PA 1463281 -4.3
6 Phoenix AZ 1461575 34.3
7 San_Antonio TX 1256509 22.3
8 San_Diego CA 1255540 10.2
9 Dallas TX 1213825 18.0
10 San_Jose CA 912332 14.4
}

In read_data, you scan into z[pos-1].... where pos starts at 0. So you will be writing beyond (before) the extend of the array.
Just do a global replace of pos-1 with pos and also increment pos; you can also use fscanf, so your reading can be:
for (int pos=0; !feof(inp); ++pos)
{
fscanf(inp, "%d %s %s %d %f",
&z[pos].rank,
z[pos].city,
z[pos].state,
&z[pos].population,
&z[pos].growth);
}

Related

Take data from a text file and insert the data into another file

I am trying to write a program in C to take the data from the input.txt file and insert it into the record.txt file in the ascending order of the students’ ID.
Content of input.txt:
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
Content of record.txt:
1287 Nancy CS11
1865 Brown CS33
When I run the program, the following data from input.txt is supposed to be inserted into the record.file(valid data with students' ID, name and course):
2012 Bob CS21
1999 Teddy CS35
2001 Eric CS11
Then the content of record.txt file after insertion(ascending order):
1287 Nancy CS11
1865 Brown CS33
1999 Teddy CS35
2001 Eric CS11
2012 Bob CS21
Below is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
typedef struct {
int id;
char name[20];
char course[5];
} Student;
int read_records(Student s[]) {
FILE *fp;
int i=0;
fp = fopen("record.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return 0;
}
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
return i;
}
void write_records(Student s[], int n) {
FILE *fp;
fp = fopen("record.txt", "w");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
for (int i=0; i<n; i++) {
fprintf(fp, "%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
fclose(fp);
}
void insert_records(Student s[], int n) {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
int i=n;
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
write_records(s, i);
printf("Insertion is done.\n");
}
void display_records(Student s[], int n) {
for (int i=0; i<n; i++) {
printf("%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
}
int main() {
int n;
Student s[MAX];
n = read_records(s);
int opt;
while (1) {
printf("1. Insert\n");
printf("2. Display\n");
printf("3. Exit\n");
printf("Choose an option: ");
scanf("%d", &opt);
switch(opt) {
case 1: insert_records(s, n);
break;
case 2: display_records(s, n);
break;
case 3: exit(0);
}
}
}
When I run the program, the user will be asked to choose an option.
If I enter 2, it will display the content of record.txt. And it works well, as I expected.
If I enter 1, the insertion should be performed and "Insertion is done" will be printed. However, the program doesn't work as I expected. It just displays nothing.
I am confused about that, and I would like to know how to fix the program.
This loop
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
will run indefinitely1 because when fscanf encounters input it cannot convert, that input is left in the stream. The file will never exhaust.
With the file
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
the first fscanf call will convert 1 "2012" "Bob". The second call and so on will fail to convert CS21 into an integer.
Additionally, see Why is “while( !feof(file) )” always wrong?.
Also note that an unbounded %s specifier in *scanf functions is as dangerous as gets. Use a field-width specifier to limit the amount of data that can be read into your buffers. This should be the maximum allowed string length, and should be at most the size of your buffer minus one, leaving room for the null-terminating byte. (i.e., char s[256]; scanf("%255s", s);).
The solution is to never ignore the return values of the fscanf family of functions. Use these return values to determine how your program should proceed. If fscanf fails to match the expected number of conversions, you must be ready to clean up the input stream. This usually means consuming characters until the end of a line is found.
This is easier said than done, however. As seen above, the way %s skips whitespace (including newline characters) means it can overreach looking for valid characters.
The general suggestion is to avoid scanf/fscanf, and instead read entire lines with fgets and then parse those lines with sscanf (or other tools).
In practice this looks like:
#include <stdio.h>
#include <stdlib.h>
#define INPUT "input.txt"
int main(void)
{
FILE *file = fopen(INPUT, "r");
if (!file) {
perror(INPUT);
return EXIT_FAILURE;
}
struct {
int id;
char name[32];
char course[16];
} student;
char buffer[512];
while (fgets(buffer, sizeof buffer, file)) {
int cv = sscanf(buffer, "%d%31s%15s",
&student.id, student.name, student.course);
if (3 == cv) {
/* three successful conversions, do whatever with `student` */
printf("%s<%d> [%s]\n",
student.name, student.id, student.course);
}
}
fclose(file);
}
With your input.txt file this prints:
Bob<2012> [CS21]
Teddy<1999> [CS35]
Eric<2001> [CS11]
CS12<2011> [CS87]
Once you know sscanf succeeded in parsing the expected number of conversions you can move on to validating the record (e.g., "CS12" is an invalid name, per your examples, and you probably need a way to avoid duplicate entries).
In your code you should only increment i after making sure all these steps are followed.
After you have your merged list of records, you should use qsort to sort the array with a comparison function such as
int student_sort(const void *va, const void *vb)
{
const Student *a = va, *b = vb;
return (a->id > b->id) - (a->id < b->id);
}
before writing the records out.
1. i will eventually overflow, invoking Undefined Behaviour. The outcome of the program after this point cannot be generally reasoned about.

Search returning only the first result in structure C

I am trying to read a file and print the records where the jumper has exceeded the distance provided by the user, but the search only returns the first line in the file and that's it, i know there is something wrong in the logic but i cant put my finger on it.
#include<stdio.h>
#include<string.h>
typedef struct {
char fname[30];
char lname[30];
char nationality [30];
double distance;
}Player;
void print(Player p) {
printf("%s %s %s %f \n", p.fname, p.lname, p.nationality, p.distance);
}
int search(double distance, Player allPlayers[], int max){
int i=0;
for(i=0; i<max; i++){
if(distance > allPlayers[i].distance){
return i;
}
return -1;
}
}
void load (char filename[10], Player players[]){
char tempfName [30];
char templName [30];
char tempNationality [30];
double tmpdistance;
FILE * input=fopen(filename,"r+");
if(input==NULL)
printf("Error found in opening the file\n");
else {
printf("The data are loaded successfully.\n");
int counter = 0;
while(!feof(input)){
strcpy(tempfName," ");
fscanf(input, "%s %s %s %f",tempfName, templName, tempNationality, &tmpdistance);
if(strcmp(tempfName, " ") !=0){
strcpy(players[counter].fname, tempfName);
strcpy(players[counter].lname, templName);
strcpy(players[counter].nationality, tempNationality);
players[counter].distance = tmpdistance;
counter++;
}
}
fclose(input);
}
}
int main (int argc, char *argv[]){
Player players2[40];
char myFileName[10] ="jump.txt";
load(myFileName, players2);
double tmpdistance;
printf("Please enter the distance threshold: ");
scanf("%lf", &tmpdistance);
printf("The jumpers exceeded 8.10 m are: \n");
int index = -1;
index = search(tmpdistance,players2,40);
if(index!=-1)
print(players2[index]);
else
printf("NOT Found! \n");
return 0;
}
some sample data from the file I'm trying to read :
Arsen Sargsyan Armenia 7.62
Boleslav Skhirtladze Georgia 7.26
Christian Reif Germany 7.92
Christopher Tomlinson Great_Britain 8.06
In your search() function:
if(distance > allPlayers[i].distance){
return i;
}
This will return the first jumper whose performance is less than the distance provided.
If you replace it with:
if(allPlayers[i].distance > distance){
return i;
}
It will return the first jumper whose performance is greater than the distance provided, which is what you want.
Also:
Don't loop over feof() to read a file: Why is “while ( !feof (file) )” always wrong?
You're printing "The data are loaded successfully" when you haven't loaded the data yet - you've just opened the file
You don't need the arguments to main()
You don't need to initialise index to -1 as you assign it in the next line
You don't need to specify the size of myFileName as the compiler can work it out

Comparing char-array in a struct with user input char-array

I am writing a code to search for a specific student in a file, and calculate it's average. everything works except for one thing. When I input the student's name in a char variable, to search for him in the file, the compiler does not get it is equal to a char in the struct of the file...
#include <stdio.h>
#include <stdlib.h>
#define nl printf("\n")
#define N 100
struct student {
char id[N][N];
int vote[10][10];
};
int main()
{
struct student stud;
FILE *filer;
int i=0, j=0, k=0, n_stud=0;
char checker[1], who[N];
float avg[10], mark=0.0, count=0.0;
printf("Introduce student id: ");
scanf("%14s", &who);
printf("Searching for student %s...\n", who);
nl;
filer=fopen("students.dat", "r");
if(filer==NULL) {
printf("Can't open file...\n");
}
for(i=0; fscanf(filer, "%s", &stud.id[i])!=NULL && fscanf(filer, "%c", &checker)!=EOF; ++i) {
for(j=0; fscanf(filer, " %d", &stud.vote[i][j])!=-1; ++j ) {
if(stud.vote[i][j]!=-1) {
mark=mark+stud.vote[i][j];
count++;
} else {
for(k=j; k<10; k++) {
stud.vote[i][k]=0;
}
break;
}
}
n_stud++;
avg[i]=mark/count;
mark=0.0; count=0.0;
}
for(i=0; i<n_stud; ++i){
if (who == stud.id[i]) { //HERE IS THE PROBLEM!!!
printf("Student %s's average is: %.2f", stud.id, avg[i]);
}
}
nl;
fclose(filer);
return EXIT_SUCCESS;
}
File
s11111 30 28 18 -1
sa44er44 23 18 30 18 29 18 29 -1
s33333 30 30 -1
22222idx 18 -1
You cannot compare C-"strings" (which in fact are just char-arrays) using the ==-operator:
if(who==stud.id[i]){
Use the strcmp() function instead:
if (strcmp(who, stud.id[i]) == 0) {
Unrelated, but still important: You want to make sure to not let the user overflow who.
You can do this by telling scanf() the size of who like this:
scanf("%14s", who); /* Tell it one less to have a spare char
to store the '0'-terminator. */
Although scanf(() typically expects an address as argument, you won't pass the address of who, but just who, because an array (which `who^ is) decays to the address of its 1st element., when being passed to a function.

clueless, c - cannot find the reason why this isn't working - printf

I've been working on this for the last few hours and I cannot seem to find why it isn't working, the printf in the store_car_stats function prints weird stuff from my argv and it runs through on gdb but seg faults normally. its really weird and I cannot seem to find the problem.
the input file it requires is
3
1993 Chevy Blazer 100200
2007 Honda Civic 23787
1903 Ford ModelT 90000
the update file is
4
1993 Chevy Blazer 39
1903 Ford ModelT 20
1993 Chevy Blazer 2
2014 Jeep Patriot 100
my code
#include <stdio.h>
#include <stdlib.h>
#define STRLEN 20
typedef struct automobiles{
int year;
char* make;
char* model;
int miles;
}Car;
void fill_garage(Car** carage,int*size,char* inputFile);
int equals(Car* garage,int year,char* make,char* model);
void drive_cars(Car* garage,int* num_cars,char* driving_records);
void store_car_stats(Car* garage,int num_cars,char* outFile);
void empty_garage(Car* garage,int num_cars);
int main(int argc,char* argv[]){
if(argc!=4){
printf("Invalid number of arguments, You have %d",argc);
return 0;
}
int size;
int newsize;
Car* mywhips;
fill_garage(&mywhips,&size,argv[1]);
printf("\n ))) %d %s %s %d",mywhips[1].year,mywhips[1].make,mywhips[1].model,mywhips[1].miles);
drive_cars(mywhips,&newsize,argv[2]);
store_car_stats(mywhips,newsize,argv[3]);
empty_garage(mywhips,newsize);
return 0;
}
void fill_garage(Car** warehouse,int*size,char*inputFile){
FILE* file=fopen(inputFile,"r");
int i;
Car* garage;
fscanf(file,"%d",size); //get size and place in address
if(file==NULL){//did file open correctly?
printf("File returned NULL when attempting to read..try again");
}
garage=malloc(sizeof(Car)*(*size)); //reserve memory for the file input
warehouse = &garage;
printf("input File DATA \n");
for(i=0;i<*size;i++){
garage[i].make = malloc(sizeof(char)*STRLEN); //reserve memory for the make
garage[i].model = malloc(sizeof(char)*STRLEN); //and model
//scan input and place in struct
fscanf(file,"%d %s %s %d",&garage[i].year,garage[i].make,garage[i].model,&garage[i].miles); //seg fault here
printf("\nYear: %d \nMake: %s\nModel: %s\nMiles: %d\n",garage[i].year,garage[i].make,garage[i].model,garage[i].miles); //seg fault here
}
fclose(file);
//printf("here");
return;
}
int equals(Car* car,int year,char* make,char* model){
if(year == car->year){
if(make == car->make){
if(model == car->model){
return 1;
}
}
}
return 0;
}
void drive_cars(Car* garage,int* num_cars,char* driving_records){
FILE* file=fopen(driving_records,"r");
int year,miles,i;
char* make;
char* model;
if(file == NULL){
printf("update file opened NULL");
}
fscanf(file,"%d",num_cars);
for(i=0;i<*num_cars;i++){
make = malloc(sizeof(char)*(*num_cars));
model = malloc(sizeof(char)*(*num_cars));
fscanf(file,"%d%s%s%d",&year,make,model,&miles);
printf("\nYear: %d \nMake: %sModel: %s\nMiles: %d\n",year,make,model,miles);
if(equals(&(garage[i]),year,make,model)==1){
printf("here");
garage[i].miles+=miles;
}
}
//printf("here");
free(make);
free(model);
}
void store_car_stats(Car* garage, int num_cars,char* outFile){
FILE* file=fopen(outFile,"w");
//printf("store_car_stats\n");
if(file == NULL){
printf("ouptput file returned NULL");
return;
}
int i;
printf("%d",num_cars);
for(i=0;i<num_cars;i++){
printf("\nYour %d %s %s has now driven %d", garage[i].year, garage[i].make, garage[i].model, garage[i].miles);
fprintf(file,"\n\n\nA %d %s %s that has now driven %d",garage[i].year,garage[i].make,garage[i].model,garage[i].miles);
}
fclose(file);
}
void empty_garage(Car* garage, int num_cars){
int i;
for(i=0;i<num_cars;i++){
//free(garage[i].make);
//free(garage[i].model);
}
//free(garage);
}
In fill_garage, this is an issue:
warehouse = &garage;
The garage is a local variable, and you're assinging the address of a local variable to warehouse. This is wrong, regardless of the rest of the code in the function. When the function returns, that garage variable ceases to exist, so the &garage will be pointing to who-knows-what.
You probably meant to do this:
*warehouse = garage;

Reading from a text file and plotting using GNU libplot

I've made a program that reads from a file, and alphabetically sorts names contained within the file. The file contains planet names, mass, size, color, primary body, positions x,y,z and velocity x,y,z. I'm now trying to plot each planet using this file, but I'm unsure how to go about doing that. I'm using GNU libplot to plot the planets. I'm thinking that I need to use a loop and fscanfs to get the x and y coordinates (can omit Z for now) to plot each planet.
Here's my current code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMES 20
struct planets{ //sets up initial characters and doubles to place into body.
char primaryBody[NAMES];
char name[NAMES];
char color[NAMES];
double mass;
double size;
double posx, posy, posz;
double velx, vely, velz;
};
struct planets body[100];
int readData(FILE* fd){ //This reads the file solarsystem.txt and fills array
int current = 0;
char line[201];
char cmpline[2] = "#";
do{
fgets(line, 200, fd); // Makes sure that the number of bytes written dont overflow your character.
}while((strlen(line)<2)||(strncmp(line, cmpline, 1))==0);
sscanf(line, "%s %lf %s %lf %s", body[current].name, &body[current].mass, body[current].color, &body[current].size, body[current].primaryBody);
fscanf(fd, "%lf %lf %lf", &body[current].posx, &body[current].posy, &body[current].posz);
fscanf(fd, "%lf %lf %lf", &body[current].velx, &body[current].vely, &body[current].velz);
current++;
while(!feof(fd)){//Runs the loop until the end of the file is met.
fscanf(fd, "%s %lf %s %lf %s", body[current].name, &body[current].mass, body[current].color, &body[current].size, body[current].primaryBody);
fscanf(fd, "%lf %lf %lf", &body[current].posx, &body[current].posy, &body[current].posz);
fscanf(fd, "%lf %lf %lf", &body[current].velx, &body[current].vely, &body[current].velz);
current++;
}
return current;
}
void sortNames(int planetNames){ //traditional bubble sorting code, with slight modification.
struct planets temp[1];
for(int i=0; i<(planetNames);i++){
for(int j=0; j<(planetNames-1); j++){
if (strcmp(body[j].name, body[j+1].name)>0){
temp[0] = body[j];
body[j] = body[j+1];
body[j+1] = temp[0];
}
}
}
void printBodies(int sortNames){// code to print out the sorted file.
int planetNames = sortNames;
printf("Name Mass Color Size Primary Body\n");
printf("_____________________________________________\n");
for(int i=0; i<(planetNames); i++){
printf("%-8s %1.4E %-6s %.2lf %s\n", body[i].name, body[i].mass, body[i].color, body[i].size, body[i].primaryBody);
}
}
int main(int argc, char *argv[]){/*runs the different structs, and if file not available, will tell the user.*/
FILE *fd;
fd = fopen (argv[1], "r");
if(!fd){
printf("File not available, or user did not type file name.\n");
return 1;
}
int planetNames=readData(fd);
sortNames(planetNames);
printBodies(planetNames);
findMax(planetNames);
return 0;
}

Resources