0

Hey all I had a quick question regarding reading in information from a text file. I have a formatted .txt with customer information in the file. The file contains multiple customers, each with a different order type, product purchased, address, city, state, total cost, etc. When I used fscanf() it returned no values, and in the debugger all the values that were assigned to my variables were incorrect.

Here is the data from the text file:

0
Programming With C
Larry Page
1600 Amphitheatre Parkway
Mountain View
California
94043
81.07

1
Data Structures and Algorithms in Java
Elon Musk
3500 Deer Creek Road
Palo Alto
California
94304
32.50

2
Computer Science Distilled
James Gosling
1234 Main Dr.
San Francisco 
California
94122
35.70

Here is the code that I used.

int orderType, zipCode;
double totalCost;
char product[MAXSIZE], customer[MAXSIZE], address[MAXSIZE], city[MAXSIZE], state[MAXSIZE];

// Scan in the order type, product name, customer, address, city, state, zip code, order cost
fscanf(customerDataPtr, "%d", "%s", "%s", "%s", "%s", "%s", "%f", "%f");
printf("%d", "%s", "%s", "%s", "%s", "%s", "%f", "%f",
       orderType, product, customer, address, city, state, zipCode, totalCost);

Any advice would be considered very helpful.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Check the return value from `fscanf()`, and get a compiler that will complain about abuses of `fscanf()` — such as GCC. You need one format string argument with all the conversions in a single string, and then the pointer arguments where the data will be stored. Similarly with `printf()`. – Jonathan Leffler Nov 20 '17 at 01:44
  • An example of the input will be handy – Ed Heal Nov 20 '17 at 01:44
  • Are there blank lines where shown in the data, or are the lines continuous? It isn't a big issue (but it is an issue). It wasn't clear when I was looking at the data while editing it. – Jonathan Leffler Nov 20 '17 at 01:57
  • I cannot see a single comma in the example data. The format for `fscanf` does not match the data – Ed Heal Nov 20 '17 at 02:00

1 Answers1

4

Before the data was illustrated

Check the return value from fscanf(), and get a compiler that will complain about abuses of fscanf() — such as GCC. You need one format string argument with all the conversions in a single string, and then the pointer arguments where the data will be stored. Similarly with printf().

You have:

fscanf(customerDataPtr, "%d", "%s", "%s", "%s", "%s", "%s", "%f", "%f");
printf("%d", "%s", "%s", "%s", "%s", "%s", "%f", "%f",
       orderType, product, customer, address, city, state, zipCode, totalCost);

A more or less minimal version of what you need is:

if (fscanf(customerDataPtr, "%d%s%s%s%s%s%d%f", &orderType, product,
           customer, address, city, state, &zipCode, &totalCost) == 8)
{
    printf("%d %s %s %s %s %s %.5d %f",
       orderType, product, customer, address, city, state, zipCode, totalCost);
}
else
{
    …handle error somehow…
}

Note the use of %d to read and print the zip code.

Also note that the product must be a single word, the customer name must be a single word, the address must be a single word, the city must be a single word, and the state must be a single word. That set of restrictions is not realistic, but you should be able to get somewhere from here.

After the data is illustrated

You need to read the lines using fgets() or perhaps POSIX getline(), and then store the results appropriately. That mainly means removing the newlines, and checking for overflows, etc.

The processing is fiddlier; it isn't entirely clear what's the best approach. A simple minded approach that should work, modulo any typos in the code, is:

static int read_line(FILE *fp, size_t buflen, char *buffer)
{
    char line[4096];
    if (fgets(line, sizeof(line), fp) == 0)
        return -1;
    line[strcspn(line, "\n")] = '\0';
    if (strlen(line) >= buflen)
        return -1;
    strcpy(buffer, line);     
    return 0;
}

In some other function:

char line[4096];
FILE *fp = customerDataPtr;  // That name is too damn long!

if (fgets(line, sizeof(line), fp) == 0)
    return -1;
if (sscanf(line, "%d", &orderType) != 1)
    return -1;
if (read_line(fp, product, sizeof(product) != 0)
    return -1;
if (read_line(fp, customer, sizeof(customer) != 0)
    return -1;
if (read_line(fp, address, sizeof(address) != 0)
    return -1;
if (read_line(fp, city, sizeof(city) != 0)
    return -1;
if (read_line(fp, state, sizeof(state) != 0)
    return -1;
if (fgets(line, sizeof(line), fp) == 0)
    return -1;
if (sscanf(line, "%d", &zipCode) != 1)
    return -1;
if (fgets(line, sizeof(line), fp) == 0)
    return -1;
if (sscanf(line, "%f", &totalCost) != 1)
    return -1;
printf("%d: %s\n%s\n%s\n%s %s %.5d\n%f",
   orderType, product, customer, address, city, state, zipCode, totalCost);
return 0;

Note that there is no error reporting; the code returns -1 on error and 0 on success. You would probably want to do a lot better than that. You could encapsulate the 'integer read and assign' code into a function analogous to the read_line() function shown; you'd rename the functions appropriately. You would probably add a function to handle 'floating point read and assign' too. Note the variation in the print format to handle line based input better. You can futz with the format string to your heart's content.

I note that in theory you could play around with a complex fscanf() format string such as this, which assumes that MAXSIZE is 128:

if (fscanf(fp, "%d %127[^\n]%*c %127[^\n]%*c %127[^\n]%*c %127[^\n]%*c %127[^\n]%*c %d %f",
           &orderType, product, customer, address, city, state, &zipCode, &totalCost) == 8)

but the error recovery doesn't bear thinking about. If you're even the remotest bit tempted to use such a monstrosity, you should read the Beginnner's Guide Away From scanf().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278