I'm writing a simple banking application in c
It saves the information in a file.
I want to load the file each time the app runs, and add the information from the file to the struct, for this purpose, I have written two functions called "loadfile" and "allocate"
In the function "loadfile" if I un-comment the comment lines, the OS throws a "stopped working" in my face :|
Can you help me with it ?
when I use (acc+i) in the "loadfile", the error shows up.
Is there a syntax problem? :o
thanks
typedef struct {
char name[20];
int id;
int balance;
char branch[10];
} account;
account *acc;
int allocate ( account *acc ) {
int num = 0 ;
char tempname[20],tempbranch[10];
int tempid = -1 ,tempbalance;
FILE *file;
file = fopen("D://bank.txt","r");
while ( !feof(file) ) {
fscanf(file,"%s %d %d %s ",tempname, &tempid, &tempbalance, tempbranch);
if (tempid != -1)
num++;
}
acc = ( account *) realloc ( acc, num * sizeof(account) );
fclose(file);
printf(" num in allocate function : %d",num);
return num;
}
int loadfile (account *acc) {
int num = allocate(acc);
char tempname[20],tempbranch[10];
int tempid ,tempbalance;
if ( num != 0 ) {
int i = 0 ;
FILE *file;
file = fopen("D:\\bank.txt","r+");
for ( i = 0 ; !feof(file) && i < num ; i++ ) {
fscanf(file,"%s ",tempname );
fscanf(file,"%d ",&tempid );
fscanf(file,"%d ",&tempbalance );
fscanf(file,"%s ",tempbranch );
printf("\n i is %d \n",i);
/* strcpy( ((acc+i)->name) , tempname);
(acc+i)->id = tempid;
(acc+i)->balance = tempbalance;
strcpy( ((acc+i)->branch) , tempbranch); */
}
fclose(file);
}
return num;
}
There are a lot of issues with the posted code. It is unclear how a separate allocation function is helpful, and the use of the file-scope variable acc is not advisable. I see no point in using realloc() here, since allocation is done only once. If you do use realloc(), you should store the result in a temporary pointer, because the function can return a NULL pointer if there is an allocation error. This leads to a memory leak if you assign directly to the pointer that you are reallocating from. I have rewritten the code to illustrate some fixes, trying to maintain the general structure of the original code.
You should check the return values of the functions that you call. realloc() and malloc() return a pointer to the allocated memory, or a NULL pointer in the event of an allocation error. You should check this value and handle the result. The scanf() functions return the number of successful assignments made. You should check this value to verify that input is as expected. It is almost always a bad idea to control a loop with feof(), because this function relies upon the end-of-file indicator being set, and this indicator is only set when an I/O operation has failed.
In the program below, fgets() is used to read a line of input from the file into a buffer, and sscanf() is used to extract the input data from the buffer. In the allocation phase, EOF or an empty line signals the end of the data. No parsing is done here, only a count of lines. For each line, space is allocated for an account. Note that the code checks for errors on opening and closing the file, as well as for an allocation error.
The loadfile() function again uses fgets() to read a line of input into buffer, and then uses sscanf() to scan the buffer. Note the use of width specifiers for the strings. These are one less than the size of the arrays that they read into, to leave space for the '\0' placed at the end of the strings by sscanf(). Also note that in the event that fewer than 4 assignments are made, the program exits with an error message. If the assignments to the temporary variables were successful, the account is updated with the data.
There are many ways that this code could be improved (most obviously by getting rid of the global variable acc), but this should provide you with a good starting point.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[20];
int id;
int balance;
char branch[10];
} account;
account *acc = NULL;
int allocate(void)
{
int num = 0 ;
char buffer[1000];
FILE *file;
file = fopen("D://bank.txt","r");
if (file == NULL) {
fprintf(stderr, "Unable to open file in allocate()\n");
exit(EXIT_FAILURE);
}
while (fgets(buffer, sizeof(buffer), file) != NULL &&
buffer[0] != '\n') {
num++;
}
acc = malloc(num * sizeof(*acc));
if (acc == NULL) {
fprintf(stderr, "Allocation error in allocate()\n");
exit(EXIT_FAILURE);
}
if (fclose(file) != 0) {
fprintf(stderr, "Unable to close file in allocate()\n");
exit(EXIT_FAILURE);
}
printf(" num in allocate function : %d\n",num);
return num;
}
int loadfile(void)
{
int num = allocate();
char buffer[1000], tempname[20],tempbranch[10];
int tempid ,tempbalance;
if ( num != 0 ) {
int i = 0 ;
FILE *file;
file = fopen("D://bank.txt","r+");
if (file == NULL) {
fprintf(stderr, "Unable to open file in loadfile()\n");
exit(EXIT_FAILURE);
}
while (fgets(buffer, sizeof(buffer), file) != NULL
&& buffer[0] != '\n') {
if (sscanf(buffer, "%19s %d %d %9s",
tempname, &tempid, &tempbalance, tempbranch) != 4) {
fprintf(stderr, "%d: Malformed input data\n", i);
exit(EXIT_FAILURE);
}
strcpy(acc[i].name, tempname);
acc[i].id = tempid;
acc[i].balance = tempbalance;
strcpy(acc[i].branch, tempbranch);
++i;
}
if (fclose(file) != 0) {
fprintf(stderr, "Unable to open file in loadfile()\n");
exit(EXIT_FAILURE);
}
}
return num;
}
int main(void)
{
int num = loadfile();
for (int i = 0; i < num; i++) {
printf("%s %d %d %s\n",
acc[i].name, acc[i].id, acc[i].balance, acc[i].branch);
}
return 0;
}
I can't explain all your problems. It will take me hours. I hope this code will be self-describing. Ask me in a comment if you need some help.
#include <stdlib.h>
#include <stdio.h>
typedef struct {
char name[20];
int id;
int balance;
char branch[10];
} account_t;
static account_t *parse_account_file(FILE *file, size_t *size) {
if (file == NULL || size == NULL) {
return NULL;
}
size_t i = 0;
account_t *account = malloc(sizeof *account);
if (account == NULL) {
return NULL;
}
int ret;
while (
(ret = fscanf(file, "%19s %d %d %9s\n", account[i].name, &account[i].id,
&account[i].balance, account[i].branch)) == 4) {
account_t *old = account;
account = realloc(account, sizeof *account * (++i + 1));
if (account == NULL) {
free(old);
return NULL;
}
}
if (ret == EOF) {
if (ferror(file)) {
perror("parse_account_file()");
} else {
*size = i;
account_t *old = account;
account = realloc(account, sizeof *account * i);
if (account == NULL) {
return old;
}
return account;
}
} else {
fprintf(stderr, "error parsing\n");
}
free(account);
return NULL;
}
int main(void) {
char const *name = "D:\\bank.txt";
FILE *file = stdin;
size_t size;
account_t *account = parse_account_file(file, &size);
fclose(file);
if (account == NULL) {
return 1;
}
for (size_t i = 0; i < size; i++) {
printf("%s %d %d %s\n", account[i].name, account[i].id, account[i].balance,
account[i].branch);
}
free(account);
}
Related
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
int i = 0;
char name[200][100];
char goods[200][100];
char qty[200][100];
char temp[200][100];
int x = 0;
int result;
while (!feof(fp)) {
fscanf(fp, "%[^,] , %[^,] , %s " , name[i], item[i], qty[i]); /*get file content and store in array */
if (strcmp(item[i], "Football") == 0) { /* only select Football */
temp[x][x] = qty[i];
if (x > 0) {
if (strcmp(temp[x][x], temp[x + 1][x + 1]) > 0) { /*compare who has more football qty */
result = x; /*output the person who have more football*/
}
}
x = x + 1;
}
}
printf("%s is team leader in class.\n", name[result]);
fclose(fp);
getchar();
return 0;
}
Hi all, I don't know why the result not correct.
I want to find out who has more football and print out his/her name.
Seems something wrong on if (strcmp(temp[x], temp[x + 1]) > 0)
I am not clearly on using pointer and address.
the content in the text file are:
Alice,Eating,001
Kitty,Football,006
Ben,Swimming,003
May,Football,004
And I expect the result is :
Kitty is team leader in class.
Thank you.
There are multiple problems in your code:
you do not test if the file is properly open.
you cannot properly parse a file with while (!feof(fp)) {. You should iterate for as long as fscanf() returns 3, or preferably read the input line by line and parse it with sscanf().
you do not tell fscanf() the maximum number of characters to store into the destination arrays. This may cause undefined behavior for invalid input.
you do not increment i for each line read. Every line of input overwrites the previous one.
you do not check if there are more than 200 lines. Undefined behavior in this case.
Your test to find the football fan with the highest quantity is broken: no need for a 2D array here, just keep track of the current maximum and update it when needed.
Here is a modified version:
#include <stdio.h>
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
char buf[300];
char name[200][100];
char goods[200][100];
char qty[200][100];
int i, qty, max_qty = 0, result = -1;
if (fp == NULL) {
fprintf(stderr, "cannot open file\n");
return 1;
}
for (i = 0; i < 200; i++) {
if (!fgets(buf, sizeof buf, fp))
break;
if (sscanf(buf, " %99[^,], %99[^,],%99s", name[i], item[i], qty[i]) != 3) {
fprintf(stderr, "invalid input: %s\n", buf);
break;
}
if (strcmp(item[i], "Football") == 0) { /* only select Football */
qty = atoi(qty[i]);
if (result == -1 || qty > max_qty) {
result = i; /*store the index of the person who have more football */
}
}
}
if (result < 0)
printf("no Football fan at all!\n");
else
printf("%s is team leader in class with %d in Football.\n", name[result], max_qty);
fclose(fp);
getchar();
return 0;
}
Above Code is not clear as what you want to do in this code block
if ( strcmp(temp [x], temp [x+1]) > 0 ){ /* when matches, accessing temp[x+1] results in undefined behaviour */
result = x;
}
also why char *temp[200][100]; as to store qty[i], char *temp is enough or you can take char temp[200][100];
Here is somewhat better one as requirement is not clear.
int main() {
FILE *fp= fopen("fileA.txt","r"); /* read file */
if(fp == NULL) {
/* not exist.. write something ?? */
return 0;
}
char name [200][100],goods[200][100],qty[200][100],temp[200][100];
int x = 0,result = 0, i = 0;
while ((fscanf(fp, "%[^,] , %[^,] , %s " , name[i], goods [i], qty[i])) == 3) {
if (strcmp(goods[i] , "Football") == 0){
strcpy(temp[x],qty[i]);
if ( strcmp(temp [x], temp [x+1]) > 0 ) { /* UB ? */
result = x;
x+=1;
}
}
}
printf("%s is team leader in class. \n", name[result]);
fclose(fp);
getchar();
return 0;
}
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);
So I got this file and i want to scanf() only the digits inside the first {} than the digits inside the second {} and so on.
I've managed to call just the digits from the file, but I don't know how to separate them into groups
this is the file:
{5, 2, 3}, {1,5}, { }, { }, {3}, { }, { }
Below is the code I use so far
void main()
{
int rc=0,num,size;
FILE* f = fopen("graph-file.txt","rt");
if (f == NULL)
{
printf("Failed to open the file\n");
}
size = fscanf(f,"%d",&num);
fseek(f,1,SEEK_CUR);
while(rc != EOF)
{
if( rc == 1)
{
printf("%d\n",num);
}
fseek(f,1,SEEK_CUR);
rc = fscanf(f,"%d",&num);
}
}
Your code as it is has some issues, for example the mode string is wrong "rt"? You only need to specify "b" for binary and that only affects the end of line character which can be a problem if the file is read/written on different platforms.
To achieve what you want there is no simple way, as #JonathanLeffler suggests in this comment you could use a json library json-c1 is a very easy to use one.
If you want to do it yourself, try this code that I did just write
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int
append_number(int size, int **arrays, int value)
{
void *pointer;
pointer = realloc(arrays[0], (++size + 1) * sizeof(**arrays));
if (pointer == NULL)
return 0;
arrays[0] = pointer;
arrays[0][size] = value;
return 1;
}
int
get_value(const char *input)
{
if (input == NULL)
return -1;
while ((*input != '\0') && (isspace((unsigned char) *input) != 0))
input++;
if (*input == '\0')
return -1;
return atoi(input);
}
int *
extract_arrays(char *array)
{
int value;
int *list;
list = malloc(sizeof(*list));
if (list == NULL)
return NULL;
list[0] = 0;
while (array != NULL)
{
char *delimiter;
delimiter = strchr(array, ',');
if (delimiter != NULL)
*delimiter = '\0';
value = get_value(array);
if (value > 0)
list[0] += append_number(list[0], &list, value);
if (delimiter != NULL)
array = delimiter + 1;
else
array = NULL;
}
return list;
}
void
print_array(int *list)
{
fprintf(stdout, "[");
for (int j = 1 ; j < list[0] ; ++j)
fprintf(stdout, "%d ", list[j]);
if (list[0] > 0)
fprintf(stdout, "%d", list[list[0]]);
fprintf(stdout, "]\n");
}
int **
parse_line(char *line, size_t *count)
{
char *open;
char *close;
char *next;
int **arrays; // Depends on the maximum size of an inner array
*count = 0;
arrays = NULL;
next = line;
while ((next != NULL) && ((open = strchr(next, '{')) != NULL))
{
close = strchr(open, '}');
if (close != NULL)
{
void *pointer;
char *values;
*close = '\0';
next = strchr(close + 1, ',');
values = open + 1;
pointer = realloc(arrays, (*count + 1) * sizeof(*arrays));
if (pointer == NULL)
goto error;
arrays = pointer;
arrays[(*count)++] = extract_arrays(values);
}
else
next = open + 1;
}
return arrays;
error:
for (size_t i = 0 ; i < *count ; ++i)
free(arrays[i]);
free(arrays);
*count = 0;
return NULL;
}
int main(void)
{
char line[100];
size_t count;
int **arrays;
FILE *file;
file = fopen("graph-file.txt", "r");
if (file == NULL)
return -1; // Failure openning the file
while (fgets(line, sizeof(line), file) != NULL)
{
arrays = parse_line(line, &count);
for (size_t i = 0 ; i < count ; ++i)
{
print_array(arrays[i]);
// DO something with it ...
free(arrays[i]);
}
free(arrays);
}
fclose(file);
return 0;
}
Of course, there are a lot of possible optimizations (specially the realloc() parts), but I leave that to you.
Above, the int ** pointer returned by parse_line() contains count arrays where the first element is the length of each array.
1I know that on most linux distributions it can be installed with the package manager, and I have been using it a lot recently for some web development related projects.
Arrays are probably what you're going to need to accomplish this task. Arrays allow you to have various amounts of digits that you can store what you read into. That's how you can separate the groupings. The next part will be how to change which part of the array you're looking at, which this link should also help with.
C Arrays Tutorial.
If the numbers that you're trying to read will only be single digit numbers, then you could ditch the fseek and fscanf functions and use getc instead. Just check each read for anything that's not a number '0'-'9'.
C getc
Those websites I linked also have a lot of other good tutorials on them for learning C\C++.
Good luck.
Edit: less condescending.
I am novice to C programming and I have written a code to a requirement specification but I am consistently getting Segmentation Fault and unable to proceed ahead.
If the file name is 'code.c' and it runs with an error of not passing the argument (filename). But if the filename is passed, we land in Segmentation Fault.
Any help/suggestions will be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
int ch = 0, lines = 0;
while (!feof(*stream))
{
ch = fgetc(*stream);
if (ch == '\n')
{
lines++;
}
}
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int size)
{
int i;
size_t chrCount;
char *text, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(size, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
rewind(stream);
for (i = 0; i < size; i++)
{
getline(&text, &chrCount, stream);
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char *name, int size, int inputs)
{
int i;
int found = 0;
char *search = " ";
char *firstName;
char *lastName;
if (inputs == 2)
{
firstName = strtok(name, search);
lastName = strtok(NULL, search);
}
printf("*******************************************\n");
if (inputs == 2)
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) && !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
else
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) || !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
if (found == 0)
{
printf("The name was NOT found.\n");
}
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int size)
{
int i;
for (i = 0; i < size; i++)
{
free(BlackBox[i].firstName);
free(BlackBox[i].lastName);
}
free(BlackBox);
BlackBox = NULL;
}
// MAIN
int main(int argv, char **argc)
{
int size;
FILE *stream;
struct _data *BlackBox;
// argv == 1 WORKS, Below message is printed.
if (argv == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argv == 2 DOES NOT WORK, Segmentation Fault.
if (argv == 2)
{
size = SCAN (&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[1], size, 1);
}
if (argv == 3)
{
size = SCAN(&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[2], size, 2);
}
return 0;
}
You have a problem in this code:
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
...
BlackBox[i].number = atol(number);
The second strtok() call should pass NULL as its first argument. As it is, the third strtok() call is certain to return NULL because the first call modifies text in such a way that the second consumes the whole thing (when tokenizing again from the beginning, as it erroneously does). You do not test for that, however, and as a result, atol() attempts to dereference a null pointer.
Update:
Additionally, as #chqrlie and later #JamesWilkins observed, you do not allocate sufficient space for BlackBox[i].firstName and BlackBox[i].lastName, as you need room for the string terminators as well. This is an entirely separate problem that could also produce a segfault. I like #chqrlie's suggestion to switch to strdup(), but it would be sufficient to just increase each allocation by one unit.
Update 2:
Furthermore, you have an issue with this line:
getline(&text, &chrCount, stream);
You do not initialize variable text before the first call, so it contains a junk value. The function allocates a buffer only when its first argument points to a NULL pointer; otherwise it writes the line to the buffer pointed to by the pointer obtained by dereferencing the first argument. Writing to a random location in memory certainly produces undefined behavior, which in practice often manifests as a segfault.
Moreover, unless you can rely on no line of the file being longer than the first, you also need to free the text pointer at the end of each loop iteration AND reset its value to NULL, so that getline() allocates a fresh buffer on the next iteration. If you do not free it on each iteration, then you need instead to free it after the end of the loop; else you will leak memory.
Try this (though I'm using Visual Studio on Windows). I added code to check for a missing '\n' on the last line, and also allowed for a variable number of search terms. I also increased the memory allocation for strings by 1 to account for the null terminating character. I noticed you are using getline(const char*..., which I think is GNU (Linux?), so I change that to fgets() just so I could compile and test it in VS (so you can change it back if you like). I put in various null checks as well, to be safer.
#include <iostream>
using namespace std;
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
if (*stream == NULL)
{
perror("Error opening file");
return 0;
}
char ch = 0, lines = 0, linesize = 0;
while ((ch = fgetc(*stream)) != EOF)
{
if (ch == '\n')
{
lines++;
linesize = 0;
}
else linesize++;
}
if (linesize > 0)
lines++; // (last line doesn't have '\n')
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int lineCount)
{
int i;
size_t chrCount = 256;
char text[256], *result, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(lineCount, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
else memset(BlackBox, 0, sizeof(struct _data) * lineCount); // (make sure all data members are null to begin)
rewind(stream);
for (i = 0; i < lineCount; i++)
{
result = fgets(text, chrCount, stream);
if (result == NULL)
break; // (EOF)
firstName = strtok(text, " ");
lastName = strtok(NULL, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char **names, int lineCount, int inputs)
{
int i, l;
int found = 0;
printf("*******************************************\n");
for (i = 0; i < inputs; ++i)
{
for (l = 0; l < lineCount; ++l)
{
if (BlackBox[l].firstName != NULL && !_stricmp(names[i], BlackBox[l].firstName)
|| BlackBox[l].lastName != NULL && !_stricmp(names[i], BlackBox[l].lastName))
{
printf("The name was found on line %d.\n", 1 + l);
found = 1;
break;
}
}
if (found) break;
}
if (!found)
printf("The name was NOT found.\n");
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int lineCount)
{
int i;
for (i = 0; i < lineCount; i++)
{
if (BlackBox[i].firstName != NULL)
free(BlackBox[i].firstName);
if (BlackBox[i].lastName != NULL)
free(BlackBox[i].lastName);
}
free(BlackBox);
}
// MAIN
int main(int argc, char **argv)
{
int lineCount;
FILE *stream;
struct _data *BlackBox;
// argc == 1 WORKS, Below message is printed.
if (argc == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argc == 2 DOES NOT WORK, Segmentation Fault.
if (argc > 1)
{
lineCount = SCAN(&stream);
if (lineCount > 0)
{
BlackBox = LOAD(stream, lineCount);
SEARCH(BlackBox, argv + 1, lineCount, argc - 1);
FREE(BlackBox, lineCount);
}
}
return 0;
}
Tested it on the command line, and it works.
The problem is the argv and argc. argc is supposed to be an int (think argument count), while argv is meant to be char**. You have them mixed up in your main.
this is an assignment for my CS course,
im trying to write a code that reads a file line by line and put the input into a struct element.the struct looks like this:
typedef char* Name;
struct Room
{
int fStatus;
Name fGuest;
};
the status is 0 for available and 1 for booked. the name will be empty if the room is available.
there are 2 function, one to read and put the values to a struct element, and the other one to print it out.
int openRoomFile()
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
while (getline(&buffer, &length, roomFile) != -1) {
if (count % 2 == 0) {
sscanf(buffer, "%d", &AllRooms[count].fStatus);
} else {
AllRooms[count].fGuest = buffer;
}
count++;
}
fclose(roomFile);
free(buffer);
return 0;
}
print function
void printLayout(const struct Room rooms[])
{
for (int i=0; i<3; i++) {
printf("%3d \t", rooms[i].fStatus);
puts(rooms[i].fGuest);
}
}
the output is not what i expected, given the input file is :
1
Johnson
0
1
Emilda
i will get the output :
1 (null)
0
0 (null)
i dont know what went wrong, am i using the right way to read the file? every code is adapted from different sources on the internet.
Here is a fixed version of the openRoomFile()
int openRoomFile(void)
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
while (1) {
buffer = NULL;
if (getline(&buffer, &length, roomFile) == -1) {
break;
}
sscanf(buffer, "%d", &AllRooms[count].fStatus);
free(buffer);
buffer = NULL;
if (getline(&buffer, &length, roomFile) == -1) {
fprintf(stderr, "syntax error\n");
return 1;
}
AllRooms[count].fGuest = buffer;
count++;
}
fclose(roomFile);
return 0;
}
When you no longer need those fGuest anymore, you should call free on them.
If your input is guaranteed to be valid (as were many of my inputs in my CS classes), I'd use something like this for reading in the file.
while(!feof(ifp)){
fscanf(ifp,"%d%s",&AllRooms[i].fStatus, AllRooms[i].fGuest); //syntax might not be right here
//might need to play with the '&'s
//and maybe make the dots into
//arrows
//do work here
i++;
}
You are not allocating memory for Name. Check this. In the below example i'm not included free() calls to allocated memory. you need to call free from each pointer in AllRooms array, once you feel you are done with those and no more required.
#include<stdio.h>
#include<stdlib.h>
typedef char* Name;
struct Room
{
int fStatus;
Name fGuest;
}Room_t;
struct Room AllRooms[10];
int openRoomFile()
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
size_t itemCount = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
buffer = (char *) malloc(16); // considering name size as 16 bytes
while (getline(&buffer, &length, roomFile) != -1) {
if (count % 2 == 0) {
sscanf(buffer, "%d", &AllRooms[itemCount].fStatus);
} else {
AllRooms[itemCount].fGuest = buffer;
itemCount++;
}
count++;
buffer = (char *) malloc(16); // considering name size as 16 bytes
}
fclose(roomFile);
free(buffer);
return 0;
}
void printLayout(const struct Room rooms[])
{
int i;
for (i=0; i<3; i++) {
printf("%3d \t", rooms[i].fStatus);
puts(rooms[i].fGuest);
}
}
int main(void)
{
openRoomFile();
printLayout(AllRooms);
// free all memory allocated using malloc()
return 0;
}