Search code examples
carraysfilesortinguser-input

How to resort an array with file input?


I am writing a program that will ask the user for a 'n' value, they will then enter 'n' values that will be stored into an array and sorted. I have this part done easy enough. I will compare this array with input from a number read from a text file. If the number is bigger than any of the current array values it will replace them and slide the rest down. This creates an array of the largest 'n' values

Example: n = 4 n values are : 999 972 954 462 937

a[4] = {999, 972, 954, 462, 937};

Sorted :

a[4] = {999, 972, 954, 937, 462};

if the file input is say 968 the result is. Resorted :

a[4] = {999, 972, 968, 937, 937};

This is my current code.

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

int main(int argc, char *argv[]){
    if (argc<3)                            //error checking
        return -1;
    int size = atoi(argv[2]);
    int a[size];
    int i, j, temp=0;
    printf("Enter %d numbers\n", size);     //user array input for size and n values
    for(i = 0; i < size; i++)
        scanf("%d", &a[i]);
    for(i=0; i < size; i++){                //sorting array
        for(j = i+1; j <size; j++){
            if( a[i] < a[j]){
                temp = a[i];
                a[i] = a[j];
                a[j] = temp; 
            }
        }
    }
    FILE *input;
    input = fopen(argv[1], "r");
    if(input ==NULL)                          //error checking
        return -1;
    if(fscanf(input, "%d", &temp) != 1)
        return -1;
    while(fscanf(input, "%d", &temp) ==1){   //loop while  there is file input
        for(i =1 < size; i++){                //check if temp is larger than array values
            if(temp > a[i] && temp < a[i-1]){      
                for(j = size-1; j >= i; j--)   //slide down the rest of the array
                    a[j] = a[j-1];
                a[i] = temp;
            }
        }
    }
    for(i=0; i <size; i++){                    //print out array
        printf("%d ", a[i]);
    }
    return (0);
}

I have tried this on a smaller simpler skill were I have already created the array and the values instead of using user input. I also just passed the array check sequence through a loop that increases a number value instead of reading from a file. This seemed to work fine with something like

a[5] = {10, 8, 6, 4, 2};
number = 5;   // number++  number = 6 number = 7... until 10
result: a[5] = {10, 9, 8, 7, 6};

I am sad to say that even if the program was not printing the right array at the beginning I could see there were numbers from the file. The loop is still going trough the file, but at one point the output just started being the sorted user array. I can't seem to get the array values right. Any ideas?


Solution

  • Continuing from my comments yesterday, I apologize if the errors were due to the retyping of your code, but that is the whole reason you want to try to cut and paste as Jonathan indicated -- eliminate human error in the transcription process.

    I think I understand what your primary problem is. If your goal is to read some number of user input values from stdin, sort them in descending order, and then open a file and read a single additional value into the array in sort-order, then you must provide space for the final value in your array at the time it is declared (if using VLA's). Otherwise, you either need to create a second VLA large enough to store the values from the use and the file, and copy the user provided values to the new array or dynamically allocate the array originally (with malloc or calloc) and then realloc as needed to add space for additional values as required.

    In this case, it's not that difficult since you know you are reading one value from the file. Just read the size from the command line argument and then create your array as int a[size + 1];

    The rest of your task can be handled in a couple of ways. After you read (and validate) the user input, you can sort your values in descending order, read the value from the file, and create an insert & shuffle routine to insert the value in the correct order and move the remaining array elements down, or (probably a less error prone method) is simply to add the element from the file to the end of the array, and call your sort routine again.

    (note: you should get used to using qsort rather than attempting to reinvent the bubble-sort, etc.. It is orders of magnitudes more efficient and much less error prone)

    You need limit (or eliminate) your use of atoi as it provides zero error checking. A better approach is to use strtol and then check errno and check the end-pointer against the original to determine if there were any digits read. Below a simple helper function incorporates error-checking for strtol to insure you have an actual value for size.

    Further, be careful. While you may expect the user will enter size integer values, there is no guarantee they will. It is better to track the number of values actually entered and use that value in subsequent iterations over the array rather than blindly iterating for (i = 0; i < size; i++) throughout the remainder of your code.

    Whether you attempt an insert-in-place of the value read from the file, or just add it to the end of the array and call your sort routine again is up to you. I would encourage you to move your sort code into a function to provide that flexibility without having to duplicate the code in main. Look the following over and let me know if you have any questions. Since I presume this was a homework assignment, the insert-in-place case is shown below (but the simple add the file value to the end and call sort again code is included commented out)

    #include <stdio.h>
    #include <stdlib.h> /* for strtol       */
    #include <limits.h> /* for LONG_MAX/MIN */
    #include <errno.h>  /* for ERANGE,errno */
    
    void sort_int_array_dec (int *a, size_t size);
    long xstrtol (char *p, char **ep, int base);
    
    int main (int argc, char **argv) {
    
        /* read size as first argument, or 5 if none given */
        int size = argc > 2 ? (int)xstrtol (argv[2], NULL, 10) : 5,
            a[size + 1],    /* variable length array for user + file values */
            n = 0,          /* number of values from user */
            fval,           /* value read from file */
            temp,           /* temporary value for array */
            i = 0;
        FILE *fp = NULL;
    
        if (size < 1) return 1;
    
        printf ("enter %d integers\n", size);
    
        while (n < size) { /* read up to size values */
    
            int result, c;
    
            printf ("  integer[%2d] : ", n + 1);
    
            /* validate read of each value using scanf return */
            if ((result = scanf ("%d", &temp)) != 1) {
    
                if (result == EOF) { /* always check for EOF */
                    fprintf (stderr, "user canceled input.\n");
                    break;
                }
    
                fprintf (stderr, "error: invalid conversion.\n");
    
                /* empty input buffer of invalid entry */
                while ((c = getchar()) != '\n' && c != EOF) {}
            }
            else    /* good value read, save, increment n */
                a[n++] = temp;
        }
    
        sort_int_array_dec (a, n); /* sort a */
    
        printf ("\nsorted array before inserting value from file:\n\n");
        for (int i = 0; i < n; i++)
            printf ("a[%2d]: %d\n", i, a[i]);
    
        if (!(fp = fopen (argv[1], "r"))) {
            fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
            return 1;
        }
    
        if (fscanf (fp, "%d", &fval) != 1) {  /* read value from file */
            fprintf (stderr, "error: read of file value failed.\n");
            return 1;
        }
        printf ("\n value from file: %d\n\n", fval);
    
    
        /* add fval into array in descending sort order 
         * (you can add it anywhere and simply call sort again, e.g.)
         */
    //     a[n] = fval;    /* add it to the end of the array */
    //     sort_int_array_dec (a, n + 1);    /* sort a again */
    
        for (i = 1; i < n + 1; i++) {
            if (fval > a[i-1]) {
                temp = a[i-1];
                a[i-1] = fval;
                break;      /* temp now holds value to insert at i */
            }
        }
    
        if (i == n + 1)     /* if already at last element just set it */
            a[n] = fval;
        else    /* otherwise, insert and shuffle remaining elements down */
            for (int j = i; j < n + 1; j++) {
                int mov = a[j];
                a[j] = temp;
                temp = mov;
            }
    
        printf ("sorted array after inserting value from file:\n\n");
        for (int i = 0; i < n + 1; i++)
            printf (" a[%2d]: %d\n", i, a[i]);
    
        return 0;
    }
    
    /** sort integer array descending (your code) */
    void sort_int_array_dec (int *a, size_t size)
    {
        size_t i, j;
        int temp;
    
        if (size < 2) return;   /* nothing to sort */
    
        for (i = 0; i < size; i++) {
            for (j = i + 1; j < size; j++) {
                if (a[i] < a[j]) {
                    temp = a[i];
                    a[i] = a[j];
                    a[j] = temp; 
                }
            }
        }
    }
    
    /** a simple strtol implementation with error checking.
     *  any failed conversion will cause program exit. Adjust
     *  response to failed conversion as required.
     */
    long xstrtol (char *p, char **ep, int base)
    {
        errno = 0;
    
        char *endpt = ep ? *ep : NULL;
    
        long tmp = strtol (p, &endpt, base);
    
        /* Check for various possible errors */
        if ((errno == ERANGE && (tmp == LONG_MIN || tmp == LONG_MAX)) ||
            (errno != 0 && tmp == 0)) {
            perror ("strtol");
            exit (EXIT_FAILURE);
        }
    
        if (endpt == p) {
            fprintf (stderr, "No digits were found\n");
            exit (EXIT_FAILURE);
        }
    
        if (ep) *ep = endpt;
    
        return tmp;
    }
    

    Example Use/Output

    $ cat dat/file.txt
    523
    
    $ ./bin/addintoarray dat/file.txt 4
    enter 4 integers
      integer[ 1] : 400
      integer[ 2] : 500
      integer[ 3] : 600
      integer[ 4] : 700
    
    sorted array before inserting value from file:
    
    a[ 0]: 700
    a[ 1]: 600
    a[ 2]: 500
    a[ 3]: 400
    
     value from file: 523
    
    sorted array after inserting value from file:
    
     a[ 0]: 700
     a[ 1]: 600
     a[ 2]: 523
     a[ 3]: 500
     a[ 4]: 400