0

I'm struggling with importing lines from a text file into a C program. Below is the text I'm trying to import:

1000|Larry Normal|CELL|3048005191
1001|Shelly Nopers|CELL|3048005191
1002|Shelly Schnats|HOME|3649155831
1003|Terry Crews|HOME|3932281123

Delimited by | and with spaces in names.

Here's the code I'm using below:

    FILE* patientFile = NULL;

    int patientNum;
    char patientName[15];
    char phoneDescription[5];
    char phoneNumber[11];

    patientFile = fopen("patientData.txt", "r");

    if (patientFile != NULL)
    {
        while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", &patientNum, patientName, phoneDescription, phoneNumber ) != EOF)
        {
            printf("%d %s %s %s\n", patientNum, patientName, phoneDescription, phoneNumber);
        }


        fclose(patientFile);
    }
    else
    {
        printf("Failed to open patients file\n");
    }

While the file can be read, I'm getting this as my output:


1024 Larry  ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry  ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry  ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry  ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
1024 Larry  ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠

Repeating forever. Can anyone offer insight for where I'm going wrong?

  • 1
    Your loop should be `while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", &patientNum, patientName, phoneDescription, phoneNumber ) == 4)`. You don't really care if you've hit the end of the file, but you do care if not all of the arguments were converted. You probably don't want the `\n` in the format string. You might consider reading the file line by line with `fgets` and using `sscanf` to parse it. That would allow you to process all lines that parsed correctly but skip ones that did not. – Retired Ninja Dec 09 '22 at 01:31
  • Hey Retired Ninja, Thanks for the reply! I tried changing it to == 4 instead of EOF at the end, and now I have a new problem - it doesn't seem to read through the file at all. Any advice? – user3385214 Dec 09 '22 at 01:40
  • The `%14s` specifier will only match a single word. It will not match `Larry Normal`, it will only match `Larry`. You may want to use `%14[^|]` instead, in order to match everything up to the `|`. – Andreas Wenzel Dec 09 '22 at 01:48
  • 1
    Yes, now you know that you aren't parsing any lines properly. A format string of `"%d|%14[^|]|%4[^|]|%10s"` should work but is fragile and will break if any of the fields are too long. I would read the line, use `strtok` to split it into pieces, and then copy the values from those pieces with `strncpy` to limit the length to destination width - 1 and manually ensure the last destination character was a 0. – Retired Ninja Dec 09 '22 at 01:52

2 Answers2

0

"%s" insufficient to read a name

"%14s" will only read and save "Larry" of "Larry Normal". Instead use "14s[^|]" to read to a "|".

Code checks against only 1 undesired return value

As fscanf() may return values other than EOF and 4, rather than check against one of the undesired return values, check against the desired one.

 // while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", ...) != EOF)
 while (fscanf(patientFile, "%d|%14s|%4s|%10s\n", ...) == 4)

Advanced: Better end-of-line detection via fgets()

"%d", "%s" ignore and consume leading white-space including '\n'. Better to read a line into a buffer and then parse.

// "%d|%14s|%4s|%10s\n"
#define LINE_SIZE (11 + 1 + 14 + 1 + 4 + 1 + 10 + 1 + 1)
char buf[LINE_SIZE * 2]; // Do not be stingy on potential line sizes.

while (fgets(buf, sizeof buf, patientFile) && 
    sscanf(buf, "%d|%14[^|]|%4[^|]|%10[^\n]", 
    &patientNum, patientName, phoneDescription, phoneNumber) == 4) {
  printf("%d %s %s %s\n", patientNum, patientName, phoneDescription, phoneNumber);
}

Robust code would also check for trailing text after the phone number, excessive long lines, etc.

Perhaps also start checking for acceptable text like "%10[^0-9#*]" instead of "%10[^\n]" for the phone number. See also longest possible worldwide phone number.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
-2

Try this pattern "%d|%[^|]|%[^|]|%s\n" instead. [^|] means read all characters till the |. %s will not work cuz | is not white-space.

Ref: https://cplusplus.com/reference/cstdio/fscanf/

S Dao
  • 555
  • 4
  • 7