2

Problem File:

[well lol], [wtf bro? 24], [0183188383], [3000.000000], [4000.000000], [12/12/2012]
[chow hai], [pukima jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]

Code:

typedef struct Customer {
    char name[50];
    char billing_address[100];
    char phone_number[15];
    double amount_paid;
    double amount_due;
    char date[20];
} Customer;

Customer customer;

FILE *file = fopen("customers.txt", "ab+");

while (!feof(file)) {
    fscanf(file, "[%s], [%s], [%s], [%lf], [%lf], [%s]\n", &customer.name, &customer.billing_address, customer.phone_number, &customer.amount_paid, &customer.amount_due, customer.date);
}

Problem:

In the above code, what I'm trying to do is parse each record value into the appropriate field in the Customer structure. Now we do know that "%s" will not read spaces. How would I read a value like "well lol" in the record since %s will not work.

Why is it not a duplicate?

I need to parse the entire line, and not just accept one value after the other as the supposedly duplicate answer.

Update on why it's not a duplicate:

while (!feof(file)) {
        fscanf(file, "[%[^\\n]], [%[^\\n]], [%[^\\n]], [%lf], [%lf], [%[^\\n]]\n", customer.name, customer.billing_address, customer.phone_number, &customer.amount_paid, &customer.amount_due, customer.date);

    printf("%s", customer.billing_address);
    if (strcmp(customer.name, search) == 0) {
        printf("FOUND!!!");
    }
}

I've updated the code as told, but my output is still wrong. I believe there's something else wrong. Output:

SEARCH A CUSTOMER PROFILE
=========================
Customer Name: a
���
codez
  • 1,440
  • 2
  • 19
  • 34
  • I've explained why my question is not a duplicate. – codez Jul 18 '18 at 16:02
  • 4
    Note that [`while (!feof(file))` is always wrong](https://stackoverflow.com/questions/5431941/) – Jonathan Leffler Jul 18 '18 at 16:15
  • I've updated on the issue again. – codez Jul 18 '18 at 16:17
  • also check how many fields you have read by checking value of `fscanf` (the answer is probably 0 here) – Jean-François Fabre Jul 18 '18 at 16:18
  • @Jean-FrançoisFabre How would I be able to check that? I'm kinda new. – codez Jul 18 '18 at 16:18
  • Every field I try to output has the same weird output. – codez Jul 18 '18 at 16:20
  • Johnatan answer shows how to check that (compares return value to 6). – Jean-François Fabre Jul 18 '18 at 16:23
  • No — we do not want or need to see all the rest of your program. – Jonathan Leffler Jul 18 '18 at 16:31
  • If you want to parse the input, you should avoid `scanf`. In point of fact, "you should avoid `scanf`" is always true. – William Pursell Jul 18 '18 at 16:33
  • @HassanAlthaf Curious, who or what text suggested `while (!feof(file)) {`? – chux - Reinstate Monica Jul 18 '18 at 16:35
  • @chux My lecturer at college. – codez Jul 18 '18 at 16:36
  • @HassanAlthaf Thanks. Note that _in this case_, `while (!feof(file)) {` is almost OK as the `...\n"`at the end of the format will make your code "work" - ususally. Tip: Don't ever do this for production code and know that lecturers are fallible. – chux - Reinstate Monica Jul 18 '18 at 16:39
  • @chux Haha, I'm aware that lecturers teach very outdated stuff. But when your deadlines are tight, you need to somehow make it work. *Welcome to College....* If it was a higher-level language, I wouldn't have had much problems, but C is just too different from other higher-level languages like Java, C#, Python, etc... – codez Jul 18 '18 at 16:48
  • @HassanAlthaf Tip: a real challenge is handling what code should do if unexpected data occurs. How to detect it and how to gracefully cope with it? The `while (!feof(file)) {` makes proper error detection/handling very difficult. A significant difference from the "old days" is the increased prevalence of hostile data (hackers) who destroy code like this. [Tread carefully](https://stackoverflow.com/questions/51406409/how-do-i-read-a-string-enclosed-within-square-braces-in-a-file-in-c-with-each-li#comment89785156_51406779) when speeding with those deadlines - often inefficient. – chux - Reinstate Monica Jul 18 '18 at 17:01

1 Answers1

3

You need to use 'scan sets', which are designated by %[…] in the format string. You need to be careful here since the data also contains square brackets, and you want to match a negated character set — anything that isn't a close square bracket. The basic idea is illustrated by:

while (fscanf(file, " [%[^]]], [%[^]]], [%[^]]], [%lf], [%lf], [%[^]]]",
              customer.name, customer.billing_address, customer.phone_number,
              &customer.amount_paid, &customer.amount_due, customer.date) == 6)
{
    …good data to process…
}

The %[^]] means 'scan set — negated — first character is ] — end of scan set. The code checks that 6 values were read, stopping if there was a problem (or EOF). Note that while (!feof(file)) is always wrong.

Using commas as well as the square brackets in the data isn't really necessary; the square brackets alone would be sufficient.

Converting this into an MCVE:

#include <stdio.h>

typedef struct Customer {
    char name[50];
    char billing_address[100];
    char phone_number[15];
    double amount_paid;
    double amount_due;
    char date[20];
} Customer;

int main(void)
{
    Customer customer;
    FILE *file = stdin;   //fopen("customers.txt", "ab+");

    while (fscanf(file, " [%49[^]]], [%99[^]]], [%14[^]]], [%lf], [%lf], [%19[^]]]",
                  customer.name, customer.billing_address, customer.phone_number,
                  &customer.amount_paid, &customer.amount_due, customer.date) == 6)
    {
        printf("Data: <<%s>> <<%s>> <<%s>> %10.2f %10.2f <<%s>>\n",
                customer.name, customer.billing_address, customer.phone_number,
                customer.amount_paid, customer.amount_due, customer.date);
    }

    return 0;
}

Note that in this version, I've added overflow protection — the numbers in the formats like %49[^]] prevent overflows. The 'off by one' on length is deliberate and necessary (and a nuisance, but hallowed by antiquity and the standard which followed the precedents of the ancient ones who gave us the standard I/O library).

The blank at the start of the format is not an accident. Three formats don't skip leading white space: %c, %[…] and %n. Putting the white space at the front gives a better user experience if the input ever comes from a terminal rather than a file. (See scanf() leaves the newline in the input stream (amongst other questions) for more information.)

This reads from standard input instead of a named file. When compiled from scan13.c into scan13 and run on the sample data, it produces:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror scan13.c -o scan13
$ cat data
[well lol], [wtf bro? 24], [0183188383], [3000.000000], [4000.000000], [12/12/2012]
[chow hai], [pukima jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
$ scan13 < data
Data: <<well lol>> <<wtf bro? 24>> <<0183188383>>    3000.00    4000.00 <<12/12/2012>>
Data: <<chow hai>> <<pukima jalan>> <<6969696969>>    6969.00 6969699.00 <<6/9/1969>>
$
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • @JohathanLeffler: is the space at start intentional? – Jean-François Fabre Jul 18 '18 at 16:23
  • @Jean-FrançoisFabre: yes; it is infinitely better than the newline at the end, especially if the input will ever come from a terminal. It skips leading white space, newlines left over from previous input, etc. Three formats don't skip white space in `scanf()`: `%c`, `%[…]`, and `%n`. – Jonathan Leffler Jul 18 '18 at 16:25