Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
138 views
in Technique[技术] by (71.8m points)

How to print sorted lines from file using array of structures in C

I have the following .txt file:

1 7 9 12
8 4 22 5
4 9 0 10
15 2 3 14

I wanna print the lines in descending order based on the average value on each individual line.

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

#define MAX 300

int comp(const void *p, const void *q) {
    float l = *(const float *)p;
    float r = *(const float *)q;
    return (r - l);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Invalid format.
");
        return -1;
    }
    FILE *fin = fopen(argv[1], "r");
    if (!fin) {
        printf("Error opening the file.
");
        return 1;
    }
    char buf[MAX];
    int n, countLines = 0;
    float *avg;
    while (fgets(buf, MAX, fin)) {
        avg = realloc(avg, (countLines + 1) * sizeof(float *));
        float sum = 0;
        float countWords = 0;
        char *word = strtok(buf, " ");
        while (word) {
            countWords++;
            //printf("Word : %s
", word);
            n = atoi(word);
            sum = sum + n;
            word = strtok(NULL, " ");
        }
        avg[countLines] = (float)(sum / countWords);
        countLines++;
    }
    qsort(avg, countLines, sizeof(float), comp);
    for (int i = 0; i < countLines; i++) {
        printf("%f
", avg[i]);
    }
    free(avg);
    fclose(fin);
    return 0;
}

Output so far:

9.750000
8.500000
7.250000
5.750000

So I'm getting the right values, I just need to figure out how to print the actual lines, not the values. I know I'm supposed to use an array of structures but I don't really know how to do that. Any ideas? Thanks in advance

question from:https://stackoverflow.com/questions/65869845/how-to-print-sorted-lines-from-file-using-array-of-structures-in-c

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your algorithm to sort the array of float values is flawed: you return the difference of the float values, which is converted to int, which uses rounding, hence may produce an incorrect result.

The comparison function should be changed to:

int comp(const void *p1, const void *p2) {
    float f1 = *(const float *)p1;
    float f2 = *(const float *)p2;
    return (f1 > f2) - (f1 < f2);
}

There are other problems;

  • avg should be initialized to NULL.

To print the lines, you should use an array of structures with the average and a copy of the line:

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

#define MAX 300

typedef struct {
    double avg;
    int lineno;
    char *line;
} element;

int comp(const void *p1, const void *p2) {
    const element *e1 = p1;
    const element *e2 = p2;
    if (e1->avg == e2->avg) {
        // keep lines with an identical average in the original order
        return (e1->lineno > e2->lineno) - (e1->lineno < e2->lineno);
    } else {
        return (e1->avg > e2->avg) - (e1->avg < e2->avg);
    }
}

double compute_avg(const char *s) {
    double sum = 0;
    int count = 0;
    for (;;) {
        char *p;
        double val = strtod(s, &p);

        if (p == s)  // no more numbers
            break;
        sum += val;
        count++;
        s = p;
    }
    if (count > 0)
        return sum / count;
    else
        return HUGE_VAL;   // sort empty lines at the end
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Invalid format.
");
        return -1;
    }
    FILE *fin = fopen(argv[1], "r");
    if (!fin) {
        printf("Error opening file %s.
", argv[1]);
        return 1;
    }
    char buf[MAX];
    int countLines = 0;
    element *array = NULL;
    while (fgets(buf, MAX, fin)) {
        buf[strcspn(buf, "
")] = '';  // strip the trailing newline if any
        array = realloc(array, (countLines + 1) * sizeof(*array));
        array[countLines].line = strdup(buf);
        array[countLines].lineno = countLines;
        array[countLines].avg = compute_avg(buf);
        countLines++;
    }
    fclose(fin);
    qsort(array, countLines, sizeof(*array), comp);
    for (int i = 0; i < countLines; i++) {
        printf("%f: %s
", array[i].avg, array[i].line); // debug
        //printf("%s
", array[i].line);
        free(array[i].line);
    }
    return 0;
}

Notes:

  • for (;;) is an infinite loop: a for loop with no test. You could also write while (1) but 1 looks confusingly similar to l. This syntax is used in many languages who took it from C: C++, java, javascript, C#...
  • return HUGE_VAL; returns the maximum value of type double, defined in <math.h>. In most modern systems, this value is +Infinity. (INF was a mistake). Storing this value as the average for empty lines will sort the at the end.
  • array = realloc(array, (countLines + 1) * sizeof(*array)); uses sizeof(*array) instead of sizeof(element) because it is more generic: taking the size of the array element is always correct and you do not need to update the code if the type of array changes.
  • (f1 > f2) - (f1 < f2); is expression that evaluates to -1, 0, 1 respectively if f1 < f2, f1 == f2 and f1 > f2, assuming neither f1 nor f2 are NaN values. The comparison operations in C have type int and values 1 for true and 0 for false.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...