I want to parse a .csv file of the form: grade(float), year(int), name(string), county(string), number(int) and I don't see my mistake:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 300
typedef struct{
char *name, *county;
float grade;
unsigned long int year, number;
}info;
int main(int argc, char **argv)
{
FILE *fin = fopen("lab3.txt", "r");
if(!fin)
{
fprintf(stderr, "ERROR OPENING THE FILE.\n");
exit(1);
}
char buf[MAX];
while(fgets(buf, MAX, fin))
{
buf[strcspn(buf,"\n")] = '\0'; // remove the trailing newline
char *fields[6], *word = strtok(buf, ",");
int i = 0;
while(word)
{
info *array = (info *)malloc(sizeof(info));
fields[i] = word;
array->name = strdup(fields[2]);
array->county = strdup(fields[3]);
array->grade = atof(fields[0]);
array->year = atoi(fields[1]);
array->number = atoi(fields[4]);
printf("Name : %s | County: %s | Year : %ld | Grade : %f | Number: %ld",array->name, array->county, array->year, array->grade, array->number);
//printf("Word : %s\n", fields[i]);
word = strtok(NULL,",");
i++;
free(array->county);
free(array->name);
free(array);
}
}
fclose(fin);
return 0;
}
There are exactly 5 fields on each line, so I wanted to break each line into words andI also used gdb to check what's wrong and the problem seems to be here array->name = strdup(fields[2]);. I've tried numerous things so far, I've allocated memory for the array and the name and county, I'm freeing the memory so, what's the mistake?
There are a number of issues.
You don't really need [nor want] either fields or word. You can just use realloc to increase the size of the array and operate on the current pointer directly.
Doing malloc(sizeof(char)), as you have it, just leaks memory because of the strdup after it.
You do not want to do free at the bottom of the loop--it just kills what you did strdup for.
You are not expanding the size of array on each iteration [using realloc], so your array size stays at one. And, you blow away the previous value on each iteration, so, again, you're leaking memory.
Here's a refactored version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 300
typedef struct {
char *name;
char *county;
float grade;
unsigned long int year;
unsigned long int number;
} info;
#define TOK \
({ \
cp = strtok(bp,","); \
bp = NULL; \
cp; \
})
#define SAVE_S(_sym) \
arrcur->_sym = strdup(TOK)
#define SAVE_F(_sym) \
arrcur->_sym = atof(TOK)
#define SAVE_I(_sym) \
arrcur->_sym = atol(TOK)
int
main(int argc, char **argv)
{
FILE *fin = fopen("lab3.txt", "r");
if (!fin) {
fprintf(stderr, "ERROR OPENING THE FILE.\n");
exit(1);
}
char buf[MAX];
info *arrbase = NULL;
int arrcnt = 0;
int arrmax = 0;
char *bp;
char *cp;
while (fgets(buf, MAX, fin)) {
// remove the trailing newline
buf[strcspn(buf, "\n")] = '\0';
// enlarge array -- use of arrmax limits number of realloc calls
if (arrcnt >= arrmax) {
arrmax += 100;
arrbase = realloc(arrbase,sizeof(*arrbase) * arrmax);
}
// point to current array entry
info *arrcur = &arrbase[arrcnt];
bp = buf;
SAVE_S(name);
SAVE_S(county);
SAVE_F(grade);
SAVE_I(year);
SAVE_I(number);
printf("Name : %s | County: %s | Year : %ld | Grade : %f | Number: %ld",
arrcur->name, arrcur->county, arrcur->year, arrcur->grade,
arrcur->number);
++arrcnt;
}
fclose(fin);
// trim to size used
arrbase = realloc(arrbase,sizeof(*arrbase) * arrcnt);
return 0;
}
Related
So following up on a previous question i asked Dynamic arrays clarification in C
the code works fine exept when I try to print the elements, all the elements are printed correct exept the 3rd element weirdly enough even when i change the input file always the 3rd element ir printet wrong. This are the print results: host0host1♥host3. Thanks for any help in advance! Updated to a complete code with the results again beeing host0host1♥host3, This is the file that i read from:
0 host0
1 host1
2 host2
3 host3
4 host4
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int total = 0;
struct names
{
int number;
char *name;
};
void readNames(FILE *fpN, struct names **name)
{
char sentence[512];
struct names *local_name = *name;
while (fgets(sentence, sizeof(sentence), fpN))
{
local_name = realloc(local_name, total + 1 * sizeof(*local_name));
char *token = strtok(sentence, " "); // grab the number
local_name[total].number = atoi(token); // add it to the array
token = strtok(NULL, " \n");
local_name[total].name = malloc(strlen(token) + 1);
strcpy(local_name[total].name, token);
total++;
// printf("TOTAL:%d",*total);
// local_numbers = (int*)realloc(local_numbers,(total+1)*sizeof(int)); //allocate more space for the int array
// local_names = (char**)(realloc(local_names, (total+1)*sizeof(char*))); //allocate more space for the string array
// printf("%d", sizeof(names));
}
*name = local_name;
printf("%s", local_name[0].name);
printf("%s", local_name[1].name);
printf("%s", local_name[2].name);
printf("%s", local_name[3].name);
}
int main(int argc, char **argv)
{
FILE *fpN = NULL;
FILE *fpG = NULL;
if (argc != 2)
{
printf("Wrong arguments");
exit(0);
}
if ((fpN = fopen(argv[1], "r")) == NULL)
{ // check if the file exists
printf("File not found!");
exit(0);
}
struct names *name = NULL; // Allcation will be done later
readNames(fpN, &name);
}
I have an archive results.csv and I need to read the first line of this archive and print it out on output.txt. Somehow it's printing random characters after everything and I couldn't figure out what is wrong.
Command: a.c results.csv
First line:
date,home_team,away_team,home_score,away_score,tournament,city,country,neutral
output.txt: date,home_team,away_team,home_score,away_score,tournament,city,country,neutral,(!£,(!£,(!£,(!£,(!£,#,£,(!£,(!£
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
typedef struct
{
char *line1;
char *line1a;
char *line1b;
char *team1;
char *team2;
char *reason;
char *city;
char *country;
char *neutral_field;
}data;
void open_input(char *argv[], FILE **input)
{
if((*input=fopen(argv[1], "r")) == NULL)
{
printf("%s not found\n", argv[1]);
exit(1);
}
}
void open_output(char *string, FILE **output)
{
if((*output=fopen(string, "w")) == NULL)
{
printf("%s not found\n", string);
exit(1);
}
}
void alloc_data(data *d, int size)
{
d->line1 = (char*)malloc(4*sizeof(char));
d->team1 = (char*)malloc(9*sizeof(char));
d->team2 = (char*)malloc(9*sizeof(char));
d->line1a = (char*)malloc(10*sizeof(char));
d->line1b = (char*)malloc(10*sizeof(char));
d->reason = (char*)malloc(10*sizeof(char));
d->city = (char*)malloc(4*sizeof(char));
d->country = (char*)malloc(7*sizeof(char));
d->neutral_field = (char*)malloc(7*sizeof(char));
}
void store(data *d, FILE *input, FILE **output)
{
fscanf(input, "%s,%s,%s,%s,%s,%s,%s,%s,%s", d[0].line1, d[0].team1, d[0].team2, d[0].line1a, d[0].line1b, d[0].reason, d[0].city, d[0].country, d[0].neutral_field );
fprintf(*output, "%s,%s,%s,%s,%s,%s,%s,%s,%s\n", d[0].line1, d[0].team1, d[0].team2, d[0].line1a, d[0].line1b, d[0].reason, d[0].city, d[0].country, d[0].neutral_field );
}
int main(int argc, char *argv[])
{
FILE *input;
FILE *output;
char *string = "output.txt";
int size = 1000;
open_input(argv, &input);
open_output(string, &output);
data *d;
d = (data*)malloc(size*sizeof(data));
alloc_data(d, size);
store(d, input, &output);
free(d);
return 0;
}
fscanf(input, "%s,%s,%s,%s,%s,%s,%s,%s,%s", d[0].line1, d[0].team1,...
The above code tries to read the whole line in to d[0].line1 which causes buffer overflow. team1 and the rest will contain uninitialized data.
You have to change fscanf as follows:
fscanf(input, "%3[^ ,\n\t],%9[^ ,\n\t],...
Where 3 is 4 - 1, and 4 is the size of d[0].line1
Alternatively you can use strtok
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void store(FILE *input, FILE *output)
{
char buf[500];
while(fgets(buf, sizeof(buf), input))
{
//strip end-of-line from `buf`
if(strlen(buf))
if(buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0;
//tokenize with strtok
char *token = strtok(buf, ",");
while(token)
{
fprintf(output, "%s", token);
token = strtok(NULL, ",");
}
fprintf(output, "\n");
}
}
int main(int argc, char *argv[])
{
FILE *input = fopen("input.txt", "r");
FILE *output = fopen("output.txt", "w");
store(input, output);
return 0;
}
With above code you don't need an additional structure.
If you do use a structure for data, you have to be more careful. It seems you are trying to create an array of 1000 data, but the following only creates one oversized pointer, not an array of data
int size = 1000;
data *d;
d = (data*)malloc(size*sizeof(data));
alloc_data(d, size);
Additionally, for each malloc there should be a corresponding free.
Your buffers aren't big enough to hold the terminating NUL byte. scanf stores that NUL byte (overrunning the buffer), but then the object that really owns that byte may overwrite it, so when printf looks for the NUL it doesn't find it until much later in memory.
The buffer overruns are a bigger problem than what you've seen, who knows what objects those NUL bytes you didn't make space for are smashing? And what happens when you read a data file with slightly different header spelling? Suddenly your hard-coded allocations sizes will be even more wrong than they are already.
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;
}
I am trying to read a file test.txt via fscanf and store it in a array of struct. This is what I tried. Problem here is that fscanf is not working as it is supposed to. After reading the file, I am also trying to print it on screen, but it won't work.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Item {
double value;
int unit;
char name[50];
};
int load(struct Item* item, FILE* data);
void display(struct Item item, int variableA);
int main()
{
struct Item I;
int i;
char ck;
ck = fopen("test.txt", "r");
if (ck)
{
for (i = 0; i < 3; i++)
{
load(&I, ck);
display(I, 0); //DISPLAY FUNCTION THAT READS test.txt and DISPLAYS
}
fclose(ck);
}
return 0;
}
int load(struct Item* item, FILE* data)
{
fscanf(data, "%d,%.2lf,%s\n", &(*item).unit,&(*item).value,&(*item).name);
return 0;
}
void display(struct Item item, int variableA)
{
printf("|%3d |%12.2lf| %20s |***\n", item.unit, item.value, item.name);
return;
}
This is what I have in test.txt file:
205,11.20,John Snow
336,23.40,Winter is coming
220,34.20,You know nothing
Error: Program compiles with some warnings , but I get segmentation fault when I execute the code.
Any idea why?
Output Expectation: OUTPUT should be read from test.txt file and should be displayed on to the screen.
Multiple problems in the program:
1.
char ck;
ck = fopen("test.txt", "r");
fopen returns a FILE*, not a char, use
FILE* ck = fopen(...);
2.
fscanf(data, "%d,%.2lf,%s\n", &(*item).unit,&(*item).value,&(*item).name);
always check return value of fscanf, if it is smaller than the number of fields you requested, the following call to fscanf is unlikely to do what you expect. Also, *item.unit is the same as item->unit, use item->unit because it is shorter and cleaner:
int ret = fscanf(data, "%d,%lf,", &item->unit, &item->value);
if (ret != 3) { // error }
Third, %s matches a sequence of non-white-space characters, so when fscanf reads "John", it will stop, and the next fscanf call will get to read "Snow" while expecting an integer.
So to input a string with whitespace, use fgets instead, and remember to remove the newline character in the end.
Try following:
int main(void)
{
struct Item I;
int i;
FILE* ck;
int ret;
ck = fopen("test.txt", "r");
if (ck)
{
for (i = 0; i < 3; i++)
{
ret = load(&I, ck);
if (ret < 0)
break;
display(I, 0); //DISPLAY FUNCTION THAT READS test.txt and DISPLAYS
}
fclose(ck);
}
return 0;
}
int load(struct Item* item, FILE* data)
{
int ret = fscanf(data, "%d,%lf,", &item->unit, &item->value);
if (ret != 2) {
return -1;
}
fgets(item->name, sizeof item->name, data);
item->name[strlen(item->name)-1] = '\0';
return 0;
}
void display(struct Item item, int variableA)
{
printf("|%3d |%12.2lf| %20s |***\n", item.unit, item.value, item.name);
return;
}
It outputs:
$ ./a.out
|205 | 11.20| John Snow |***
|336 | 23.40| Winter is coming |***
|220 | 34.20| You know nothing |***
You can try this different approach.
It uses:
malloc,realloc to allocate and reallocate memory for array of structs. I assumed that much larger text files with more lines will be used and this allows the array to resize when needed to accommodate more information.
strtok to parse each peice of data between , delimeters, and then store them into the array of structures.
Checks return value of pointers to avoid segmentation faults.
Uses fgets to read each line of the file into a string, from which we can parse ourselves afterwards.
This is the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMESTRLEN 50
#define INITSIZE 3
#define MAXSIZE 100
typedef struct {
int unit;
double value;
char name[NAMESTRLEN+1];
} item_t;
typedef struct {
item_t *items;
int numlines;
} allitems_t;
allitems_t *initialize_arraystructs(void);
void print_arraystructs(allitems_t *allitems);
void read_insert_items(FILE *filestream, allitems_t *allitems);
void check_ptr(void *ptr, const char *msg);
int
main(void) {
allitems_t *allitems;
FILE *fp;
fp = fopen("test.txt", "r");
if (fp == NULL) {
fprintf(stderr, "%s\n", "Error reading file!\n");
exit(EXIT_FAILURE);
}
allitems = initialize_arraystructs();
read_insert_items(fp, allitems);
print_arraystructs(allitems);
return 0;
}
void
read_insert_items(FILE *filestream, allitems_t *allitems) {
int count = 0;
char line[MAXSIZE];
char *unit, *value, *name;
size_t numitems = INITSIZE;
allitems->items = malloc(numitems * sizeof(item_t));
check_ptr(allitems->items, "Initial Allocation");
while (fgets(line, MAXSIZE, filestream) != NULL) {
unit = strtok(line, ",");
value = strtok(NULL, ",");
name = strtok(NULL, "\n");
if (count == numitems) {
numitems *= 2;
allitems->items = realloc(allitems->items, numitems * sizeof(item_t));
check_ptr(allitems->items, "Reallocation");
}
allitems->items[count].unit = atoi(unit);
allitems->items[count].value = atof(value);
strcpy(allitems->items[count].name, name);
count++;
allitems->numlines++;
}
}
allitems_t
*initialize_arraystructs(void) {
allitems_t *allitems;
allitems = malloc(sizeof(allitems_t));
check_ptr(allitems, "Initial Allocation");
allitems->items = NULL;
allitems->numlines = 0;
return allitems;
}
void
print_arraystructs(allitems_t *allitems) {
int i;
for (i = 0; i < allitems->numlines; i++) {
printf("%d,%.2f,%s\n",
allitems->items[i].unit,
allitems->items[i].value,
allitems->items[i].name);
}
}
void
check_ptr(void *ptr, const char *msg) {
if (!ptr) {
printf("Unexpected null pointer: %s\n", msg);
exit(EXIT_FAILURE);
}
}
In the below code, the file test.txt has the following data :
192.168.1.1-90
192.168.2.2-80
The output of this is not as expected.
I expect the output to be
192.168.1.1
90
192.168.2.2
80
Any help would be much appreciated.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char *result[10][4];
int i=0;
const char s[2] = "-";
char *value,str[128];
fp = fopen("test.txt", "r");
if (fp == NULL)
printf("File doesn't exist\n");
else{
while(!feof(fp)){
if(fgets(str,sizeof(str),fp)){
/* get the first value */
value = strtok(str, s);
result[i][0]=value;
printf("IP : %s\n",result[i][0]); //to be removed after testing
/* get second value */
value = strtok(NULL, s);
result[i][1]=value;
printf("PORT : %s\n",result[i][1]); //to be removed after testing
i++;
}}
for (int k=0;k<2;k++){
for (int j=0;j<2;j++){
printf("\n%s\n",result[k][j]);
}
}
}
return(0);
}
You can try this solution. It uses dynamic memory instead, but does what your after.
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFSIZE 128
void exit_if_null(void *ptr, const char *msg);
int
main(int argc, char const *argv[]) {
FILE *filename;
char buffer[BUFFSIZE];
char *sequence;
char **ipinfo;
int str_size = 10, str_count = 0, i;
filename = fopen("ips.txt", "r");
if (filename == NULL) {
fprintf(stderr, "%s\n", "Error Reading File!");
exit(EXIT_FAILURE);
}
ipinfo = malloc(str_size * sizeof(*ipinfo));
exit_if_null(ipinfo, "Initial Allocation");
while (fgets(buffer, BUFFSIZE, filename) != NULL) {
sequence = strtok(buffer, "-\n");
while (sequence != NULL) {
if (str_size == str_count) {
str_size *= 2;
ipinfo = realloc(ipinfo, str_size * sizeof(*ipinfo));
exit_if_null(ipinfo, "Reallocation");
}
ipinfo[str_count] = malloc(strlen(sequence)+1);
exit_if_null(ipinfo[str_count], "Initial Allocation");
strcpy(ipinfo[str_count], sequence);
str_count++;
sequence = strtok(NULL, "-\n");
}
}
for (i = 0; i < str_count; i++) {
printf("%s\n", ipinfo[i]);
free(ipinfo[i]);
ipinfo[i] = NULL;
}
free(ipinfo);
ipinfo = NULL;
fclose(filename);
return 0;
}
void
exit_if_null(void *ptr, const char *msg) {
if (!ptr) {
printf("Unexpected null pointer: %s\n", msg);
exit(EXIT_FAILURE);
}
}
The key thing to understand is that char *strtok(char *str, const char *delim) internally modifies the string pointed to by str and uses that to store the result. So the returned pointer actually points to somewhere in str.
In your code, the content of str is refreshed each time when you parse a new line in the file, but the address remains the same. So after your while loop, the content of str is the last line of the file, somehow modified by strtok. At this time, result[0][0] and result[1][0] both points to the same address, which equals the beginning of str. So you print the same thing twice in the end.
This is further illustrated in the comments added to your code.
int main()
{
FILE *fp;
char *result[10][4];
int i=0;
const char s[2] = "-";
char *value,str[128];
fp = fopen("test.txt", "r");
if (fp == NULL)
printf("File doesn't exist\n");
else{
while(!feof(fp)){
if(fgets(str,sizeof(str),fp)){
/* get the first value */
value = strtok(str, s);
// ADDED: value now points to somewhere in str
result[i][0]=value;
// ADDED: result[i][0] points to the same address for i = 0 and 1
printf("IP : %s\n",result[i][0]); //to be removed after testing
/* get second value */
value = strtok(NULL, s);
// ADDED: value now points to somewhere in str
result[i][1]=value;
// ADDED: result[i][1] points to the same address for i = 0 and 1
printf("PORT : %s\n",result[i][1]); //to be removed after testing
i++;
}}
// ADDED: now result[0][0]==result[1][0], result[0][1]==result[1][1], you can test that
for (int k=0;k<2;k++){
for (int j=0;j<2;j++){
printf("\n%s\n",result[k][j]);
}
}
}
return(0);
}
To get the expected output, you should copy the string pointed by the pointer returned by strtok to somewhere else each time, rather than just copy the pointer itself.