reading data from binary file record by record - c

I'm trying to make a database that holds some datas about teams for now. The problem is that I cannot read the entered datas record by record. I want to explain it with just one example:
insert manunited,manchester,old_trafford,1878,black-rd to teams
insert chelsea,london,stamford_bridge,1905,blue-whte to teams
select colors,team_name,founding_date from teams
select prints on screen the specified information i.e. colors,stadium etc.
insert takes teams information for now.So, According to the select command, output should be followed:
black-rd manunited 1878
blue-whte chelsea 1905
But, I get
black-rd manunited 1878 blue-whte
I've been trying to analyse my error for hours. I cannot find the error(s) and mistake(s).
Thank you for all appreciated answers. By the way, I haven't maden while loop yet to insert or select commands. I'm pondering to find the error(s).
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char team_name[TEAM_NAME];
char city[TEAM_NAME];
char stadium[STADIUM_NAME];
int founding_date;
char colors[COLOR];
}teams;
int main()
{
char read_command[12];
/* checking insert */
char str1[100],str2[5],str3[18];
FILE *fptr;
teams t;
scanf("%s", read_command);
if(strcmp(read_command,"insert") == 0)
{
scanf("%s %s %s", str1, str2, str3);
//printf("%s\n%s\n%s",str1,str2,str3);
insertfunc(fptr,str1, str2, str3);
}
if(strcmp(read_command,"select") == 0)
{
scanf("%s %s %s", str1, str2, str3);
selectfunc(fptr,str1, str2, str3);
}
return 0;
}
void selectfunc(FILE *fptr,char *str1,char *str2,char *str3)
{
char *buff;
buff = (char*) malloc(strlen(str1) + 1);
strcpy(buff,str1);
char *token;
const char comma[2] = ",";
if(strcmp(str3,"teams") == 0)
{
teams t;
fptr = fopen("teams.bin","rb");
if( fptr == NULL )
{
perror("File cannot be opened.");
exit(1);
}
else
{
while(fread(&t,sizeof(teams),1,fptr) == 1)
{
/* get the first token */
token = strtok(buff, comma);
while( token != NULL )
{
if(strcmp(token,"team_name") == 0)
{
printf(" %s ",t.team_name);
}
else if(strcmp(token,"city") == 0)
{
printf(" %s ",t.city);
}
else if(strcmp(token,"stadium") == 0)
{
printf(" %s ",t.stadium);
}
else if(strcmp(token,"colors") == 0)
{
printf(" %s ",t.colors);
}
else
{
printf(" %d ",t.founding_date);
}
token = strtok(NULL, comma);
}
}
}
fclose(fptr);
}
}
void insertfunc(FILE *fptr,char *str1,char *str2,char *str3)
{
char *buff;
buff = (char*) malloc(strlen(str1) + 1);
strcpy(buff,str1);
const char comma[2] = ",";
/*
if(buff == NULL)
perror("error");
*/
if(strcmp(str3,"teams") == 0)
{
teams t;
int date;
strcpy(t.team_name,strtok(buff, comma));
strcpy(t.city, strtok(NULL, comma));
strcpy(t.stadium, strtok(NULL, comma));
date = atoi(strtok(NULL, comma));
t.founding_date = date;
strcpy(t.colors, strtok(NULL, comma));
fptr = fopen("teams.bin","ab+");
if( fptr == NULL )
{
perror("File cannot be opened.");
exit(1);
}
else
{
fwrite(&t,sizeof(teams),1,fptr);
fclose(fptr);
}
}
free(buff);
}

Working code below.
Apart from changes necessary to get it to build (primarily defining sizes for structure fields), the dump_teams() function is used to log the information from a team record, and the err_exit() function reports errors succinctly. Your error checks are unchanged. My primary objection to perror() is that you typically don't get the key information, such as the file name that wasn't opened, reported. It isn't hard to upgrade err_exit() to report the system error as well as a meaningful message (such as failed to open file "teams.bin": No such file or directory) — exercise for the OP. Those functions are used to validate the information, and to make sure unexpected spellings are detected. Note that the field values in debugging are enclosed in double angle brackets <<…info…>>; this makes it easier to spot various problems, such as leading or trailing spaces, or '\r' characters in input data. The assert() ensures that str2 is used in the two main functions. The select data formatting is made more uniform. There's a single name data_file to hold the data file name.
The key bug fix is in the selectfunc() loop, where the value of str1 is copied into buff afresh for each record.
This is a lazy way of working; it would be better to preparse the string once and then iterate over the data records using the data structure generated by the preparsing. This would matter more if there were millions of records than if there are just 2, of course.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { TEAM_NAME = 20, STADIUM_NAME = 20, COLOR = 20 };
static const char data_file[] = "teams.bin";
typedef struct
{
char team_name[TEAM_NAME];
char city[TEAM_NAME];
char stadium[STADIUM_NAME];
int founding_date;
char colors[COLOR];
} teams;
void insertfunc(char *str1, char *str2, char *str3);
void selectfunc(char *str1, char *str2, char *str3);
void err_exit(const char *fmt, ...);
void dump_teams(FILE *fp, const char *tag, const teams *team);
int main(void)
{
char read_command[12];
char str1[100], str2[5], str3[18];
if (scanf("%s", read_command) != 1)
err_exit("read for command failed");
else if (strcmp(read_command, "insert") == 0)
{
if (scanf("%99s %4s %17s", str1, str2, str3) != 3)
err_exit("read for insert failed");
insertfunc(str1, str2, str3);
}
else if (strcmp(read_command, "select") == 0)
{
if (scanf("%99s %4s %17s", str1, str2, str3) != 3)
err_exit("read for select failed");
selectfunc(str1, str2, str3);
}
else
err_exit("Unrecognized command <<%s>>", read_command);
return 0;
}
void selectfunc(char *str1, char *str2, char *str3)
{
FILE *fptr;
char *buff;
buff = (char*) malloc(strlen(str1) + 1);
char *token;
fprintf(stderr, "select: <<%s>> <<%s> <<%s>>\n", str1, str2, str3);
assert(strcmp(str2, "from") == 0);
const char comma[2] = ",";
if (strcmp(str3, "teams") == 0)
{
teams t;
fptr = fopen(data_file, "rb");
if (fptr == NULL)
{
perror("File cannot be opened.");
exit(1);
}
else
{
while (fread(&t, sizeof(teams), 1, fptr) == 1)
{
dump_teams(stderr, "select", &t);
strcpy(buff, str1);
/* get the first token from command str1 */
token = strtok(buff, comma);
while (token != NULL)
{
fprintf(stderr, "token = <<%s>>\n", token);
if (strcmp(token, "team_name") == 0)
{
printf(" %-20s", t.team_name);
}
else if (strcmp(token, "city") == 0)
{
printf(" %-20s", t.city);
}
else if (strcmp(token, "stadium") == 0)
{
printf(" %-20s", t.stadium);
}
else if (strcmp(token, "colors") == 0)
{
printf(" %-20s", t.colors);
}
else if (strcmp(token, "founding_date") == 0)
{
printf(" %d", t.founding_date);
}
else
err_exit("Unrecognized field name <<%s>>", token);
token = strtok(NULL, comma);
}
putchar('\n');
}
}
fclose(fptr);
}
else
err_exit("Unrecognized data source <<%s>>", str3);
}
void insertfunc(char *str1, char *str2, char *str3)
{
FILE *fptr;
char *buff;
buff = (char*) malloc(strlen(str1) + 1);
strcpy(buff, str1);
fprintf(stderr, "select: <<%s>> <<%s> <<%s>>\n", str1, str2, str3);
assert(strcmp(str2, "to") == 0);
const char comma[2] = ",";
if (strcmp(str3, "teams") == 0)
{
teams t;
int date;
strcpy(t.team_name, strtok(buff, comma));
strcpy(t.city, strtok(NULL, comma));
strcpy(t.stadium, strtok(NULL, comma));
date = atoi(strtok(NULL, comma));
t.founding_date = date;
strcpy(t.colors, strtok(NULL, comma));
dump_teams(stderr, "insert", &t);
fptr = fopen(data_file, "ab+");
if (fptr == NULL)
{
perror("File cannot be opened.");
exit(1);
}
else
{
if (fwrite(&t, sizeof(teams), 1, fptr) != 1)
err_exit("fwrite failed");
fclose(fptr);
}
}
else
err_exit("Unrecognized data destination <<%s>>", str3);
free(buff);
}
void dump_teams(FILE *fp, const char *tag, const teams *team)
{
assert(fp != 0 && tag != 0 && team != 0);
fprintf(fp, "%s\n", tag);
fprintf(fp, "%8s: <<%s>>\n", "Team", team->team_name);
fprintf(fp, "%8s: <<%s>>\n", "City", team->city);
fprintf(fp, "%8s: <<%s>>\n", "Stadium", team->stadium);
fprintf(fp, "%8s: <<%d>>\n", "Founded", team->founding_date);
fprintf(fp, "%8s: <<%s>>\n", "Colours", team->colors);
fflush(fp);
}
#include <stdarg.h>
void err_exit(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
exit(EXIT_FAILURE);
}
Sample output
The files cmd1, cmd2 and cmd3 contain the three command lines from the question.
$ ./rw < cmd1
select: <<manunited,manchester,old_trafford,1878,black-red>> <<to> <<teams>>
insert
Team: <<manunited>>
City: <<manchester>>
Stadium: <<old_trafford>>
Founded: <<1878>>
Colours: <<black-red>>
$ ./rw < cmd2
select: <<chelsea,london,stamford_bridge,1905,blue-white>> <<to> <<teams>>
insert
Team: <<chelsea>>
City: <<london>>
Stadium: <<stamford_bridge>>
Founded: <<1905>>
Colours: <<blue-white>>
$ ./rw < cmd3
select: <<colors,team_name,founding_date>> <<from> <<teams>>
select
Team: <<manunited>>
City: <<manchester>>
Stadium: <<old_trafford>>
Founded: <<1878>>
Colours: <<black-red>>
token = <<colors>>
token = <<team_name>>
token = <<founding_date>>
black-red manunited 1878
select
Team: <<chelsea>>
City: <<london>>
Stadium: <<stamford_bridge>>
Founded: <<1905>>
Colours: <<blue-white>>
token = <<colors>>
token = <<team_name>>
token = <<founding_date>>
blue-white chelsea 1905
$ ./rw < cmd3 2>/dev/null
black-red manunited 1878
blue-white chelsea 1905
$
Note that by placing the debug information on standard error, it is easy to separate the debugging information from the normal output, as in the last run.

Related

How to print part of a tokenized string from a file in C

I am having some trouble with a tokenized string that is a line from a file. I want to print the line from where the token is found, but I cannot seem to find a way around it. Please, ignore the output to the file part as well as the author, class and method if statements as I have them sorted out.
For example, I want it to print from this line: #return the matric only this part: the matric
Code:
#include <stdio.h>
#include <string.h>
int main (int argc, char **argv)
{
char line [1000];
char *delimeters = ".,; \t\n";
int total_lines = 0;
int total_comments = 0;
int nonblank_lines = 0;
FILE *input = fopen (argv[2], "r");
FILE *output = fopen (argv[4],"w");
while(fgets(line,1000,input) != NULL)
{
char *word = strtok(line, delimeters);
total_lines++;
if(word != NULL)
{
nonblank_lines++;
}
if(word != NULL && strcmp(word,"/**") == 0)
{
total_comments++;
}
while(word != NULL)
{
if(word != NULL && strcmp(word,"#author") == 0)
{
char *author_name = strtok(NULL, delimeters);
char *author_surname = strtok(NULL, delimeters);
printf ("Author: %s %s\n", author_name, author_surname);
}
if(word != NULL && strcmp(word,"public") == 0)
{
char *jmp = strtok(NULL, delimeters);
if(jmp != NULL && strcmp(jmp,"class") == 0)
{
char *class_name = strtok(NULL, delimeters);
printf ("Class %s\n", class_name);
}else{
char *method_name = strtok(NULL, delimeters);
printf ("Method %s\n", method_name);
}
}
if(word != NULL && strcmp(word,"#return") == 0)
{
printf("Enters IF 4\n");
char *return_value = strtok(NULL, delimeters);
printf ("Returns: %s\n", return_value;
}
/*if(word != NULL && strcmp(word,"#param") == 0)
{
printf("Enters IF 5\n");
char *parameters = strtok(NULL, delimeters);
printf("Parameter: %s\n", parameters);
//int param_found
}*/
word = strtok(NULL, delimeters);
}
}
printf ("The total number of lines is %d\n", total_lines);
printf ("The total number of non-blank lines is %d\n", nonblank_lines);
printf ("The total number of comments is %d\n", total_comments);
fclose(input);
fclose(output);
return 0;
}
So following my comment, you'd want something like this block for each if statement:
while(fgets(line, 1000, input) != NULL)
{
char *first_word_in_line = strtok(line, delimeters);
if(strcmp(first_word_in_line, "#return") == 0)
{
char *word = strtok(NULL, delimeters);
printf ("Returns: ");
while(word != NULL)
{
printf ("%s ", word);
word = strtok(NULL, delimeters);
}
printf("\n");
}
}
Note that I added another variable first_word_in_line on top word - it's not a must but it makes it less confusing when you code and it shows that the first word in the line has a different meaning because that's just the title.
Also, you should read about strcmp vs. strncmp. Usually it's a good practise to use strncmp.
The answer I got is this:
if(word != NULL && strcmp(word,"#return") == 0)
{
char *return_value = strtok(NULL, delimeters);
printf ("Returns: ");
while(return_value != NULL)
{
printf ("%s ", return_value);
return_value = strtok(NULL, delimeters);
}
printf("\n");
}
You messed a bit with the parameters at the beginning.
./program.c input.txt output.txt
argv[0] will give you the name of the program (in this case program.c), argv[1] the first param you pass (input.txt) and argv[2] the second parameter (output.txt)
There was also a missing bracket and for a comment /* comment */ one star in c is enough.
That fixed it worked for me.
But be aware if you cut the comment with strtok() the way you do it, there has to be a delimiter left and right to the start of your comment.
somecode; /* This should be recognized */
somecode;/* This not */
somecode; /*This neither */
Here ist the corrected code:
#include <stdio.h>
#include <string.h>
int main (int argc, char **argv)
{
char line [1000];
char *delimeters = ".,; \t\n";
int total_lines = 0;
int total_comments = 0;
int nonblank_lines = 0;
FILE *input = fopen (argv[1], "r");
FILE *output = fopen (argv[2],"w");
while(fgets(line,1000,input) != NULL)
{
char *word = strtok(line, delimeters);
total_lines++;
if(word != NULL)
{
nonblank_lines++;
}
if(word != NULL && strcmp(word,"/*") == 0)
{
total_comments++;
}
while(word != NULL)
{
if(word != NULL && strcmp(word,"#author") == 0)
{
char *author_name = strtok(NULL, delimeters);
char *author_surname = strtok(NULL, delimeters);
printf ("Author: %s %s\n", author_name, author_surname);
}
if(word != NULL && strcmp(word,"public") == 0)
{
char *jmp = strtok(NULL, delimeters);
if(jmp != NULL && strcmp(jmp,"class") == 0)
{
char *class_name = strtok(NULL, delimeters);
printf ("Class %s\n", class_name);
}else{
char *method_name = strtok(NULL, delimeters);
printf ("Method %s\n", method_name);
}
}
if(word != NULL && strcmp(word,"#return") == 0)
{
printf("Enters IF 4\n");
char *return_value = strtok(NULL, delimeters);
printf ("Returns: %s\n", return_value);
}
/*if(word != NULL && strcmp(word,"#param") == 0)
{
printf("Enters IF 5\n");
char *parameters = strtok(NULL, delimeters);
printf("Parameter: %s\n", parameters);
//int param_found
}*/
word = strtok(NULL, delimeters);
}
}
printf ("The total number of lines is %d\n", total_lines);
printf ("The total number of non-blank lines is %d\n", nonblank_lines);
printf ("The total number of comments is %d\n", total_comments);
fclose(input);
fclose(output);
return 0;
}

Trying to write numbers from a .txt to a binary file

I am currently reading a text file that is below:
New York,4:20,3:03
Kansas City,12:03,3:00
North Bay,16:00,0:20
Kapuskasing,10:00,4:02
Thunder Bay,0:32,0:31
I have the city names being fprintf to a new .txt file which works fine, however I am trying to take the times and print them to a binary file and am stuck as to where I am having an issue. Any help would be appreciated.I need to store the times as 04, 20 for "New York" in a 2 byte value and having issues parsing to have this specifically.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable: 4996)
// a function to remove the trailing carraige return
void clearTrailingCarraigeReturn(char* buffer);
/* == FUNCTION PROTOTYPES == */
/* == CONSTANTS == */
// MAIN
typedef struct
{
char cityName[20];
short flightTime;
short layoverTime;
} Flight;
Flight parseFlight(char* line) {
char delimiter[2] = ",";
Flight flight;
char* token = strtok(line, delimiter);
int i = 0;
while (token != NULL)
{
if (i == 0)
{
strcpy(flight.cityName, token);
}
if (i == 1)
{
flight.flightTime = atoi(token);
}
if (i == 2)
{
flight.layoverTime = atoi(token);
}
token = strtok(NULL, delimiter);
i++;
}
return flight;
}
int main(int argc, char* argv[])
{
FILE *fpIn, *fpOut, *fbOut;
char line[80];
Flight flight;
fpIn = fopen(argv[1], "r");
fpOut = fopen("theCities.txt", "w+");
fbOut = fopen("theTimes.dat", "wb+");
while (fgets(line, 1024, fpIn) > 0)
{
clearTrailingCarraigeReturn(line);
printf(" >>> read record [%s]\n", line);
flight = parseFlight(line);
fprintf(fpOut, "%s\n", flight.cityName);
fwrite(&flight.flightTime, sizeof(short), 1, fbOut);
fwrite(&flight.layoverTime, sizeof(short), 1, fbOut);
}
fclose(fpIn);
fclose(fpOut);
fclose(fbOut);
}
// This function locates any carraige return that exists in a record
// and removes it ...
void clearTrailingCarraigeReturn(char* buffer)
{
char* whereCR = strchr(buffer, '\n');
if (whereCR != NULL)
{
*whereCR = '\0';
}
}
Perhaps something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Time_s
{
unsigned char hours;
unsigned char minutes;
} Time_t;
typedef struct Flight_s
{
char cityName[20];
Time_t flightTime;
Time_t layoverTime;
} Flight_t;
// a function to remove the trailing carraige return
void clearTrailingCarraigeReturn(char *buffer)
{
int more;
do {
size_t index;
index = strlen(buffer);
if(!index)
break;
--index;
switch(buffer[index])
{
case '\n':
case '\r':
buffer[index] = '\0';
more = 1;
break;
default:
more = 0;
break;
}
} while(more);
return;
}
int ParseTime(char *timeStr, Time_t *time)
{
int rCode=0;
char *next;
time->hours = (unsigned char)strtoul(timeStr, &next, 10);
if(':' == *next)
{
++next;
time->minutes = (unsigned char)strtoul(next, NULL, 10);
}
return(rCode);
}
Flight_t parseFlight(char* line)
{
char delimiter[2] = ",";
Flight_t flight;
char *token = strtok(line, delimiter);
int i = 0;
while(token)
{
switch(i)
{
case 0:
strcpy(flight.cityName, token);
break;
case 1:
ParseTime(token, &flight.flightTime);
break;
case 2:
ParseTime(token, &flight.layoverTime);
break;
}
token = strtok(NULL, delimiter);
i++;
}
return(flight);
}
int main(int argc, char* argv[])
{
int rCode=0;
FILE *fpIn=NULL, *fpOut=NULL, *fbOut=NULL;
char line[80+1];
Flight_t flight;
if(argc < 2)
{
fprintf(stderr, "ERROR: argc < 2\n");
goto CLEANUP;
}
fpIn = fopen(argv[1], "r");
if(!fpIn)
{
fprintf(stderr, "ERROR: fopen(\"%s\",\"r\")\n", argv[1]);
goto CLEANUP;
}
fpOut = fopen("theCities.txt", "w+");
if(!fpOut)
{
fprintf(stderr, "ERROR: fopen(\"theCities.txt\",\"w+\")\n");
goto CLEANUP;
}
fbOut = fopen("theTimes.dat", "wb+");
if(!fbOut)
{
fprintf(stderr, "ERROR: fopen(\"theTimes.dat\",\"wb+\")\n");
goto CLEANUP;
}
while(fgets(line, sizeof(line), fpIn) > 0)
{
clearTrailingCarraigeReturn(line);
flight = parseFlight(line);
printf("%s,%02hhu:%02hhu,%02hhu:%02hhu\n",
flight.cityName,
flight.flightTime.hours, flight.flightTime.minutes,
flight.layoverTime.hours, flight.layoverTime.minutes
);
fprintf(fpOut, "%s\n", flight.cityName);
fwrite(&flight.flightTime, sizeof(Time_t), 1, fbOut);
fwrite(&flight.layoverTime, sizeof(Time_t), 1, fbOut);
}
CLEANUP:
if(fpIn)
fclose(fpIn);
if(fpOut)
fclose(fpOut);
if(fbOut)
fclose(fbOut);
return(rCode);
}

Best way to read name value pair in c

What is the best way to read name and its value from a configuration file in c programming?
Sample configuration file:
NAME=xxxx
AGE=44
DOB=mmddyyyy
WORK=zzzz
This is the code which I am using. It is working. But I would like to know if there is a better way.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getValue(char *line, char* name, char value[])
{
char* pch = NULL;
char* token = NULL;
pch = strstr(line, name);
if(pch)
{
token = strtok(pch, "=");
while (token != NULL)
{
pch = token;
token = strtok(NULL, "=");
}
pch[strcspn ( pch, "\n" )] = '\0';
strcpy(value,pch);
return 1;
}
return 0;
}
int main()
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
char value[100];
int ret = 0;
fp = fopen("test.txt", "r");
if (fp == NULL)
{
printf ("Cannot open file \n");
return -1;
}
while ((read = getline(&line, &len, fp)) != -1)
{
ret = getValue(line,"NAME",value);
if (ret)
{
printf("NAME is %s\n", value);
}
ret = getValue(line,"AGE",value);
if (ret)
{
printf("AGE is %s\n", value);
}
}
free(line);
fclose(fp);
return 0;
}
I would be also happy to hear if there is any issue with this code.
There are several issues
When the file is like below, your parsing is incorrect. it be found as long as there is this string on the line, regardless of whether it is on the value or part of the key.
NAMEX=xxxx
AGEX=44
DOB=mmddyyyyAGE
WORK=zzzzAGE
Use strtok line content will be changed. In fact, when you call getValue for the second time, the content of line is different from the file.
AGE=NAMEzzzz=1=2
From the performance, you can directly use line the substring, no need to strcpy out
It is recommended to parse the key and value first, then compare the key you are looking for multiple times. the code below is for reference only
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *trim(char *str)
{
char *start = str;
char *end = str + strlen(str);
while(*start && isspace(*start))
start++;
while(end > start && isspace(*(end - 1)))
end--;
*end = '\0';
return start;
}
int parse_line(char *line, char **key, char **value)
{
char *ptr = strchr(line, '=');
if (ptr == NULL)
return -1;
*ptr++ = '\0';
*key = trim(line);
*value = trim(ptr);
return 0;
}
int main()
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
char *key, *value;
fp = fopen("test.txt", "r");
if (fp == NULL) {
printf ("Cannot open file \n");
return -1;
}
while ((read = getline(&line, &len, fp)) != -1) {
if (parse_line(line, &key, &value))
continue;
if (strcmp(key, "NAME") == 0) {
printf("NAME is %s\n", value);
} else if (strcmp(key, "AGE") == 0) {
printf("AGE is %s\n", value);
}
}
free(line);
fclose(fp);
return 0;
}

How to edit .csv files in C

I'm new at programming, and I need help in my C project. I have to search for a city, confirm it exists in the first file (city.csv), and take its id from there. Then I have to match that id with the corresponding one in the second file (meteo.csv), and then edit its weather information, that is in that second file. However, I don't know how I can take the city id from the first file, and then how to edit the second file after obtaining all the new weather informations. Here is the code:
void addInfo() {
FILE * fp;
char id_city[100];
char city[100];
char humidity[100];
char temp_max[100];
char temp_min[100];
char pressure[100];
char date[100];
printf("Name of the city: ");
scanf("%s", city);
// I think it's here that I have to write the code for take the city's id from the first file
if (id_city != NULL) {
printf("Maximun temperature: ");
scanf("%s", temp_max);
printf("Minimun temperature: ");
scanf("%s", temp_min);
printf("Humidity: ");
scanf("%s", humidity);
printf("Pressure: ");
scanf("%s", pressure);
printf("Date, in the format YYYY-MM-DD: ");
scanf("%s", date);
fp = fopen ("meteo.csv", "a");
fprintf(fp, "%s, %s, %s, %s, %s \n", temp_max, temp_min, humidity, pressure, date); //I think there's something wrong here too...
fclose(fp);
printf("Information edited successfully");
}
The file city.csv has 152 lines and 4 columns:
(id_city,city,county,district)
such as
(56,Lisbon,Lisbon,Lisbon)
The file meteo.csv has 152 lines and 7 columns:
(id_meteo_city,id_city,temp_max,temp_min,humidity,pressure,date)
such as
(56,56,14,5,62,1025,2018-02-12)
The first thing I would do is encapsulate the data in a struct, that makes it
easier to map a line of a CSV file into an object representing a line.
If both files city.csv and meteo.csv have different columns, I'd create a
different struct for each file. If both files have the same columns, you could
use the struct. I assume that both files are different and that city has the
format meteo_id,city_id,name.
typedef struct city_t {
int meteo_id;
int city_id;
char name[100]; // no city should have
// longer than 100 chars
} city_t;
typedef struct meteo_t {
int meteo_id;
int city_id;
int tempt_max;
int tempt_mix;
double humidity;
double preassure;
char date[11];
} meteo_t;
Let's assume that both files are well formatted, otherwise you would have to
write code that checks for errors and handles them, that would be the next step
in the exercise, so I'm going to write only the basic version with basic error
recognition.
#include <stdio.h>
#include <string.h>
#include <errno.h>
// takes 2 params, the filename and a pointer
// to size_t where the number of cities is stored
city_t *read_cities(const char *filename, size_t *len)
{
if(filename == NULL || len == NULL)
return NULL;
FILE *fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
return NULL;
}
city_t *arr = NULL, *tmp;
*len = 0;
// assuming that no line will be longer than 1023 chars long
char line[1024];
while(fgets(line, sizeof line, fp))
{
tmp = realloc(arr, (*len + 1) * sizeof *arr);
if(tmp == NULL)
{
fprintf(stderr, "could not parse the whole file %s\n", filename);
// returning all parsed cities so far
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
arr = tmp;
// %99[^\n] is to read up to 99 characters until the end of the line
if(sscanf(line, "%d,%d,%99[^\n]", &(arr[*len].meteo_id),
&(arr[*len].city_id), arr[*len].name) != 3)
{
fprintf(stderr, "Invalid line format (skipping line):\n%s\n", line);
// skip this line, and decrement *len
(*len)--;
continue;
}
// incrementing only when parsing of line was OK
(*len)++;
}
fclose(fp);
// file is empty or
// all lines have wrong format
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
void print_cities(city_t *cities, size_t len, FILE *fp)
{
if(cities == NULL || fp == NULL)
return;
for(size_t i = 0; i < len; ++i)
fprintf(fp, "%d,%d,%s\n", cities[i].meteo_id, cities[i].citiy_id,
cities[i].name);
}
Now I've written the read and write functions for the file citiy.csv assuming the
format meteo_id;city_id;name. The print_cities allows you to print the CSV
content on the screen (passing stdout as the last argument) or to a file
(passing a FILE object as the last argument).
You can use these functions as templates for reading and writing meteo.csv, the
idea is the same.
You can use these function as follows:
int main(void)
{
size_t cities_len;
city_t *cities = read_cities("city.csv", &cities_len);
// error
if(cities == NULL)
return 1;
do_something_with_cities(cities, cities_len);
// update csv
FILE *fp = fopen("city.csv", "w");
if(fp == NULL)
{
fprintf(stderr, "Could not open city.csv for reading: %s\n",
strerror(errno));
free(cities);
return 1;
}
print_cities(cities, cities_len, fp);
fclose(fp);
free(cities);
return 0;
}
Now for your exercise: write a similar function that parses meteo.csv (using
my function as a template shouldn't be that difficult) and parse both files. Now
that you've got them in memory, it's easy to manipulate the data (insert,
update, delete). Then write the files like I did in the example and that's it.
One last hint: how to search for a city:
// returns the index in the array or -1 on error or when not found
int search_for_city_by_name(city_t *cities, size_t len, const char *name)
{
if(cities == NULL || name == NULL)
return -1;
for(size_t i = 0; i < len; ++i)
if(strcmp(name, cities[i].name) == 0)
return i;
// not found
return -1;
}
Now I have given you almost all parts of the assignment, all you have to do is
stick them together and write the same functions for the meteo.csv file.
To edit one field:
void _ERR(char a) {
if (a == "f") printf("\n\tError File !!\n\n");
if (a == "m") printf("\n\tError Memory !!\n\n");
exit(1); }
char* stmm(const char* src) {
char* dst = malloc(strlen(src) + 1);
if (dst == NULL) return NULL;
strcpy(dst, src);
return dst; }
const char* getfield(char* line, int num) {
const char* tok;
for (tok = strtok(line, ",");
tok && *tok;
tok = strtok(NULL, ",\n"))
{
if (!--num)
return tok;
}
return NULL; }
void edit_file(char* FName, char* NewValue, int row, int col) {
int i, r = 0, c;
char line[1024];
FILE* fr, * fw;
fr = fopen(FName, "r");
fw = fopen(FName, "r+");
if (fr == NULL|| fw == NULL) _ERR("f");
while (fgets(line, 1024, fr))
{
char* tmp = stmm(line);
if (tmp == NULL) _ERR("m");
for (i = 0, c = 1; i < strlen(tmp); i++) {
if (tmp[i] == 44) c++;
}
for (i = 0; i < c; i++) {
if (r == row && i+1 == col) {
fprintf(fw,"%s", NewValue);
} else {
free(tmp);
tmp = stmm(line);
if (tmp == NULL) _ERR("m");
fprintf(fw,"%s", getfield(tmp, i + 1));
}
(i < c - 1) ? fprintf(fw,",") : fprintf(fw,"\n");
}
free(tmp);
r++;
}
fclose(fr);
fclose(fw); }
edit_file(".\FileName.csv","NewValue",Row,Column);

Having problems with fprintf

I am trying to write a code that simulates an Anti-Virus scan, it scans 5 specific files and then creates a file named AntiVirusLog.txt. In this file it writes the results, for example PSY.avi INFECTED. An infected file is a file that contains the string in the file youtubesign.
My problem is when I try to print in the results to the file AntiVirusLog.txt it does not print anything and leaves the file blank.
My code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<dirent.h>
#define BUZZ_SIZE 1024
int fast_scan(char *fname, char *str, FILE *fs);
int slow_scan(char *fname, char *str, FILE *fs);
int main(int argc, char *argv[])
{
char name[100];
char choice[5];
char buff[BUZZ_SIZE];
FILE *f7, *f2;
struct dirent *de;
DIR *dr = opendir(argv[1]);
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory");
return 0;
}
f2 = fopen(argv[2], "rb");
f7 = fopen("AntiVirusLog.txt", "wt");
printf("Welcome to Amnon's Anti-Virus program\n which scan would you like to choose?:\n");
printf("Fast: check only the first and the last 20% of the file\n Slow: Checks the entire file\n");
printf("Enter fast for a fast scan and slow for a slow scan\n");
scanf("%s", choice);
if ((strcmp(choice, "slow"))==0)
{
while ((de = readdir(dr)) != NULL)
{
strcpy(name, argv[1]);
strcat(name, de->d_name);
if((fgets(buff, BUZZ_SIZE, f2)) != NULL)
{
slow_scan(name, buff, f7);
}
}
}
if ((strcmp(choice, "fast")) == 0)
{
while ((de = readdir(dr)) != NULL)
{
strcpy(name, argv[1]);
strcat(name, de->d_name);
if ((fgets(buff, BUZZ_SIZE, f2)) != NULL)
{
fast_scan(name, buff, f7);
}
}
}
printf("The scan was made successfuly, check the file AntiVirusLog.txt to see the results\n");
closedir(dr);
fclose(f2);
fclose(f7);
system("PAUSE");
return (0);
}
int slow_scan(char *fname, char *str, FILE *fs)
{
int findres = 0;
FILE *fp;
char temp[BUZZ_SIZE];
if ((fopen_s(&fp, fname, "rb")) != NULL)
{
return(-1);
}
while ((fgets(temp, BUZZ_SIZE, fp)) != NULL)
{
if ((strstr(temp, str)) != NULL)
{
fprintf(fs, "%s INFECTED\n", fname);
findres++;
}
}
if (findres==0)
{
fprintf(fs, "%s NOT INFECTED\n", fname);
}
fclose(fp);
return(0);
}
int fast_scan(char *fname, char *str, FILE *fs)
{
int findres=0;
int i, j, len, partlen;
FILE *fp;
if ((fopen_s(&fp, fname, "rb")) != NULL)
{
return(-1);
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
partlen = (len * 20) / 100;
char *temp=malloc(partlen);
while ((fgets(temp, BUZZ_SIZE, fp)) != NULL)
{
for (i = 0; i < partlen; i++)
{
if (temp[i]=str[i])
{
findres++;
}
if (temp[i] != str[i])
{
i = partlen + 1;
}
if (findres == partlen)
{
fprintf(fs, "%s INFECTED\n", fname);
i = partlen + 1;
}
}
for (j = len - partlen; j < len; j++)
{
if (temp[j] = str[j])
{
findres++;
}
if (temp[j] != str[j])
{
j = partlen + 1;
}
if (findres == partlen)
{
fprintf(fs, "%s INFECTED\n", fname);
j = partlen + 1;
}
}
}
if (findres!= partlen)
{
fprintf(fs, "%s NOT INFECTED\n", fname);
}
fclose(fp);
return(0);
}
There are primarily two major issues with your code
Point 1: In your code, for the series of calls like
search_sign(argv[1], buff, f7);
you're using buff uninitialized. The buff is then passed as the second parameter of search_sign(), (to be accepted as str) which is again used as the search string in strstr().
As buff is an automatic local variable, the initial content (value) is garbage (indeterminate) and hence , when used as the search key in strstr(), will invoke undefined behaviour.
Point 2: That said, as my previous comment, you should always be checking the success of fopen() call(s) before using the returned file pointer any further.
I have used some of the advices listen and found some fixes of my own and now it works perfectly! the updated code looks like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUZZ_SIZE 1024
int search_sign(char *fname, char *str, FILE *fs);
int main(int argc, char *argv[])
{
char buff[BUZZ_SIZE];
FILE *f,*f7;
f7 = fopen("AntiVirusLog.txt", "wt");
f = fopen(argv[1], "rb");
if ((fgets(buff, BUZZ_SIZE, f)) != NULL)
{
search_sign(argv[2], buff, f7);
search_sign(argv[3], buff, f7);
search_sign(argv[4], buff, f7);
search_sign(argv[5], buff, f7);
search_sign(argv[6], buff, f7);
}
printf("The scan was made successfuly, check the file AntiVirusLog.txt to see the results\n");
fclose(f);
fclose(f7);
system("PAUSE");
return (0);
}
int search_sign(char *fname, char *str, FILE *fs)
{
int findres = 0;
FILE *fp;
char temp[BUZZ_SIZE];
if ((fopen_s(&fp, fname, "rb")) != NULL)
{
return(-1);
}
while ((fgets(temp, BUZZ_SIZE, fp)) != NULL)
{
if ((strstr(temp, str)) != NULL)
{
fprintf(fs, "%s INFECTED\n", fname);
findres++;
}
}
if (findres==0)
{
fprintf(fs, "%s NOT INFECTED\n", fname);
}
fclose(fp);
return(0);
}

Resources