0

The idea of my program is to read data from a file (in this case file includes 5 names), and store them in a list, so I can use the data later to for example calculating the min/max characters. So far I have been able to read the data and print it out, but rather than printing, I'd want to save them to a list. I couldn't find a way around how to do this, so I'd appreciate some help.

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

int main(void) {

    char file, filename[25];
    FILE *f;

    printf("Enter the file name: ");
    scanf("%s", filename);

    f = fopen(filename, "r");

    if (f == NULL)
    {
        perror("No file found.\n");
        return 0;
    }

    printf("The contents of %s file are:\n", filename);

    while((file = fgetc(f)) != EOF)
        printf("%c", file);

    fclose(f);

    return 0;
}
bruceg
  • 2,433
  • 1
  • 22
  • 29
Armeija
  • 53
  • 7
  • And where is your list implementation? What have you tried doing? – UnholySheep Feb 21 '19 at 20:16
  • Printing one character at a time seems extremely inefficient. Read in chunks and dump those out. – tadman Feb 21 '19 at 20:17
  • https://www.geeksforgeeks.org/data-structures/linked-list/ – Robert Harvey Feb 21 '19 at 20:17
  • 1
    `char file` ==> `int file`. `EOF` is a negative value of type `int`. If your implementation `char` is unsigned you have a big big problem. – pmg Feb 21 '19 at 20:20
  • I've tried with struct @UnholySheep – Armeija Feb 21 '19 at 20:20
  • I don't see any `struct` in the code you posted – UnholySheep Feb 21 '19 at 20:21
  • I deleted it, as it wasn't linked to anything and thus didn't do anything. – Armeija Feb 21 '19 at 20:23
  • Please don't use `gets`; it's dangerous and no longer supported by C – Govind Parmar Feb 21 '19 at 20:26
  • Ah, changed `gets` to `scanf` – Armeija Feb 21 '19 at 20:30
  • So, what exactly is your problem? [Reading the contents of an entire file into memory?](https://stackoverflow.com/questions/174531/how-to-read-the-content-of-a-file-to-a-string-in-c) – Antti Haapala -- Слава Україні Feb 21 '19 at 20:39
  • I don't approve of using the internet to find people to do your homework for you. But I'll tell you that if you want to sort the characters by their value, then first find the size of the file, then allocate that much memory and load the file into memory, then use qsort. That is to say that you do not need a list. You should be able to figure out how to do this on your own and by googling. – Daniel Santos Feb 21 '19 at 21:51
  • What makes you think it's my homework? It's a exercise found from the internet, I'm just learning programming. Thanks for the comment tho. I never asked for a solution. I'm more than happy for people to guide what should I do. – Armeija Feb 23 '19 at 12:13

2 Answers2

1

Just a simple example using linked names for your list :

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

typedef struct NameList {
  char * name; 
  struct NameList * next;
} NameList;

int append(NameList ** head, NameList ** tail, char * s)
{
  NameList * l;

  if (((l = malloc(sizeof(NameList))) == NULL) ||
      ((l->name = strdup(s)) == NULL))
    /* not enough memory */
    return 0;

  l->next = NULL;
  if (*head == NULL) {
    *head = *tail = l;
  }
  else {
    (*tail)->next = l;
    *tail = l;
  }

  return 1;
}

int main(void) {
  char filename[25];
  FILE * f;

  printf("Enter the file name: ");
  if (scanf("%24s", filename) != 1)
    return 0;

  f = fopen(filename, "r");

  if (f == NULL)
  {
    puts("No file found.");
    return 0;
  }

  NameList * head = NULL;
  NameList * tail = NULL;
  char s[64];

  /* suppose a name has no space even composed and less than 64 characters */
  while (fscanf(f, "%63s", s) == 1) {
    if (!append(&head, &tail, s))
      return 0;
  }

  fclose(f);

  printf("The names in %s file are:\n", filename);

  NameList * l;

  l = head;
  while (l != NULL) {
    puts(l->name);
    l = l->next;
  }

  /* search longer name */
  size_t maxlen = 0;
  char * longer = NULL;

  l = head;
  while (l != NULL) {
    size_t ln = strlen(l->name);

    if (ln > maxlen) {
      maxlen = ln;
      longer = l->name;
    }

    l = l->next;
  }
  if (longer != NULL)
    printf("longer name is : %s\n", longer);

  /* free resources */
  while (head != NULL) {
    l = head;
    head = head->next;
    free(l->name);
    free(l);
  }

  return 0;
}

Compilation and execution

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra l.c
pi@raspberrypi:/tmp $ cat aze
firstname secondname
anothername
lastname
pi@raspberrypi:/tmp $ ./a.out
Enter the file name: aze
The names in aze file are:
firstname
secondname
anothername
lastname
longer name is : anothername

Execution under valgrind

pi@raspberrypi:/tmp $ valgrind ./a.out
==10132== Memcheck, a memory error detector
==10132== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10132== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10132== Command: ./a.out
==10132== 
Enter the file name: aze
The names in aze file are:
firstname
secondname
anothername
lastname
longer name is : anothername
==10132== 
==10132== HEAP SUMMARY:
==10132==     in use at exit: 0 bytes in 0 blocks
==10132==   total heap usage: 12 allocs, 12 frees, 6,570 bytes allocated
==10132== 
==10132== All heap blocks were freed -- no leaks are possible
==10132== 
==10132== For counts of detected and suppressed errors, rerun with: -v
==10132== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)

Note the linked list can be replaced by an array of char*, using realloc to increase its size when reading the names etc

bruno
  • 32,421
  • 7
  • 25
  • 37
  • Thanks for the comment, for some reason when trying your code, I get Segmentation fault right after entering the file. I tried to work around the code a bit myself, but I couldn't get it solved. Just for reference, inside the .csv is 5 names each in it's own line. – Armeija Feb 23 '19 at 12:45
  • @Armeija are you sure you get my code without modification ? – bruno Feb 23 '19 at 12:51
  • @Armeija can you reduce the size of the csv to the minimum producing the crash and give it contains ? – bruno Feb 23 '19 at 12:56
  • Yeah, no modifications. No matter how I edit the csv file, it doesn't work. This is indeed pretty weird – Armeija Feb 23 '19 at 13:11
  • @Armeija give the first line of your csv then – bruno Feb 23 '19 at 13:14
  • One file contains 'a' only, and another 'Jack' 'Mary' 'Tony' – Armeija Feb 23 '19 at 13:15
  • 1
    @Armeija what is your OS ? – bruno Feb 23 '19 at 13:17
  • And just to add, I open it with a text editor. I don't think is a problem either, as if I open a legitimate .txt file, it gives the same error. OS: Ubuntu – Armeija Feb 23 '19 at 13:17
  • you are probably under window, run the program into a debugger or add traces messages – bruno Feb 23 '19 at 13:19
  • I compile it with: `gcc -o test test.c -std=c99 -Wall` It gives some warnings: https://i.imgur.com/QYpG5bv.png – Armeija Feb 23 '19 at 13:25
  • 1
    @Armeija if you compile with _c99_ you have to define _strdup_ by yourself because it is not standard, or remove that flag. `-std=c99`. `char * strdup(const char * s) { char * r = malloc(strlen(s) + 1); if (r != NULL) strcpy(r, s); return r; }` – bruno Feb 23 '19 at 13:31
  • Ahh, that's a new thing for me. I'll look into it. Thanks ! – Armeija Feb 23 '19 at 13:37
  • 1
    @Armeija to avoid problem let the compiler to decide the version of C, remove -std option – bruno Feb 23 '19 at 13:46
  • Appreciate your time! Thank you. Sorry for making it a bit hard for you – Armeija Feb 23 '19 at 13:50
  • 1
    @Armeija no problem, have happy coding – bruno Feb 23 '19 at 13:52
1

Apart for details of the format of the input, reading in the data is not flexible in your current solution. Heres a more generous input reading. Combine this with the other answers and you should be on your way.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    {
        char *cwd = getcwd(NULL, 0);
        printf("FYI, the current working directory of this program is : `%s'\n", cwd);
        free(cwd);
    }
    printf("Enter the file name: ");
    char *filename;
    int scanf_return = scanf("%m[a-zA-Z./]", &filename);
    if (scanf_return != 1) {
        if (errno != 0) {
            perror("scanf");
        } else {
            fprintf(stderr, "%s\n",
                    "Sorry, unable to read file name. "
                    "Only 'a'...'z', 'A'...'Z', '.' (period) "
                    "and '/' (slash) allowed in the name.");
        }
        return EXIT_FAILURE;
    }
    FILE *f = fopen(filename, "r");
    if (f == NULL) {
        perror(filename);
        free(filename);
        return EXIT_FAILURE;
    }
    printf("The contents of `%s' file are:\n", filename);
    free(filename);
    filename = NULL;

    size_t line_sz = 0u;
    char *line = NULL;
    int nread;
    errno = 0;
    while ((nread = getline(&line, &line_sz, f)) != -1) {
        // If we reached the EOF then there might not be a newline character
        if (line[nread - 1] != '\n') {
            nread++;
        }
        printf("`%.*s'\n", nread - 1, line);
    }
    if (errno != 0) {
        perror("getline");
        free(line);
        fclose(f);
        return EXIT_FAILURE;
    }
    free(line);
    fclose(f);
    return EXIT_SUCCESS;
}
Bo R
  • 2,334
  • 1
  • 9
  • 17