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

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

Related

How i can create a function for reading structure from a test.txt

How i can create a function for reading structure from a test.txt. I have a good works code in main, but i need to carry out it from main(). How combine (struct student PI1[N] and (fread() or fgets() or fwrite()));
struct student {
char surname[50];
char name[50];
char dayBirth[50];
int mark;
};
struct student PI1[N];
int main()
{
int counter = 0;
char str[50];
const char s[2] = " ";
char* token;
FILE* ptr;
int i = 0;
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
printf("file can't be opened \n");
}
char* tmp;
int Itmp;
while (fgets(str, 50, ptr) != NULL) {
token = strtok(str, s);
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
strcpy(PI1[i].name, token);
token = strtok(NULL, s);
strcpy(PI1[i].dayBirth, token);
token = strtok(NULL, s);
Itmp = atoi(token);
PI1[i].mark = Itmp;
i++;
counter++;
}
}
Rather than "can create a function for reading structure from a test.txt", start with a function to convert a string from fgets() into a struct. Then call it as needed.
Use sprintf() and " %n" to detect complete scan with no extra text.
// Return success flag
bool string_to_student(struct student *stu, const char *s) {
int n = 0;
sscanf(s, "%49s%49s%49s%d %n", stu->surname, stu->name,
stu->dayBirth, &stu->mark, &n);
return n > 0 && s[n] == '\0';
}
Use
while (i < N && fgets(str, sizeof str, ptr) &&
string_to_student(&PI1[i], str)) {
i++;
}
counter = i;

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

using strtok to get input from file

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

Unable to achieve expected parsing output with strtok

I have been working on this for a while now but I do not seem to be able to resolve this bug. Any insights will be greatly appreciated thanks.
I am writing code that will parse a string first by ";" and then by " ". The code I have below is as follows:
void arrayVis(char **arr, int size){
printf("[");
for(int i = 0; i < size; i++){
if(arr[i] == NULL || strcmp(arr[i], "") == 0){
break;
}
printf("%s,", arr[i]);
}
void parser2(char *line){
char *token = strtok(line, " ");
char *arr[10];
int index = 0;
while(token != NULL){
arr[index] = token;
token = strtok(NULL, " ");
index++;
}
arrayVis(arr, 10);
}
void parser1(char *line){
char *token = strtok(line, ";");
while(token != NULL){
parser2(token);
// myPrint(token);
printf("\n");
token = strtok(NULL, ";");
}
}
array vis will just allow me to visualize the array that is produced. When I pass "1 2 3;4 5 6;"
I am expecting an output of
[1,2,3
[4,5,6
but instead I just get the output
[1,2,3
Why is my output omitting the second portion of the parse? I have been thinking about this for a while now but I dont seem to understand why this happens. Any insights will be appreciated. Thank you.
int main(void) {
static const char *ROW_TOKENS=";";
static const char *COL_TOKENS=" ";
char buf[] = "data1;data2 data3;data4 data5";
char *aux_row,*cursor_row;
cursor_row = strtok_r(buf, ROW_TOKENS, &aux_row);
// printf("[A] buf: %p, cursor_row=%p, aux_row=%p\n", buf, cursor_row, aux_row);
while (cursor_row) {
char *aux_col,*cursor_col;
printf("[");
cursor_col = strtok_r(cursor_row, COL_TOKENS, &aux_col);
// printf("[B] cursor_row=%p, aux_row=%p, cursor_col=%p, aux_col=%p\n",
// cursor_row, aux_row, cursor_col, aux_col);
while (cursor_col) {
// printf("[C] cursor_row=%p, aux_row=%p, cursor_col=%p, aux_col=%p\n",
// cursor_row, aux_row, cursor_col, aux_col);
printf("%s,", cursor_col);
cursor_col = strtok_r(NULL, COL_TOKENS, &aux_col);
}
cursor_row = strtok_r(NULL, ROW_TOKENS, &aux_row);
printf("\n");
}
return 0;
}

including <stdlib.h> causes segmentation fault

I am writing a program to read info from a text file and I had everything working. The problem is that I am trying to add the functionality to calculate the mean of some of fields and have to convert the strings to doubles. I noticed that atof would work in some cases but would return -1 for the most part. i then realized that I didn't include stdlib, so I added that, but now I am getting a segmentation fault with just that one change.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
main(int argc, char *argv[]){
int numParts=10;
long numTests=49000;
char filename[] = "sweep_data.txt";
FILE *fp= fopen(filename,"r");
FILE *out= fopen("TesterData.csv", "w+");
FILE *meanf= fopen("meanData.txt", "w+");
char delims[] = " <>";
char *result = NULL;
char line [128];
char *TestNum=NULL;
char *TestName=NULL;
char *TestName2=NULL;
char *SequencerName=NULL;
char *lowlim=NULL;
char *hilim=NULL;
char *value=NULL;
char *units=NULL;
char *DeviceNum=NULL;
char ValueArray[numTests][10];
char ***ValuePtr = NULL;
char InfoArray[numTests][20];
char ***InfoPtr = NULL;
double mean[numTests];
long sum;
int intResult;
int count=0;
int DeviceCount=-1;
int i,j, m,n,k,a,b, len, mLen;
/*Allocate Memory for 2D arrays*/
ValuePtr = malloc(numParts * sizeof *ValuePtr);
for(i=0; i<numParts;i++){
ValuePtr[i]=malloc(numTests*sizeof *ValuePtr);
for(j=0; j<numTests; j++){
ValuePtr[i][j] = malloc(strlen(ValueArray[j]) +1);
}
}
InfoPtr = malloc(6 * sizeof *InfoPtr);
if(InfoPtr != NULL){
for(a=0; a<6;a++){
InfoPtr[a]=malloc(numTests*sizeof *InfoPtr);
for(b=0; b<numTests; b++){
InfoPtr[a][b]= malloc(strlen(InfoArray[b]) +1);
}
}
}
while(fgets(line, sizeof line, fp) != NULL){
result = strtok(line, delims);
TestNum=result;
intResult = strtol(result, NULL, 10);
if(intResult ==0){
if(strcmp(result, "Device:")==0){
DeviceCount++;
count=0;
}
continue; //if doesn't start with a number go to next line
}
result = strtok(NULL, delims);
TestName= result;
result = strtok(NULL, delims);
TestName2= result;
result = strtok(NULL, delims);
SequencerName = result;
lowlim=SequencerName;
if(atof(SequencerName)>1 || atof(SequencerName)<-1){
result= strtok(NULL, delims);
lowlim=result;
strcat(TestName, TestName2);
}
else
SequencerName= TestName2;
if(strstr(TestName, "%")!=NULL || strcmp(TestName,"PTgen")==0 || strcmp(TestName,"mode")==0){
units="NA";
}
else if(strstr(TestName, "2nd") == NULL && strstr(TestName, "3rd") == NULL){
result= strtok(NULL, delims);
units = result;
}
result= strtok(NULL, delims);
value=result;
if(strstr(TestName, "%")==NULL && strcmp(TestName,"PTgen")!=0 && strcmp(TestName,"mode")!=0){
result=strtok(NULL, delims);
if(strstr(TestName, "2nd") != NULL || strstr(TestName, "3rd") != NULL)
units = result;
}
result=strtok(NULL, delims);
hilim=result;
if(hilim[strlen(hilim)-1]=='\n'){
hilim[strlen(hilim)-1]='\0';
hilim[strlen(hilim)-1]='\0';
}
if(DeviceCount==0){
strcpy(InfoPtr[0][count], TestNum);
strcpy(InfoPtr[1][count], TestName);
strcpy(InfoPtr[2][count], SequencerName);
strcpy(InfoPtr[3][count], lowlim);
strcpy(InfoPtr[4][count], hilim);
strcpy(InfoPtr[5][count], units);
}
strcpy(ValuePtr[DeviceCount][count],value);
count++;
}
for(b=0;b<numTests;b++){
sum=0;
for(a=0;a<numParts;a++){
fprintf(meanf, "%s\n", ValuePtr[a][b]);
sum=atof(ValuePtr[a][b]);
fprintf(meanf, "%f\n",sum);
}
mean[b]=sum/numParts;
for(n=0; n<2;n++){
fprintf(meanf, "%s ", InfoPtr[n][b]);
}
fprintf(meanf, "%f\n",mean[b]);
}
printf("NumTests: %i\n",count); //number of tests run
printf("NumParts: %i\n", DeviceCount+1);//number of parts run
fprintf(out,"Test#, TestName,SeqName,LowLim,UpLim,Units,");
for(j=1; j<=numParts;j++){
fprintf(out," Device#%i,", j);
}
fprintf(out, "\n");
for (n = 0; n < numTests; n++) {
for(a=0;a<6;a++){
fprintf(out, "%s", InfoPtr[a][n]);
fprintf(out, ", ");
}
for (m =0; m < numParts ; m++) {
fprintf(out, "%s, ", ValuePtr[m][n]);
}
fprintf(out, "\n");
}
}
The part where I was needing to convert to a double is
for(b=0;b<numTests;b++){
sum=0;
for(a=0;a<numParts;a++){
fprintf(meanf, "%s\n", ValuePtr[a][b]);
sum+=atof(ValuePtr[a][b]);
fprintf(meanf, "%f\n",sum);
}
mean[b]=sum/numTests;
for(n=0; n<2;n++){
fprintf(meanf, "%s ", InfoPtr[n][b]);
}
fprintf(meanf, "%f\n",mean[b]);
}
The following line doesn't seem correct. It is using strlen on something that has not been initialized. So the results would be nondeterministic:
ValuePtr[i][j] = malloc(strlen(ValueArray[j]) +1);
The use of that later in the calls to atof() would quite possibly not produce consistent results. And depending on the actual allocation size, it could result in a seg fault when storing data in it.
Not that this has anything to do with the segfault, but:
ValuePtr = malloc(numParts * sizeof **ValuePtr);
...
InfoPtr = malloc(6 * sizeof **InfoPtr);

Resources