Search code examples
cfiletextmallocargv

Count number of integers in text file in C


I made text file called qsort.txt and wrote arbitrary many integers in file (In my case 35 integers to be precise). My goal is to count how many integers are in that file, put them into an array defined by malloc() and sort them using qsort(). After that I want sorted numbers writen into a text file called sorted.txt.

This is my code, but obviously doesn't work as expected. Also I want to be more flexible with code so in FILE *fa = fopen(argv[1], "r+") is argv[1] so I can put any other text file to be sorted. Anyway my problem is that I don't know how to count how many integers are in file.

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

int compare(const void *a, const void *b) {
    if (*(int*)a < *(int*)b)
        return -1;
    else if (*(int*)a > *(int*)b)
        return 1;
    else
        return 0;
}

int main(int argc, char **argv) {

    FILE *fa = fopen(argv[1], "r+");
    if (!fa)
        exit(1);
    FILE *fb = fopen("sortirano.txt", "w+");

    long duljina, i;
    long *ptr, *zapis;
    long count = 0;

     while (!feof(fa))
         count++;

     printf("%lu\n", count);

     fseek(fa, 0, SEEK_END);
     duljina = ftell(fa);
     fseek(fa, 0, SEEK_SET);

     ptr = (long *)malloc(sizeof(long));

     while (!feof(fa)) {
         fscanf(fa, "%lu", ptr);
         count++;
     }
     printf("count: %lu\n", count);
     for (i = 0; i < count; i++)
         printf("%lu ", ptr[i]);

    printf("\n");

    free(ptr);

    fclose(fa);
    fclose(fb);

    return 0;
}

EDIT:

This is my new code, which is simpler:

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

int compare(const void *a, const void *b) {
    if (*(long*)a > *(long*)b)
        return 1;
    if (*(long*)a < *(long*)b)
        return -1;
    else
        return 0;
}

int main() {
    FILE *fa = fopen("qsort.txt", "r"); assert(fa != NULL);
    FILE *fb = fopen("sorted.txt", "w+"); assert(fa != NULL);

    long *ptr;
    long save;
    long count = 0;
    long i;
    long k;

    while ((fscanf(fa, "%ld", &save)) == 1) //number of elements
        prebroji++;

    printf("count: %ld\n", count); //checking how many elements i have, just to make sure it works ok

    ptr = (long *)malloc(count * sizeof(long)); //mallociranje

    for (i = 0; i < count; i++)
        fscanf(fa, "%ld", &ptr[i]);

    for (i = 0; i < count; i++) //checking if numbers were saved at malloc array
        printf("%ld ", ptr[i]);

    qsort(ptr, count, sizeof(long), compare);

    for (i = 0; i < count; i++) //checking if sorted correctly
        printf("%ld ", ptr[i]);

    for (i = 0; i < count; i++)
        fprintf(fb, "%ld", ptr[i]);

    printf("\n");

    free(ptr);
    fclose(fa);
    fclose(fb);

    return 0;
}

But it doesn't work: all I got are zeros printed.


Solution

  • Since the file only contains integers and whitespace, you can parse it with fscanf("%ld", ...) and store the numbers into an array you reallocate on purpose as you read more numbers.

    Note these remarks:

    • Your comparison function is OK for use with qsort.

    • There is no need to open the files for update with + in the mode string,

    • nor is there any need for seeking to the end of file.

    • testing for end of file with while(!feof(fa)) is always wrong. You should instead test for fscanf() success and reallocate the array as needed.

    Here is a modified version:

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int compare(const void *aa, const void *bb) {
        const long int *a = aa;
        const long int *b = bb;
        return (*a > *b) - (*a < *b);
    }
    
    int main(int argc, char **argv) {
        if (argc < 2) {
            fprintf(stderr, "missing argument\n");
            return 1;
        }
        FILE *fa = fopen(argv[1], "r");
        if (!fa) {
            fprintf(stderr, "cannot open %s: %s\n", argv[1], strerror(errno));
            return 1;
        }
    
        long *ptr = NULL;
        size_t alloc = 0, count = 0;
        long value;
    
        while (fscanf(fa, "%ld", &value) == 1) {
            if (count == alloc) {
                size_t new_alloc = alloc + alloc / 2 + 32;
                long *new_ptr = realloc(ptr, sizeof(*ptr) * new_alloc);
                if (!new_ptr) {
                    fprintf(stderr, "out of memory for %zu entries\n", new_alloc);
                    free(ptr);
                    fclose(fa);
                    return 1;
                }
                ptr = new_ptr;
                alloc = new_alloc;
            }
            ptr[count++] = value;
        }
        fclose(fa);
    
        printf("count: %zu\n", count);
    
        qsort(ptr, count, sizeof(*ptr), compare);
    
        const char *outfile = "sorted.txt";
        FILE *fb = fopen(outfile, "w");
        if (!fb) {
            fprintf(stderr, "cannot open %s: %s\n", outfile, strerror(errno));
            free(ptr);
            return 1;
        }
    
        for (size_t i = 0; i < count; i++) {
            fprintf(fb, "%ld\n", ptr[i]);
        }
    
        fclose(fb);
        free(ptr);
    
        return 0;
    }
    

    EDIT:

    You new code has multiple problems:

    • there is no need to open sorted.txt in update mode, just use "w".

    • you should assert(fb) after opening the second file

    • prebroji should be count

    • you should add assert(ptr) after you allocate the array

    • you must add rewind(fa); after you allocate the array to re-read the file from the beginning. This explains why you get all values as 0 because you do not test the return value of fscanf() that returns 0 or EOF for every try.

    • it is less brittle to write qsort(ptr, count, sizeof(*ptr), compare); to avoid size inconsistencies if you change the type of the array.

    • similarly, writing ptr = malloc(count * sizeof(*ptr)) is more reliable as it remains consistent should you change the type of ptr.

    • you should add a separator in fprintf(fb, "%ld", ptr[i]) to separate the numbers you write into the output file.