1

I'm a beginner in C and I'm trying to create a simple todo list program. I'm trying to use getline in a while loop, as I saw on another stack overflow answer and I thought I understood it but it's just creating an infinite loop. Also it seems to be skipping the first word for some reason. Here is my code so far:

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

FILE *list;
int i = 0;
int item = 0;
char items[10];
char chars[1000];
char * line = NULL;
size_t len = 0;
ssize_t read;

int main() {
  list = fopen("/Users/bendavies/Documents/C/list.txt", "r");
  int letterCount = fscanf(list,"%s",chars);

  printf("Welcome to the to-do list. It can hold up to 10 items.\n");

  printf("%d\n", letterCount);

  if (letterCount == -1) {
    printf("The list currently has no items!\n");
  } else {
      while ((read = getline(&line, &len, list)) != 1) {
        item += 1;
        printf("%d. %s", item, line);
      }
    }
    fclose(list);

  return 0;
}

The output I'm currently getting with the following list.txt:

Eat food
Drink water
Breath air

Is:

1.  food
2. Drink water
3. Breath air
4. 18446744073709551615
5. 18446744073709551615

and so on and so forth.

Thank you in advance! :)

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
bavies
  • 101
  • 1
  • 5
  • 1
    Consider changing this `(read = getline(&line, &len, list)) != 1` to this `(read = getline(&line, &len, list)) != -1`, and I think that the `fscanf` is reading the first word, and you sir are not providing the full output. – Lahcen YAMOUN Apr 07 '20 at 18:04
  • Style guide: create global variables as a last resort. They are sometimes necessary, but none of the ones in your code should be global. When they are outside a function, make them `static` unless there's another source file that also needs to access them (do that with functions, too). When a variable or function must be accessed in more than one file, create a header to declare them, and include the header in both the file that defines the variables and functions and in the file(s) that use them. You'd then note that `items` and `chars` are unused variables. You must initialize `item` too. – Jonathan Leffler Apr 08 '20 at 02:30
  • If you're ensuring no leaks, remember to `free(line);` after the loop. It's quite normal for `getline()` to allocate memory even if the first call ends up detecting EOF. – Jonathan Leffler Apr 08 '20 at 03:25

1 Answers1

4

but it's just creating an infinite loop.

This line of your code: while ((read = getline(&line, &len, list)) != 1)

Unless a line contains 1 character (just a newline), it will be an infinite loop. The POSIX getline() function will return -1 (not EOF, even though EOF is usually -1) when the file is completely read. So change that line to:

while ((read = getline(&line, &len, list)) != -1)

But, I don't see you using the value of read inside that loop, so this would be better:

  • Fix 1: while (getline(&line, &len, list) != -1)

And inside that loop, I see: printf("%d. %s", item, line);

You might find very old implementations of getline() that don't include the newline, in which case, if you want your output in separate lines, you need to put a \n:

  • Fix 2: printf("%d. %s\n", item, line);

However, if you use a more modern implementation, it will preserve the newline in accordance with the POSIX specification.

Also, if the very last 'line' in the file is not terminated with a newline, you might still want to add one. In that case, you could keep the read length and use that to detect whether there is a newline at the end of the line.

Also it seems to be skipping the first word for some reason

Because of int letterCount = fscanf(list,"%s",chars);

That fscanf reads the first word of your file. Now the file pointer is at that position (end of the first word) and further reading of the file will happen from that place.

So, reposition the file pointer to the beginning of the file after reading the first word from the file:

  • Fix 3:

    int letterCount = fscanf(list,"%s",chars);
    fseek(list, 0, SEEK_SET); // <-- this will reposition the file pointer as required
    
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
  • 1
    Note that POSIX [`getline()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html) returns `-1`, not `EOF`, when it reaches the end of input. I don't know why — it's silly. But it's so. – Jonathan Leffler Apr 08 '20 at 01:34
  • @JonathanLeffler Thanks for the info. I don't know much about that, but I guess `EOF` might be `-1` on POSIX? – Ardent Coder Apr 08 '20 at 01:59
  • @JonathanLeffler Lol I just checked your profile, thanks for voting and commenting on my answer! – Ardent Coder Apr 08 '20 at 02:08
  • 1
    `EOF` will be negative — in practice, it is always `-1`, but in theory, someone could create a (POSIX and C) compliant system where `EOF` was `-256` — and `getline()` would still return `-1`, not `EOF`. – Jonathan Leffler Apr 08 '20 at 02:12
  • @JonathanLeffler I see. I don't know that thing in detail, feel free to edit my answer as necessary :) – Ardent Coder Apr 08 '20 at 02:14
  • @JonathanLeffler Thanks, but what about that paragraph which says about `EOF`? – Ardent Coder Apr 08 '20 at 02:21
  • 1
    Note that your 'fix 2' will give double-spaced output because `getline()` includes the newline at the end of each input line. The return value could be used to check that there is a newline there; the last line of input might not end with a newline. Also, the code should, but doesn't, check that the file was successfully opened before crashing on failure to open the file. – Jonathan Leffler Apr 08 '20 at 02:21
  • 1
    I fixed that too — it takes longer when it isn't your own material you're fixing. – Jonathan Leffler Apr 08 '20 at 02:21
  • @JonathanLeffler Now I can say that JL interacted with my answer on SO :P – Ardent Coder Apr 08 '20 at 02:25
  • 1
    YW — but it isn't all that unusual for an answer to a question tagged [tag:c]. I do quite a lot of 'interacting'. :D – Jonathan Leffler Apr 08 '20 at 02:26
  • @JonathanLeffler I don't want your name to go away from the edit lol you can remove that paragraph on *Fix 2* altogether and rename *Fix 3* to *Fix 2* if you're sure about that statement on `getline`. Because, I didn't have a `getline` on CodeBlocks and I took it from [here](https://dev.w3.org/libwww/Library/src/vms/getline.c), which didn't put that `\n` :( – Ardent Coder Apr 08 '20 at 02:38
  • 1
    That's a very old (1991, 1994) implementation of `getline()` intended for VMS. You can find more modern implementations online: [`getline()`](https://stackoverflow.com/questions/735126/are-there-alternate-implementations-of-gnu-getline-interface), or [`getline()`](https://opensource.apple.com/source/cvs/cvs-19/cvs/lib/getline.c), or [`getline()`](https://code.woboq.org/userspace/glibc/stdio-common/getline.c.html) which needs [`_IO_getdelim()`](https://code.woboq.org/userspace/glibc/libio/iogetdelim.c.html#_IO_getdelim). etc. It's unfortunate that you picked a deviant implementation. – Jonathan Leffler Apr 08 '20 at 02:52
  • @JonathanLeffler Wow, I got to learn many things after this chat! Please edit it and let your name stay there :D Or I can do it if I'm wasting your time, sorry for this extended discussion. – Ardent Coder Apr 08 '20 at 02:54
  • @JonathanLeffler Cool, that's a brilliant edit! I don't have anything to thank you, but feel free to reach me here in case I could help you with something :) – Ardent Coder Apr 08 '20 at 02:58
  • I've added your fixes but line is still returning 18446744073709551615 repeatedly instead of -1? I initialised this as a signed variable so I'm not sure why it's returning this instead of -1. – bavies Apr 08 '20 at 16:17
  • @bavies Could you please explain in detail? It's working fine for me. Under what circumstances is your code failing? Note that you should first check if the file was opened successfully. – Ardent Coder Apr 08 '20 at 17:04
  • @ArdentCoder it's returning the first word now however after the first 3 items it is returning an endless list of 18446744073709551615 i.e. 4. 18446744073709551615, 5. 18446744073709551615, 6. 18446744073709551615 etc. The file was definitely opened successfully as it prints the first 3 items (as in the original post) – bavies Apr 08 '20 at 23:56
  • @bavies That was a part of your original problem, didn't you apply those fixes carefully? Especially, changing that `1` to `-1` or `EOF`. – Ardent Coder Apr 09 '20 at 09:07