Mario Mateas
Mario Mateas

Reputation: 25

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.

Upvotes: 1

Views: 64

Answers (1)

David Ranieri
David Ranieri

Reputation: 41026

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);

Upvotes: 1

Related Questions