-5

i need help finding my mistake.

Write a program that reads a multi-line CSV file and reads any number of values ​​<X>,<Y>,...,<N> per line. Example file1.csv:

4,10,9,13
-1
7,13,100

After reading in, the values ​​are to be output in the format <X>/<Y>/ <N>:

4/10/9/13
-1
7/13/100

If opening the file fails, it should print "Error opening file\n".

Hint: Use fscanf for formatted reading from the file and strtok to split the lines.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct profile
{
    char a[30];
    char b[30];
    char c[30];
    char d[30];
    char e[30];
    char f[30];
    struct profile* next;
} profile;

profile* createProfile(char* line);
struct profile* addFirst(profile* item, profile* head);
void printList(struct profile* head);
struct profile* addLast(profile* item, profile* head);

int main() {
    FILE* file;
    char input[256];
    printf("Enter filename: ");
    scanf("%s", input);
    file = fopen(input, "r");
    if(file == NULL)
        printf("Error opening file\n");
    else
    {
    
        profile* head = NULL;

        while(fscanf(file, "%255s\n", input) != EOF)
        {
            profile* profile = createProfile(input);
              head = addLast(profile, head);
        }

        fclose(file);

        printList(head);
    }

    return 0;
}

profile* createProfile(char* line)
{
    profile* newProfile = malloc(sizeof(profile));
    char* token;

    token = strtok(line, ",");
    if(token != NULL)
        strcpy(newProfile->a, token);
        
    token = strtok(NULL, ",");
    if(token != NULL)
        strcpy(newProfile->b, token);

    token = strtok(NULL, ",");
    if(token != NULL)
        strcpy(newProfile->c, token);
    
    token = strtok(NULL, ",");
    if(token != NULL)
        strcpy(newProfile->d, token);

    token = strtok(NULL, ",");
    if(token != NULL)
        strcpy(newProfile->e, token);

    token = strtok(NULL, ",");
    if(token != NULL)
        strcpy(newProfile->f, token);

    newProfile->next = NULL;

    return newProfile;
}

struct profile* addFirst(profile* item, profile* head)
{
    item->next = head;
    return item;
}

void printList(struct profile* head)
{
    if(head != NULL)
    {
        printf("%s/%s/%s/%s/%s/%s\n",
            head->a,
            head->b,
            head->c,
            head->d,
            head->e,
            head->f);

        printList(head->next);
    }
    
}


struct profile* addLast(profile* item, profile* head)
{
 
    if (head == NULL)
    {

        return item;
    }

  
    profile* current = head;
    while(current->next != NULL)
    {
        current = current->next;
    }

  
    current->next = item;

    return head;
}

enter image description here

tried a lot nothing worked. any help is greatly appreciated.

Barmar
  • 741,623
  • 53
  • 500
  • 612
manuel
  • 1
  • 2
  • You don't check if the line has fewer than 6 fields. And what will you do if there are more than 6 fields? – Barmar Dec 08 '22 at 02:00
  • well one obvious error is you printf all the fields even if they are empty, this is why you get all those trailing '/' characters. you should remeber how many are filled – pm100 Dec 08 '22 at 02:11
  • Why do you bother with the `struct profile` opposed to an array? – Allan Wind Dec 08 '22 at 02:43
  • It's a bad idea to reuse a variable for different things (input). If you use `scanf()` to read a string always maximum length to avoid buffer overflow. – Allan Wind Dec 08 '22 at 02:45
  • @pm100 how can i fix that? – manuel Dec 08 '22 at 03:23

1 Answers1

0

You did not respond to my question as to why struct profile is required so I kept it. I would suggest just using array that you resize as needed instead like I do for fields.

Use constants (INPUT_LEN) instead of magic values.

The problem description talks about an arbitrary number of fields. This means you to keep the fields in a data structure that is expandable instead of hard-coding 6 variables. I used a dynamically allocated array here.

When using scanf() to read a string always specify the maximum field width when reading a string to avoid buffer overflows.

It's a bad idea to reuse the input variable so used pathname for the input file. If you move main() to the bottom then you usually don't require prototypes in a simple program like this.

#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INPUT_LEN 255
#define STR(s) STR2(s)
#define STR2(s) #s

typedef struct profile {
    size_t n;
    char **fields;
    struct profile *next;
} profile;

profile* createProfile(char* line) {
    profile* newProfile = malloc(sizeof(*newProfile));
    newProfile->n = 0;
    newProfile->fields = NULL;
    for(;;) {
        char *token = strtok(newProfile->fields ? NULL : line, ",");
        if(!token)
            break;
        newProfile->n++;
        char **tmp = realloc(newProfile->fields, (newProfile->n) * sizeof(*newProfile->fields));
        if(!tmp) {
            printf("realloc failed\n");
            free(newProfile->fields);
            free(newProfile);
            return NULL;
        }
        newProfile->fields = tmp;
        newProfile->fields[newProfile->n - 1] = strdup(token);
    }
    newProfile->next = NULL;
    return newProfile;
}

void printList(struct profile* head) {
    for(; head; head = head->next)
        for(size_t i = 0; i < head->n; i++)
            printf("%s%s", head->fields[i], i + 1 < head->n ? "/" : "\n");
}

struct profile* addLast(profile *head, profile *item) {
    if (!head)
        return item;
    profile *p = head;
    for(; p->next; p = p->next);
    p->next = item;
    return head;
}

int main(void) {
    printf("Enter filename: ");
    char pathname[FILENAME_MAX + 1];
    if(scanf("%" STR(FILENAME_MAX) "s", pathname) != 1) {
        printf("scanf failed\n");
        return 1;
    }

    FILE *file = fopen(pathname, "r");
    if(!file) {
        printf("Error opening file\n");
        return 1;
    }

    profile *head = NULL;
    char input[INPUT_LEN + 1];
    while(fscanf(file, "%" STR(INPUT_LEN) "s\n", input) != EOF) {
        head = addLast(head, createProfile(input));
    }
    fclose(file);
    printList(head);
    // TODO: free your list and its contents
}

and here is an example run:

Enter filename: input.csv
4/10/9/13
-1
7/13/100

If this was not a school assignment, you could just read each character and if it's a , print a / otherwise print whatever you read. Read from stdin instead of a file.

#include <stdio.h>

int main(void) {
    for(;;) {
        int c = getchar();
        switch(c) {
            case EOF:
                return 0;
            case ',':
                c = '/';
                break;
            default:
                break;
        }
        putchar(c);
    }
}

and you would call it like this:

./a.out < input.txt

If you are unix operating system you can could also just use tr:

tr ',' '/' < input.txt
Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • Thank you! the struct was my dumb way of trying to solve the task(this file stuff is very new to me) greatly appreciated. – manuel Dec 08 '22 at 05:20