Search code examples
carrayschars

Reading text file and breaking it by delimiters


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?


Solution

  • 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).