using strtok to get input from file - c

I am trying to create a program that takes in a number of processes (name, start time, remaining time) from a file, then uses a round robin algorithm to handle the queue.
The issue is, when I try to tokenize each line of the file by using strtok() and fgets(), the name of the process is always wrong.
For example, if the first line is P1 0 3 the output is like this:
void RoundRobin(char *filename) {
Queue *q = initQueue();
char string[MAX_SIZE];
FILE *file;
Process process[20];
char *token;
file = fopen(filename, "r");
if (!file) {
printf("File Cannot Be Opened");
}
fgets(string, 150, file);
token = strtok(string, "=");
token = strtok(NULL, "+");
int time_quantum = atoi(token);
int process_count = 0;
while (fgets(string, 150, file)) {
char *token1;
token1 = strtok(string, " ");
process[process_count].name = token1;
token1 = strtok(NULL, " ");
process[process_count].starting_time = atoi(token1);
token1 = strtok(NULL, " ");
process[process_count++].remaining_time = atoi(token1);
token1 = strtok(NULL, " ");
}
for (int i = 0; i < process_count; i++) {
printf("%s %d %d\n", process[i].name, process[i].starting_time, process[i].remaining_time);
}
fclose(file);
}

You are reusing a single char[] for all of your token parsing. fgets() will overwrite the contents of that char[] each time, and strtok() will return pointers to memory inside of that char[]. Thus, each time you read a new line from the file, the previous pointers you already stored in the process[] array are still pointing at the same memory, but the contents of that memory have been altered.
You need to instead allocate a separate char[] string for each name that you want to save in the process[] array. You can use strdup() for that, eg:
while (fgets(string, 150, file)){
char* token1 token1 = strtok(string, " ");
process[process_count].name = strdup(token1); // <-- HERE
...
}
// use process[] as needed...
for(int i = 0; i < process_count; i++){
free(process[i].name);
}

The problem is strtok() returns a pointer into the line that it parses. Hence all entries in the process array point to the same string array that is modified by the call to fgets().
You must duplicate the string you store in the process description structure:
void RoundRobin(const char *filename) {
char string[MAX_SIZE];
Process process[20];
Queue *q = initQueue();
char *token;
FILE *file;
file = fopen(filename, "r");
if (!file) {
printf("Cannot open file %s\n", filename);
return;
}
int time_quantum = 0;
int process_count = 0;
if (fgets(string, sizeof string, file)
&& (token = strtok(string, "=")) != NULL
&& (token = strtok(NULL, "+")) != NULL) {
time_quantum = atoi(token);
}
while (fgets(string, sizeof string, file)) {
char *token1;
if ((token1 = strtok(string, " ")) == NULL)
contine;
process[process_count].name = strdup(token1);
if ((token1 = strtok(string, " ")) == NULL)
contine;
process[process_count].starting_time = atoi(token1);
if ((token1 = strtok(string, " ")) == NULL)
contine;
process[process_count].remaining_time = atoi(token1);
process_count++;
}
for (int i = 0; i < process_count; i++) {
printf("%s %d %d\n", process[i].name, process[i].starting_time, process[i].remaining_time);
}
for (int i = 0; i < process_count; i++) {
free(process[i].name);
}
fclose(file);
}

Related

How to read from the file and write it in the structure? I have a little trouble with my code

I have to write this code, I mean I should read from the file name of students and their mark, and then sort students by the grow of mark. Now I just want to output only mark. I want to display grades using structures. I don't know where the problem is.
text.file
Jon 3
Alina 5
Ron 1
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
int main()
{
const int N = 3;
int i = 0;
struct student {
char surname[50];
int mark;
};
struct student PI1[N];
char str[50];
const char s[1] = " ";
char* token;
FILE* ptr;
token = strtok(str, s);
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
printf("file can't be opened \n");
}
while (fgets(str, 50, ptr) != NULL){
token = strtok(str, s);
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
PI1[i].mark = atoi(token);
i++;
}
fclose(ptr);
printf("The marks is:\n");
printf("%d %d %d", PI1[0].mark, PI1[1].mark, PI1[2].mark);
return 0;
}
You need to prevent the program from reading from the file pointer if opening the file fails:
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
perror("test.txt");
return 1; // this could be one way
}
The second argument to strok should be a null terminated string. const char s[1] = " "; only has room for one character. No null terminator (\0). Make it:
const char s[] = " "; // or const char s[2] = " "; or const char *s = " ";
Don't iterate out of bounds. You need to check so that you don't try to put data in PI1[N] etc.
while (i < N && fgets(str, sizeof str, ptr) != NULL) {
// ^^^^^^^^
Check that strok actually returns a pointer to a new token. If it doesn't, the line you've read doesn't fulfill the requirements.
while (i < N && fgets(str, sizeof str, ptr) != NULL) {
token = strtok(str, s);
if(!token) break; // token check
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
if (token) // token check
PI1[i].mark = atoi(token);
else
break;
i++;
}
You could also skip the strcpy by reading directly into your struct student since char str[50]; has the same length as surname. str should probably be larger though, but for now:
while (i < N && fgets(PI1[i].surname, sizeof PI1[i].surname, ptr) != NULL) {
token = strtok(PI1[i].surname, s);
if(!token) break;
token = strtok(NULL, s);
if (token)
PI1[i].mark = atoi(token);
else
break;
i++;
}
Only print as many marks as you successfully read
printf("The marks are:\n");
for(int idx = 0; idx < i; ++idx) {
printf("%d ", PI1[idx].mark);
}
putchar('\n');

Reading a csv file to a struct in C

The csv files will all have the following format:
Number of Spaces,10,,,,,,,,
,,,,,,,,,
,,,,,,,,,
Type,Set Id,Intraset Id,Name,Property Cost,House Cost,Hotel Cost,Rent,Rent with House,Rent With Hotel
Go,400,MU,,,,,,,
Property,0,0,0A,500,50,50,5,50,2000
Property,0,1,0B,1000,50,50,10,75,2500
Property,1,0,1A,2000,200,200,20,100,3000
Property,1,1,1B,2500,200,200,25,150,3000
Property,1,2,1C,3000,200,200,30,200,3500
Property,2,0,2A,4000,400,400,40,300,4000
Property,2,1,2B,4500,400,400,45,400,4000
Property,2,2,2C,5000,400,400,50,500,4000
Property,2,3,2D,5500,400,400,55,600,4500
The fourth line describes what each field in the lines below it are i.e. for line 5, type is property, set id is 0, name is 0A, etc. I have a struct Space that contains variables for all this information. The 5th line is special: it has type Go, get $400 for passing Go, name is MU, and none of the other fields apply. (This is a version of Monopoly).
Where I'm struggling is how to get the values that I need. So far I have only managed to get the number of spaces value (this determines the number of rows on the board) with this:
void openSpecs(char *fileName) {
FILE* file = fopen(fileName, "r");
if (file == NULL) {
printf("Could not open %s\n", fileName);
}
char c;
do {
fscanf(file, "%c", &c);
//printf("%c", c);
} while (!feof(file) && c != ',');
//printf("\n\n");
int numSpaces;
fscanf(file, "%d", &numSpaces);
//printf("there are %d spaces\n", numSpaces);
// note: the printf statements are there to help me see where I'm at in the file
fclose(file);
}
I'm conflicted on how to approach the rest of the file. I'm thinking of using a while loop to just skip the rest of the commas, and then just reading through line 4, as I don't need to save any of that. From there, I'm not sure what to do. If I use strtok, I need to have a line from the file already as a C string, correct? I can't statically allocate a C string and then use fscanf (no static allocation allowed), so how do I dynamically allocate for a string whose length is unknown?
Edit:
char str[4096];
fgets(str, 4096, file);
printf("%s\n", str);
int goCash = 0;
char* name = NULL;
char delim[2] = ",";
char* token;
token = strtok(str, delim); // this is Go
token = strtok(str, delim);
goCash = (int) token;
token = strtok(str, delim);
strcpy(name, token);
printf("you get %d for going past %s\n", goCash, name);
Be careful as strtok could run in to problems.
For example, consider the the following lines:
Property,0,0,0A,500,50,50,5,50,2000
Property,,0,0A,500,50,50,5,50,2000
Note that in the second line, the second field is missing and you have two consecutive delimiters: ",,". strtok doesn't give you any indication that there was a field missing, it just skips to the next available field.
You can fix this by replacing the occurrences of ,, with , ,
Another issue is that fgets includes the end of line character and you want to remove that.
enum { Type, SetId, IntrasetId, Name, PropertyCost, HouseCost, HotelCost,
Rent, RentwithHouse, RentWithHotel, total };
FILE *fp = fopen("test.txt", "r");
char buf[1000], temp[1000];
while(fgets(temp, sizeof(temp), fp))
{
//remove end of line
temp[strcspn(temp, "\r\n")] = 0;
//replace ",," with ", ,"
int j = 0;
for(int i = 0, len = strlen(temp); i < len; i++)
{
buf[j++] = temp[i];
if (temp[i]==',' && temp[i+1]==',')
buf[j++] = '0';
}
buf[j] = 0;
//read all the fields
char field[total][100];
for(int i = 0; i < total; i++) *field[i] = 0;
int i = 0;
char *ptr = strtok(buf, ",");
while(ptr)
{
strcpy(field[i++], ptr);
ptr = strtok(NULL, ",");
}
for(int i = 0; i < total; i++)
printf("%s, ", field[i]);
printf(" rent(%d)\n", atoi(field[RentWithHotel]));
}
fclose(fp);

C parsing input text file into words

I am trying to parse input file (containing a text document with multiple lines and delimiters, i.e. "!,.?") into words. My function 'splitting function' is:
int splitInput(fp) {
int i= 0;
char line[255];
char *array[5000];
int x;
while (fgets(line, sizeof(line), fp) != NULL) {
array[i] = strtok(line, ",.!? \n");
printf("Check print - word %i:%s:\n",i, array[i]);
i++;
}
return 0;
}
Here's the corrected function [sorry for extra the style cleanup]:
int
splitInput(fp)
{
int i = 0;
char *cp;
char *bp;
char line[255];
char *array[5000];
int x;
while (fgets(line, sizeof(line), fp) != NULL) {
bp = line;
while (1) {
cp = strtok(bp, ",.!? \n");
bp = NULL;
if (cp == NULL)
break;
array[i++] = cp;
printf("Check print - word %i:%s:\n",i-1, cp);
}
}
return 0;
}
Now, take a look at the man page for strtok to understand the bp trick
If I understand your question correctly you want to read every line and split each line into words and add that into an array.
array[i] = strtok(line, ",.!? \n");
That will not work for obvious reasons because it will only return the first word for each line and you never allocate memory.
This is probably what you want.
char *pch;
pch = strtok(line, ",.!? \n");
while(pch != NULL) {
array[i++] = strdup(pch); // put the content of pch into array at position i and increment i afterwards.
pch = strtok(NULL, ",.!? \n"); // look for remaining words at the same line
}
Don't forget to free your array elements afterwards though using free.

Breaking down a string and putting it into array using strtok()

I'm writing a basic program that takes a CSV file, prints the first field, and does some numerical evaluation of the other fields.
I'm looking to put all the numerical fields into an array but every time I do this and try to access a random element of the array, it prints the entire thing
My CSV file is:
Exp1,10,12,13
Exp2,15,16,19
and i'm trying to access the second field so it prints
Exp1 12
Exp2 16
but instead I'm getting
Exp1 101213
Exp2 151619
If someone could provide some suggestions. This is my code:
#define DELIM ","
int main(int argc, char *argv[])
{
if(argc == 2) {
FILE *txt_file;
txt_file = fopen(argv[1], "rt");
if(!txt_file) {
printf("File does not exist.\n");
return 1;
}
char tmp[4096];
char data[4096];
char expName[100];
char *tok;
int i;
while(1){
if(!fgets(tmp, sizeof(tmp), txt_file)) break;
//prints the experiment name
tok = strtok(tmp, DELIM);
strncpy(expName, tok, sizeof(expName));
printf("\n%s ", expName);
while(tok != NULL) {
tok = strtok(NULL, DELIM);
//puts data fields into an array
for(i=0; i < sizeof(data); i++) {
if(tok != NULL) {
data[i] = atoi(tok);
}
}
printf("%d", data[1]);
}
}
fclose(txt_file);
return 0;
}
sample to fix
char tmp[4096];
int data[2048];
char expName[100];
char *tok;
int i=0;
while(fgets(tmp, sizeof(tmp), txt_file)){
tok = strtok(tmp, DELIM);
strncpy(expName, tok, sizeof(expName));
printf("\n%s ", expName);
while((tok = strtok(NULL, DELIM))!=NULL){
data[i++] = atoi(tok);
}
printf("%d", data[1]);
i = 0;
}
A modified code snippet:
int data[20]; // change 20 to a reasonable value
...
while (1)
{ if (!fgets(tmp, sizeof(tmp), txt_file))
break;
//prints the experiment name
tok = strtok(tmp, DELIM);
strncpy(expName, tok, sizeof(expName));
printf("\n%s ", expName);
i = 0;
tok = strtok(NULL, DELIM);
while (tok != NULL)
{ //puts data fields into an array
data[i++] = atoi(tok);
if (i == 20)
break;
tok = strtok(NULL, DELIM);
}
if (i > 1)
printf("%d", data[1]);
}

Adding Tokens to an Array C

So I'm trying to add tokens to an array the if statement keeps verifying that the array, tokenHolder, is empty. My second while loop is where I try to input tokens into the array. However no tokens are inputted into the array and I don't understand why.
char* token;
int* bufflength = 0;
char* buffer = NULL;
char input[25000];
char *tokenHolder[2500];
int pos = 0;
while(1){
printf("repl> ");
getline(&buffer, &bufflength, stdin);
token = strtok(buffer, "");
//code to input tokens into array
while(token != NULL){
pos++;
token = strtok(NULL, "");
tokenHolder[pos] = token;
}
if(tokenHolder[0] == NULL){
printf("It's NULL");
}
}
You increment pos to 1 before you save any token, so nothing is ever assigned to tokenHolder[0].
Either use (note the use of blank rather than an empty string as the delimiter):
tokenHolder[0] = token = strtok(buffer, " ");
(or an equivalent) or do something like:
char *data = buffer;
while ((tokenHolder[pos++] = strtok(data, " ")) != NULL)
data = NULL;
char *tokenHolder[2500] = { NULL };
...
while(token != NULL){
tokenHolder[pos++] = token;
token = strtok(NULL, "");
}
if(tokenHolder[0] == NULL){//or if(pos == 0){
printf("It's NULL");
}

Resources