How to take unknown number of string from file in C? - c

I have a struct storing persons' names, surnames and salaries, but the number of their names is random. For example:
list.txt
John Smith Green 1000 //He has two names
Jennifer Wilson 2000 //She has one name
Steve Adams 1500 //He has one name
James Robert Harris 1250 //He has two names
Robin Walker 1750 //He has one name
I want to store their names in person[count].name, their surnames in person[count].surname and their salaries in person[count].salary.
To do that, I wrote:
fscanf(file, "%s %s %d", person[count].name, person[count].surname, &person[count].salary)
However, problem is that if a person has two names, his second name is stored in person[count].surname, and I cannot take the surname.
How can I take the name of a person with two names in person[count].name?
For this text file:
person[0].name ==> "John Smith"
person[1].name ==> "Jennifer"
person[2].name ==> "Steve"
person[3].name ==> "James Robert"
person[4].name ==> "Robin"

I tried reading the file line by line, then separating it into tokens (words). I am assuming each line contains at max 10 tokens (words), and the last token is salary, the one before the last is surname and first N-2 tokens are the names of the person. So, each person could have surnames with only one word, I am assuming. Here is the code, note that I did not pay attention to memory leaks or dangling pointers etc.
I edited the solution according to the suggestions from #
chux - Reinstate Monica
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person
{
char *name, *surname;
int salary;
} Person;
int main()
{
Person persons[10];
FILE *file = fopen("text.txt", "r");
if (file == NULL)
{
printf("Error opening file!\n");
exit(1);
}
// read file line by line
char line[256];
int person_count = 0;
while (fgets(line, sizeof(line), file) != NULL)
{
char *tokens[10];
int i = 0;
tokens[0] = strtok(line, " ");
while (tokens[i] != NULL && i < 9)
{
tokens[++i] = strtok(NULL, " ");
}
char name[sizeof line];
strcpy(name, tokens[0]);
for (int j = 1; j < i - 2; j++)
{
strcat(name, " ");
strcat(name, tokens[j]);
}
persons[person_count].name = strdup(name);
persons[person_count].surname = strdup(tokens[i - 2]);
persons[person_count].salary = atoi(tokens[i - 1]);
person_count++;
}
for (int i = 0; i < person_count; i++)
{
printf("%s %s %d\n", persons[i].name, persons[i].surname, persons[i].salary);
}
fclose(file);
}

You cannot use fscanf() for this problem. Here is a simple approach reading one line at a time and parsing it explicitly with strrchr():
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *name, *surname;
int salary;
};
int main() {
const char *filename = "list.txt";
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
// read file line by line
char line[256];
struct Person *persons = NULL;
int count = 0;
while (fgets(line, sizeof line, file) != NULL) {
char *last = strrchr(line, ' ');
if (!last) // invalid format
continue;
*last++ = '\0';
int salary;
if (sscanf(last, "%d", &salary) != 1)
continue;
const char *name = line;
char *surname = strrchr(line, ' ');
if (surname) {
*surname++ = '\0';
} else {
name = ""; // handle Superman: no first name
surname = line;
}
persons = realloc(persons, (count + 1) * sizeof(*persons));
if (persons == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
persons[count].name = strdup(name);
persons[count].surname = strdup(lastname);
persons[count].salary = salary;
count++;
}
fclose(file);
// dump the database
for (int i = 0; i < count; i++) {
printf("%s %s %d\n", persons[i].name, persons[i].surname, persons[i].salary);
}
// free the database
for (int i = 0; i < count; i++) {
free(persons[i].name);
free(persons[i].surname);
}
free(persons);
return 0;
}

Related

Implementation of Dynamic memory allocation in files in C program

I want the program to read the details of n employees from the file and store the Salary details of n employees to an array which is dynamically allocated and then print the average salary.
The below program gets the data from the user and stores it in a file.
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
char name[10];
char nameEr[10];
int salaryannual;
char desig[10];
}Employee;
void main(int argc, char *argv[])
{
int i,n=0,*ptr,sum=0;
n=atoi(argv[1]);
printf("The no.of employee records to be created are: %d\n",n);
FILE *fp;
if((fp=fopen("employee.txt","w"))==NULL)
{
printf("File could not be opened\n");
}
Employee employees[n];
printf("Enter %d Employee Details: \n \n",n);
for(i=0; i<n; i++)
{
printf("Employee %d: \n",i+1);
printf("Employee name: ");
scanf("%s",employees[i].name);
printf("Employer name: ");
scanf("%s",employees[i].nameEr);
printf("Employee designation: ");
scanf("%s",employees[i].desig);
printf("Employee annual salary: ");
scanf("%d",&employees[i].salaryannual);
printf("\n");
}
for(i=0;i<n;i++)
{
fprintf(fp,"%s\n",employees[i].name);
fprintf(fp,"%s\n",employees[i].nameEr);
fprintf(fp,"%s\n",employees[i].desig);
fprintf(fp,"%d\n",employees[i].salaryannual);
}
fclose(fp);
for(i=0;i<n;i++)
{
printf("Employee name \t: ");
printf("%s \n",employees[i].name);
printf("Employer name \t: ");
printf("%s \n",employees[i].nameEr);
printf("Employee designation \t: ");
printf("%s \n",employees[i].desig);
printf("Employee annual salary: ");
printf("%d \n",employees[i].salaryannual);
printf("\n");
}
How do I proceed further from this code.
Please help me.
There are certainly many approaches to solve this.
I think of one of the simplest is:
Get the employee count by opening the file for reading and counting the number of lines. The employee count will be the number of lines divided by four, since there are four lines for each employee. You typically make a function to do so:
int get_number_of_employees(int *number_of_employees)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
}
*number_of_employees = line_count / 4;
ret = 0;
fclose(fp);
}
return ret;
}
Allocate memory for your buffer using the number of employees you got with the function above:
int salaries_array[employees_count];
Create a function fo fill in the array you just created. This will require the program to open the file for reading again:
int get_employees_salaries(int salaries_array[], int employees_count)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
if ((line_count % 4) == 0) {
int salary = atoi(line);
salaries_array[(line_count / 4) - 1] = salary;
ret = 0;
}
}
fclose(fp);
}
return ret;
}
Now, create a function to get the average salary from the array:
int get_average_salary(int salaries_array[], int employees_count)
{
int salaries_sum = 0;
int i = 0;
for (; i < employees_count; i++) {
salaries_sum += salaries_array[i];
}
return salaries_sum / employees_count;
}
Putting it all together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char name[10];
char nameEr[10];
int salaryannual;
char desig[10];
} Employee;
int get_number_of_employees(int *number_of_employees)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
}
*number_of_employees = line_count / 4;
ret = 0;
fclose(fp);
}
return ret;
}
int get_employees_salaries(int salaries_array[], int employees_count)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
if ((line_count % 4) == 0) {
int salary = atoi(line);
salaries_array[(line_count / 4) - 1] = salary;
ret = 0;
}
}
fclose(fp);
}
return ret;
}
int get_average_salary(int salaries_array[], int employees_count)
{
int salaries_sum = 0;
int i = 0;
for (; i < employees_count; i++) {
salaries_sum += salaries_array[i];
}
return salaries_sum / employees_count;
}
void main (int argc, char *argv[])
{
int employees_count = 0;
if (get_number_of_employees(&employees_count) == 0) {
int salaries_array[employees_count];
if (get_employees_salaries(salaries_array, employees_count) == 0) {
int average_salary = get_average_salary(salaries_array, employees_count);
printf("Average salary = %d\n", average_salary);
}
}
}
In order to dynamically allocate, you first need to decide on the allocation scheme. Here are a couple of schemes:
increment by a fixed number every time
increment by a fixed percentage
Unlike #jardellucca's method, even though the file is not read twice, these schemes require a lot of reallocation.
/* Increment by fixed number */
int increment = 5;
int employee_count = 0;
int employee_max = increment;
Employee* employees = malloc(sizeof(Employee) * employee_max);
...
void Save(Employee* emp)
{
/* check if there is enough space */
if (employee_count == employee_max)
{
employee_max += increment;
employees = realloc(employees, sizeof(Employee) * employee_max)
}
employees[employee_count] = *emp;
++employee_count;
}
If you have 1000 records, this method reallocs 200 times. An alternative is to increase by a percentage
int increment_pc = 150; /* 50% */
int employee_count = 0;
int employee_max = 10;
Employee* employees = malloc(sizeof(Employee) * employee_max);
...
void Save(Employee* emp)
{
/* check if there is enough space */
if (employee_count == employee_max)
{
employee_max = (employee_max * increment_pc) / 100;
employees = realloc(employees, sizeof(Employee) * employee_max)
}
employees[employee_count] = *emp;
++employee_count;
}
Using this allocation scheme, 1000 employees takes 13 reallocations but there is some wastage. The last allocation will allocate 1234, which is 234 over. In the previous scheme, it will, at most be 4 over. This is just a space/time problem.
You can waste time reading the file twice. You could, for instance be reading the file across a slow network.
You can waste time reallocating lots of times and have a few left over
You can waste space reallocating fewer times. You may be on a system that is tight on memory so a scheme like this may not be feasible.
The scheme you choose will be dependant upon the environment and the constraints. As for average salary, you can do sum up on the fly as the records are being saved and then just compute the average at the end.

Reading a file in a table format and print it

So, what I'm trying to do is, at first create a file in a table format then read that file and put that file in 4 different dynamic arrays using struct and print them in order
So, here is the struct I'm using: I used capacity = 5 to change the size of dynamic array later but still don't know how to change it
struct score{
char *str1;
char *str2;
int *num1;
int *num2;
};
int main(){
int capacity = 5;
struct score table;
table.str1=(char *)malloc(sizeof(int)*capacity);
table.str2=(char *)malloc(sizeof(int)*capacity);
table.num1=(int *)malloc(sizeof(int)*capacity);
table.num2=(int *)malloc(sizeof(int)*capacity);
After I created a File to write:
inFile = fopen("Subject.txt", "w");
if(inFile == NULL)
{
printf("Error!");
exit(1);
}
char names[] = {"Joe Math 52 85\nBilly Phy 65 70\nSophia Chem 86 71"};
fprintf(inFile,"%s",names);
fclose(inFile);
At the end reading a File and putting them in arrays:
inFile = fopen("Subject.txt", "r");
if(inFile == NULL)
{
printf("Error!");
exit(1);
}
fscanf(inFile, "%s %s %d %d",table.str1, table.str2, table.num1, table.num2);
fclose(inFile);
for(i=0;i<6;i++){
printf("%s %s %d %d\n",table.str1, table.str2, table.num1, table.num2);
}
So, I need this code to print like this, and I couldn't do it:
Name Subj. Test1 Test2
------------------------
Joe Math 52 85
Billy Phy 65 70
Sophia Chem 86 71
Here is my full Code just in case:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct score{
char *str1;
char *str2;
int *num1;
int *num2;
};
int main(){
FILE *inFile;
int num, capacity = 5, i;
struct score table;
table.str1=(char *)malloc(sizeof(int)*capacity);
table.str2=(char *)malloc(sizeof(int)*capacity);
table.num1=(int *)malloc(sizeof(int)*capacity);
table.num2=(int *)malloc(sizeof(int)*capacity);
inFile = fopen("Subject.txt", "w");
if(inFile == NULL)
{
printf("Error!");
exit(1);
}
char names[] = {"Joe Math 52 85\nBilly Phy 65 70\nSophia Chem 86 71"};
fprintf(inFile,"%s",names);
fclose(inFile);
inFile = fopen("Subject.txt", "r");
if(inFile == NULL)
{
printf("Error!");
exit(1);
}
fscanf(inFile, "%s %s %d %d",table.str1, table.str2, table.num1, table.num2);
fclose(inFile);
for(i=0;i<6;i++){
printf("%s %s %d %d\n",table.str1, table.str2, table.num1, table.num2);
}
}
You want an array of structs.
But, you've got a fixed number of scores (e.g. num1, num2). This, also, should be an array.
I think you'll be better off with a second struct. That is, struct student and struct score
And, Paul outlined how to do this with fixed limits.
In general, you could allocate things dynamically to accomodate an arbitrary number of students that have an arbitrary (and varying) number of test scores.
Based on your sample data, your input format is:
<name> <subject> <score1> <score2> ... <scoreN>
Because of this, I interpret the subject to be the students major, so it gets grouped in the student record.
Otherwise, we'd need something like:
<name> <subject1> <score1> <subject2> <score2> ... <subjectN> <scoreN>
And, then, the subject would go into the score record
To get it to work, I had to [heavily] refactor your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct score {
int score;
};
struct student {
char *name;
char *subject;
struct score *scores;
int count;
};
int
main(void)
{
FILE *inFile;
struct student *students = NULL;
int student_count = 0;
struct student *who;
struct score *score;
const char *delim = " \t\n";
char *cp;
char buf[1000];
inFile = fopen("Subject.txt", "w");
if (inFile == NULL) {
printf("Error!");
exit(1);
}
char names[] = { "Joe Math 52 85\nBilly Phy 65 70\nSophia Chem 86 71" };
fprintf(inFile, "%s", names);
fclose(inFile);
inFile = fopen("Subject.txt", "r");
if (inFile == NULL) {
printf("Error!");
exit(1);
}
while (1) {
// get a line
cp = fgets(buf,sizeof(buf),inFile);
if (cp == NULL)
break;
// get the student name
cp = strtok(buf,delim);
if (cp == NULL)
continue;
// allocate new student record
students = realloc(students,
sizeof(struct student) * (student_count + 1));
who = &students[student_count];
student_count += 1;
// save the student name
who->name = strdup(cp);
// get the subject and save it
cp = strtok(NULL,delim);
if (cp == NULL)
break;
who->subject = strdup(cp);
// clear out the scores array
who->count = 0;
who->scores = NULL;
// get all scores
while (1) {
cp = strtok(NULL,delim);
if (cp == NULL)
break;
// increase the size of the scores array for this student
who->scores = realloc(who->scores,
sizeof(struct score) * (who->count + 1));
score = &who->scores[who->count];
who->count += 1;
score->score = atoi(cp);
}
}
fclose(inFile);
for (who = &students[0]; who < &students[student_count]; ++who) {
printf("%10s %10s", who->name, who->subject);
for (score = who->scores; score < &who->scores[who->count]; ++score)
printf("%4d",score->score);
printf("\n");
}
return 0;
}
Here's the program output:
Joe Math 52 85
Billy Phy 65 70
Sophia Chem 86 71
I think you want an array of structs with one array element for each student. Now you are using one struct to hold arrays of the individual pieces of information.
The array would look like:
#define MAX_NAME 30
#define MAX_STUDENTS 10
struct score{
char str1[MAX_NAME];
char str2[MAX_NAME];
int num1;
int num2;
};
struct score table[MAX_STUDENTS];
With this definition you don't need to malloc memory (it is simpler, but not flexible).
You read the information into the array as:
int i=0;
while (fscanf(inFile, "%30s %30s %d %d",table[i].str1, table[i].str2,
&table[i].num1, &table[i].num2) == 4) {
i++;
}

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);

Problems with Implementing Mutex in C

I am implementing a program to read filenames with room names, connections, and room types. Ex:
ROOM NAME: chicago
CONNECTION 1: sarasota
CONNECTION 2: columbus
CONNECTION 3: miami
CONNECTION 4: boston
ROOM TYPE: END_ROOM
The program is designed to show the user the room they are starting in, ask for input from the user, check to see if the input is the end room or another connection. If it is another connection, the prompt will display again. If the user reaches the end room, the game ends. However, I am required to implement a mutex that, if the user enters "time", a file is created, the time is written to it, and it is displayed on the screen. After that, prompt is displayed again for the user. My code works fine when the mutex implementation is stripped out. Here is what I am seeing when the mutex is in the code. I appear to reach the time function and the program seems to recognize incorrect rooms but when a "correct" room is entered the cursor just returns and does nothing. Any clue on why I am getting this behavior only on mutex implementation?
The program is below, do you see anything that would cause this issue?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
//Read in the room data
//Perform the stat() function on the rooms directory in the same directory
//and open the file with the most recent st_mtime component of the returned stat struct
#define NUM_USED_ROOMS 7
char usedRooms[NUM_USED_ROOMS][256];
char roomFilePath[75];
char timeFilePath[75];
char* connections[NUM_USED_ROOMS];
int end = 0;
char input[20];
int numberOfSteps = -1;
char *steps[75];
int file_descriptor;
char timeText[100];
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
void * getTime() {
pthread_mutex_lock(&myMutex);
printf("You asked for the time!\n");
pthread_mutex_unlock(&myMutex);
return NULL;
}
//Check if the room number passed is the end
void isEnd(int roomNumber, char *dirName){
//Counting the number of steps for the end of the program
numberOfSteps++;
steps[numberOfSteps - 1] = usedRooms[roomNumber];
//Getting the name of the proper file
sprintf(roomFilePath, "%s/%s", dirName, usedRooms[roomNumber]);
char substring[20];
int numLine = 1;
FILE * filePointer;
filePointer = fopen(roomFilePath, "r");
int lines = 0;
char buffer[256];
while(fgets(buffer, sizeof(buffer), filePointer) != NULL){
lines = lines + 1;
}
fclose(filePointer);
//Opening the file to read to see if it is the end. If it is, assign end = 1.
filePointer = fopen(roomFilePath, "r");
while(fgets(buffer, sizeof(buffer), filePointer) != NULL) {
if (numLine == lines)
{
strcpy(substring, buffer+11);
}
numLine = numLine + 1;
}
if(strstr(substring, "END" ) != NULL) {
end = 1;
}
}
//Get the user input
void getInput() {
fflush(stdin);
scanf("%s", input);
fflush(stdin);
fflush(stdout);
}
void readFile(char *dirName){
DIR *dir;
struct dirent *ent;
int i = 0;
if ((dir = opendir (dirName)) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
if (strncmp(ent->d_name,".",sizeof(ent->d_name)) == 0 ||
strncmp(ent->d_name,"..",sizeof(ent->d_name)) == 0 )
{
} else {
strcpy(usedRooms[i],ent->d_name);
i++;
}
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
}
}
void playGame(int roomNumber, char * dirName){
int i;
printf("usedRooms is %s", usedRooms[roomNumber]);
pthread_mutex_lock(&myMutex);
pthread_t secondThread;
pthread_create(&secondThread, NULL, getTime,NULL);
//Check if the user guessed the end room
if(end == 1) {
fflush(stdout);
return;
}
else{
isEnd(roomNumber, dirName);
if (end == 1)
{
fflush(stdout);
return;
}
}
int move = 1;
while(move == 1) {
//Open the file of the path of the room passed in
sprintf(roomFilePath, "%s/%s", dirName, usedRooms[roomNumber]);
FILE * filePointer;
filePointer = fopen(roomFilePath, "r");
int fileLines = 0;
char line[256];
//Count the lines in the file so I know how to traverse it
while(fgets(line, sizeof line, filePointer) != NULL) {
fileLines = fileLines + 1;
}
fclose(filePointer);
filePointer = fopen(roomFilePath, "r");
int currentLine = 0;
//Create the array for the rooms that can be navigated to
char gameRooms[6][30];
while(fgets(line, sizeof line, filePointer) != NULL) {
char *pos;
if((pos = strchr(line, '\n')) != NULL)
{
*pos = '\0';
}
//Print out the current room
if (currentLine == 0)
{
char substring[20];
strcpy(substring, line+11);
printf("CURRENT LOCATION: %s\n", substring);
}
//Print the first connection from this room
else if (currentLine == 1){
printf("POSSIBLE CONNECTIONS: ");
fflush(stdout);
char substring[20];
strcpy(substring, line+14);
printf("%s", substring);
fflush(stdout);
strcpy(gameRooms[currentLine - 1], substring);
}
//Print the rest of the connections, comma separated
else if (currentLine > 1 && currentLine < fileLines - 1) {
printf(",");
fflush(stdout);
char substring[20];
strcpy(substring, line+14);
printf("%s", substring);
fflush(stdout);
strcpy(gameRooms[currentLine - 1], substring);
//gameRooms[currentLine - 1] = substring;
}
else {
printf(".");
fflush(stdout);
}
currentLine = currentLine + 1;
}
fclose(filePointer);
printf("\nWHERE TO?>");
//Get the user input
getInput();
if(strcmp("time", input) == 0){
pthread_mutex_unlock(&myMutex);
pthread_join(secondThread, NULL);
pthread_mutex_lock(&myMutex);
pthread_create(&secondThread, NULL, getTime, NULL);
}
//Loop through the file to see if the input matches a room name in the array
for(i = 0; i < fileLines - 2; i++) {
if (strcmp(gameRooms[i], input) == 0)
{
int j;
for(j = 0; j < NUM_USED_ROOMS; j++) {
//If there is a match, play the game starting at the room entered
if(strcmp(usedRooms[j], input) == 0) {
printf("THE STRINGS MATCH usedRooms is %s "
"and input is %s\n",usedRooms[j],input);
playGame(j,dirName);
}
}
move = 0;
}
}
//If the user's input didn't match the list of rooms
if (move == 1) {
printf("\nHUH? I DON'T UNDERSTAND THAT ROOM. TRY AGAIN. \n\n");
fflush(stdout);
fflush(stdin);
}
}
}
int main() {
int newestDirTime = -1; // Modified timestamp of newest subdir examined
char targetDirPrefix[32] = "walterer.rooms."; // Prefix we're looking for
char newestDirName[256]; // Holds the name of the newest dir that contains prefix
memset(newestDirName, '\0', sizeof(newestDirName));
DIR* dirToCheck; // Holds the directory we're starting in
struct dirent *fileInDir; // Holds the current subdir of the starting dir
struct stat dirAttributes; // Holds information we've gained about subdir
dirToCheck = opendir("."); // Open up the directory this program was run in
if (dirToCheck > 0) // Make sure the current directory could be opened
{
while ((fileInDir = readdir(dirToCheck)) != NULL) // Check each entry in dir
{
if (strstr(fileInDir->d_name, targetDirPrefix) != NULL) // If entry has prefix
{
//printf("Found the prefex: %s\n", fileInDir->d_name);
stat(fileInDir->d_name, &dirAttributes); // Get attributes of the entry
if ((int)dirAttributes.st_mtime > newestDirTime) // If this time is bigger
{
newestDirTime = (int)dirAttributes.st_mtime;
memset(newestDirName, '\0', sizeof(newestDirName));
strcpy(newestDirName, fileInDir->d_name);
}
}
}
}
closedir(dirToCheck);
//Read the file at the specified directory
readFile(newestDirName);
int start;
int i;
for (i = 0; i < NUM_USED_ROOMS; i++)
{
memset(roomFilePath, '\0', sizeof(roomFilePath));
sprintf(roomFilePath,"%s/%s", newestDirName, usedRooms[i]);
char output[256];
memset(output, '\0', sizeof(output));
char* token;
char* connectRoom;
FILE *filePointer;
filePointer = fopen(roomFilePath,"r");
//Find the starting room and pass that into the playGame function
if (filePointer == NULL)
{
printf("Unable to open file!\n");
} else {
while(!feof(filePointer)) {
fgets(output, 256, filePointer);
token = strtok(output, "\n");
if(strstr(token, "START") != NULL){
start = i;
}
}
fclose(filePointer);
}
}
//Play the game with the starting room at the directory name
playGame(start, newestDirName);
printf("YOU HAVE FOUND THE END ROOM. CONGRATULATIONS!\n");
printf("YOU TOOK %d STEPS. YOUR PATH TO VICTORY WAS: \n", numberOfSteps);
for(i = 0; i < numberOfSteps; i++){
printf("%s\n", steps[i]);
}
return 0;
}
As a rule, you should lock a mutex for the shortest time possible. Violation of this rule is a severe anti-pattern called "asking for trouble" which is usually punished with deadlocks. Remove that anti-pattern from your code.

Saving a string from a text file to a struct using fscanf (C)

Sample Text file:
234765 PETER
867574 SMITH
I'm trying to take the id and string from the text file and save it into a struct. The id is saving fine but the string isn't.
typedef struct student
{
int id[DATA_SIZE];
char *student[DATA_SIZE];
}studentinfo;
studentinfo list;
struct student *create_space(int size)
{
struct student *tmp = (struct student*)malloc(size*sizeof(struct student));
return(tmp);
}
struct student * readData(struct student*pointer,studentinfo v)
{
int count =0;
int tmpid;
char str[256];
FILE* in_file;
in_file = fopen("studentlist.txt","r");
while(fscanf(in_file,"%d",&tmpid)!= EOF && count<DATA_SIZE)
{
fscanf(in_file,"%s",v.student[count]);
//printf("%s\n",str );
v.id[count]=tmpid;
count++;
}
pointer =&v;
return pointer;
}
int main()
{
struct student *data;
struct student *sdata;
data = create_space(1);
sdata = readData(data,list);
//printf("%s\n",sdata->student[2] );
}
Their are a couple of issues:
fscanf() reads formatted input, and returns the number of items read.
This line:
while(fscanf(in_file,"%d",&tmpid)!= EOF && count<DATA_SIZE)
Could be this:
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
Which verifies that 2 values are being read on each line successfully.
You are not checking if in_file returns NULL. It's safe to do this. This goes the same for malloc().
You need to correctly create space for char *students[DATA_SIZE], as this is an array of char * pointers. Once you allocate space for this via malloc() or strdup(), then you can copy the contents into students.
Here is an example of doing such a thing:
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
/* allocate space for one student */
list.student[count] = malloc(strlen(str)+1);
if (!list.student[count]) {
printf("Cannot allocate string\n");
exit(EXIT_FAILURE);
}
/* copy it into array */
strcpy(list.student[count], str);
count++;
}
Here is an example that you can use to help achieve your desired result:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DATA_SIZE 256
typedef struct {
int id[DATA_SIZE];
char *student[DATA_SIZE];
} studentinfo_t;
int main(void) {
FILE *in_file;
studentinfo_t list;
char str[DATA_SIZE];
size_t count = 0;
in_file = fopen("studentlist.txt", "r");
if (!in_file) {
fprintf(stderr, "%s\n", "Error reading file");
exit(EXIT_FAILURE);
}
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
list.student[count] = malloc(strlen(str)+1);
if (!list.student[count]) {
printf("Cannot allocate string\n");
exit(EXIT_FAILURE);
}
strcpy(list.student[count], str);
count++;
}
for (size_t i = 0; i < count; i++) {
printf("%d %s\n", list.id[i], list.student[i]);
}
return 0;
}

Resources