Question

i'm trying to read strings from a file and into a struct but when i reach strings with two or more words everything i seem to try does not work

data in file

"K300" "Keyboard" "US Generic" 150.00 50

"R576" "16-inch Rims" "Toyota Verossa" 800.00 48

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

typedef struct partInfo {
  char number[6];
  char name[20];
  char description[30];
  double price;
  int qty;
}Part;

int main() {

char num[6], name[20], desc[30];
int i=0;
int q;
double p;
char ch;


FILE * in = fopen("input.txt", "r");

Part part1;
fscanf(in, " %[^ ]s", &num);
printf("%s\n", num);

fscanf(in, " %[^ ]s", &name);
printf("%s\n", name);

fscanf(in, " %[^ ]s", &desc); //right here only copy "US and not the Generic"
printf("%s\n", desc);

strcpy(part1.number, num);
strcpy(part1.name, name);
strcpy(part1.description, desc);

fclose(in);
return 0;
}

however when i try to use

 fscanf(in, " %[^\n]s", &desc); 

it copies the rest of the line i've been stuck on this for two days can someone please help me and also how to get rid of the double quotes if that is possible i tried a different set of code for that and more errors arise :(

Was it helpful?

Solution

In scanf, the expression %[chars] reads the longest string that contains the characters (or character ranges) in the bracket. A caret as first character reverses this: %[^chars] reads the longest string that does not contain any of the characters. Hence, %[^ ] reads stuff up to the next space, and %[^\n] reads stuff up to the next new line.

In your case, where the string is delimited by double quotes, you should read the opening quote, then stuff up to the next quote and finally the closing quote:

res = fscanf(in, " \"%[^\"]\"", name);

This format starts with a space and so discards white space before the first quote. The format string looks ugly because the double quote itself is escaped. To illustrate, this is how the command would look like if your strings were delimited by single quotes.

res = fscanf(in, " '%[^']'", name);

This approach works only if your strings are always enclosed in quotes, even if they don't have spaces.

It is probably cleaner to read a whole line with fgets and then sscanf from that line to catch unmatched quotes. That way you could also scan the line several times - once for a string with quotes, a second time for an unquoted string, say - without accessing the disk more than once.

Edit: Corrected the format syntax, which containes a spurious s and updated the description of the bracket syntax for strings in the first paragraph.

Edit II: Because the OP seems to be confused about how fscanf works, here's a small example that reads parts from a file line by line:

#define MAX 10
#define MAXLINE 240

int main(int argc, char *argv[])
{
    FILE *in;
    int nline = 0;

    Part part[MAX];
    int npart = 0;
    int res, i;

    in = fopen(argv[1], "r"); // TODO: Error checking

    for (;;) {
        char buf[MAXLINE];
        Part *p = &part[npart];

        if (fgets(buf, MAXLINE, in) == NULL) break;
        nline++;

        res = sscanf(buf, 
            " \"%5[^\"]\" \"%19[^\"]\" \"%29[^\"]\" %lf %d", 
            p->number, p->name, p->description, &p->price, &p->qty);

        if (res < 5) {
            static const char *where[] = {
                "number", "name", "description", "price", "quantity"
            };

            if (res < 0) res = 0;
            fprintf(stderr, 
                "Error while reading %s in line %d.\n",
                where[res], nline);
            break;
        }

        npart++;
        if (npart == MAX) break;
    }
    fclose(in);

    // ... do domething with parts ...

    return 0;
}

Here, the line is read in forst from the file. Then, that line (buf) is scanned for the required format. Of course, sscanf must be used instead of fscanf here. On error, a simple error message is printed. This message includes the line number and the field entry where reading went wrong, so the error can be located in the input file.

Note how the sscanf includes maximum field lengths to avoid overflowing the string buffers of the part. A scanning error occurs when the quoted string is too long. It would be nicer to have sscanf read all characters and only store the first 5, say, but that's not how sscanf works. Such a solution requires another approach, probably a custom scanning function.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top