Search code examples
cfilereallocstrtokqsort

Reading from lines with strtok + qsort problem


What I wanted to create was a program that takes lines from an external file of the form {key: value}. For example, we have the file t.dat:

{myName: Mario}
{name2: Asdadas}
{someOtherData: _D123}

My program should have ordered this data in a decreasing manner based on the length of the key (in our case, myName, name2, or someOtherData) and if two keys with the same length are found, they should be ordered lexicographically based on the value.

I did this by using a struct array that will keep data for every line from the document:

typedef struct Line{
    char key[50];
    char value[50];
}Line;

and tried to take that data out from the file by using fgets (to take every line) and strtok.

This is my entire code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 512

typedef struct Line{
    char key[50];
    char value[50];
}Line;

int comp(const void* a, const void* b)
{
    const Line *aa = a;
    const Line *bb = b;
    puts(bb->key);
    if (strlen(aa->key) == strlen(bb->key)) {
        return strcmp(aa->key, bb->key);
    }
    return  strlen(bb->value)-strlen(aa->value);
}

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        printf("Invalid number of args.\n");
        return -1;
    }
    FILE *f = fopen(argv[1], "rb");
    if (!f)
    {
        printf("Unable to open the specified file.\n");
        return -2;
    }
    
    Line* ln;

    char buff[MAX];
    int lineNumber = 0;
    int isSet = 0;
    int i = 0;
    while (fgets(buff, MAX, f))
    {
        char *p = strtok(buff, " {}:\n\r\t");
        while (p)
        {
            char word[MAX] = "";
            if (isSet == 0)
            {
                ln = malloc(1*sizeof(ln));
                isSet = 1;
            }
            else if (i == 0) ln = (Line*)realloc(ln, (lineNumber+1)*sizeof(ln));
            word[0] = '\0';
            if (i == 0) {
                strcpy(word, p);
                strcpy(ln[lineNumber].key, word);
                i = 1;
            }
            else if (i == 1) {
                strcpy(word, p);
                strcpy(ln[lineNumber].value, word);
                lineNumber++;
                i = 0;
            }
            p = strtok(NULL, " {}:\n\r\t");
        }

    }

    qsort(ln, lineNumber, sizeof(ln), comp);
    puts("\n");
    for (int i = 0; i<lineNumber; i++)
    {
        printf("%s\n", ln[i].key);
    }
    return 0;
}

The problem is, the data from the first line isn't read correctly (I'm referring to value - "Mario". It contains elements from the key, but certainly not the word Mario). Thought this may be from the strtok, but didn't find a solution for it.

Also, the data is not ordered properly using the provided comp function. It isn't ordered at all. The output is the same as it was before the order.

What can I do? Thank you. If any more details are needed, please tell me and I'll make sure I'll post it.


Solution

  • The problem is

    ln = (Line*)realloc(ln, (lineNumber+1)*sizeof(ln));
    

    you want to reserve spaces for n elements, not for n pointers to element, switch to

    ln = realloc(ln, (lineNumber+1)*sizeof(*ln)); // Don't cast
    

    also, always use a temporary variable with realloc:

    Line *temp = realloc(ln, (lineNumber+1)*sizeof(*ln));
    
    if (temp == NULL)
    {
        // raise error
    }
    ln = temp;
    

    Same here

    qsort(ln, lineNumber, sizeof(ln), comp);
    

    each element occupies sizeof(*ln), not sizeof(ln), switch to

    qsort(ln, lineNumber, sizeof(*ln), comp);