3

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.

4
  • 4
    Maybe because "John" is incorrect ; that's "Jon" Snow... Commented Nov 28, 2016 at 1:04
  • @ringø haha :D :D :D Commented Nov 28, 2016 at 1:12
  • You have to debug the program to see where the error occurs (reading or displaying) and also to see if you get what you think you should get. Commented Nov 28, 2016 at 1:26
  • Well, for one, fopen doesn't return char, it return FILE*. Don't ignore the warnings. They're there to tell you something is wrong. So fix them. Commented Nov 28, 2016 at 1:28

2 Answers 2

4

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 |***
Sign up to request clarification or add additional context in comments.

6 Comments

Ty for prompt explanation. :)
I get a black output still.
@John Can you show how you compile the program and how you run it?
gcc -Wall test.c and then ./a.out
@John does test.txt exist?
|
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.