0

I am trying to read a text file with the following structure:

Item 1 - 50, Item 2 - 400, Item 3 - 120, Item 4 - 15;

and while looping over the file, I want to store the price and the item name in two strings. To make it more clear, I have int credit which I want to compare with the price and if the credit is bigger I'm printing the price.

I managed to print all the products with this function :

int credit = 1000; //For the example purpose
int displayProducts(){

    int row=0;
    char line[MAX_LINE_SIZE + 1]; // ptr to the current input line
    FILE *fp;

    fp = fopen("machinedata.txt", "r");
    if (fp == NULL)
    {
        printf("Error while opening the file.\n");
    //  exit(EXIT_FAILURE);
    }

    while (fgets(line, MAX_LINE_SIZE, fp)) {
        char *next_item;  // ptr to the next item extracted from the current line
        char* name = NULL;
        int price;

        next_item = strtok(line, " ,");

        while (next_item != NULL){
            printf("%s\n", next_item);
            next_item = strtok(NULL, " ,");
            /*WHAT CAN I DO HERE TO EXTRACT EACH PRICE AND COMPARE WITH CREDIT??*/
        }
    }

    fclose(fp);
    return 1;
}

I am taking first steps in C language and I just can't figure out how to do that. Any help please?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Donoven Rally
  • 1,670
  • 2
  • 17
  • 34
  • Welcome to Stack Overflow. Please read the [About] page soon. Is the data in the file really all on one line? Is there a significance to the commas marking the end of the first items and the semicolon marking the end of the last? Does semicolon mark end of line, end of data, something else? Note that you must do something other than try the `fgets()` call if you fail to open the file; `return 0;` might be appropriate given that you have `return 1;` at the end on success. – Jonathan Leffler May 20 '15 at 23:21
  • hi there, the file is all in one line. commas representing end of item and semicolon represents end of file – Donoven Rally May 20 '15 at 23:32
  • Also, is the word "Item" of significance, or is it constant text? Is the input 'product name', dash, price, comma/semicolon end marker? – Jonathan Leffler May 20 '15 at 23:32

1 Answers1

0

Having read a line as you are doing, you need to split it into item entries. Since you're using strtok(), I assume that you don't need to identify the delimiter. Also, one of the reasons for disliking strtok() and preferring Microsoft's strtok_s() on Windows and POSIX strtok_r() everywhere else is that you can't have nested calls to strtok(), but you need them. Or you need to use scanf(), or some other technique.

Warning: none of this code has been near a compiler!

char *next_ptr = NULL;
char *next_item = strtok_r(line, ",;", &next_ptr);

while (next_item != NULL){
    char *item_ptr = NULL;
    char *name = strtok_r(next_item, "-", &item_ptr);
    if (name == NULL)
    {
        fprintf(stderr, "Failed to scan name out of [%s]\n", next_item);
        break;
    }
    int price;
    next_item = strtok_r(NULL, " ,", &item_ptr);
    assert(next_item != NULL);
    if (sscanf(next_item, "%d", &price) != 1)
        fprintf(stderr, "Failed to convert [%s] to integer\n", next_item);
    else
        printf("Item: [%s] price %d\n", name, price);
    next_item = strtok_r(NULL, ",;", &next_ptr);
}

The break on error is because I'm being lazy. Ideally, you should continue to the next item in the line, but that involves rewriting the loop as a for loop:

for (next_item = strtok_r(line, ",;", &next_ptr);
     next_item != NULL;
     next_item = strtok_r(NULL, ",;", &next_ptr))
{
    ...body of loop except for final call to strtok_r()...
}

Now you can use continue and the next_item = strtok_r(NULL, ",;", &next_ptr) statement will be executed, and that's crucial. You might need to worry about an empty item after the semicolon; add && *next_item != '\0' to the loop condition.

If you can't use strtok_r() or strtok_s() for some reason (what?), then you can consider using:

char name[30];
int price;
if (sscanf(next_item, "%29[^-] - %d", name, &price) != 2)
{
    fprintf(stderr, "Failed to extract item name and price from [%s]\n", next_item);
    continue; // Or break!
}

And there are other options too — many of them, in fact.

Note that strtok() and its relatives all (a) destroy the input string and (b) do not tell you which character marked the end, and (c) treats multiple separators as one, so you can't spot empty items between two delimiters (which isn't a problem in this example). The destruction is chopping the original string into shorter substrings by inserting null bytes where the delimiter was found.

Note that if an item name contains a dash, you're hosed with this system. You'll interpret the 'Vacuum-24' as being an item named 'Vacuum' with 24 as the price. You can work around that, too, but it is harder (think strstr() to find " - " (blank, dash, blank) as a separator between item name and price, for example).

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