简体   繁体   中英

Reading numbers from file into an array

I'm trying to get the numbers from a File into the Array. I tried different approaches, and i came up with the following code:

int* readFileWithNumbers ()
{
    FILE *file = fopen("number_list.txt", "r");
    int*integers = malloc(sizeof(int) * 240);;

    int i=0;
    int num;
    while(fscanf(file, "%d", &num) > 0) {
       integers[i] = num;
        i++;
    }
    fclose(file);
    return integers;
}

My input file is in the format:

106
-18
248
-237
148
142
38
-189
59
-120
-219
-172
237
-257
-154
-267
-34
-292
239
-182
-243
-115
-26
-238
298

Can you please guide me, what I'm doing wrong here ?

Generally, you will want to pass a FILE * pointer, the array , a pointer to the index , and a pointer to the current allocation of memory max to your function. That way your function can read values from the file into the array, keep a count of the current index to compare against the current allocation size so you can realloc if the number of values exceed your current allocation. Finally, by virtual of using a pointer for both your index and your max allocation, the changes to either are immediately available back in your calling function (typically main() for these short examples.

Putting all those pieces together, you could do something similar to:

/* read array of unknown size int 'a' from 'fp' */
int *readarr (FILE *fp, int *a, size_t *idx, size_t *max)
{
    int tmp;

    if (!a) /* if a not allocated -- allocate/validate */
        if (!(a = calloc (1, *max * sizeof *a))) {
            fprintf (stderr, "readarr() virtual memory exhausted.\n");
            exit (EXIT_FAILURE);
        }

    while (fscanf (fp, " %d", &tmp) == 1) {     /* for each int in file */
        if (*idx == *max) {                     /* check idx and max    */
            void *ap = realloc (a, 2 * *max * sizeof *a);   /* realloc  */
            if (!ap) {      /* validate realloc success */
                fprintf (stderr, "realloc() error: memory exhausted.\n");
                break;      /* if failure, return with exising data */
            }
            a = ap;     /* assign new mem block, zero new mem */
            memset (ap + *max * sizeof *a, 0, *max * sizeof *a);
            *max *= 2;  /* update allocation size */
        }
        a[(*idx)++] = tmp;  /* add int to array, update index */
    }

    return a;   /* return array */
}

( note: the function will accept an allocated array, or NULL and allocate/reallocate as needed. However, the value for your array a cannot be a statically declared array due to the call to realloc that takes place. Further, you must assign the return from the function to your array in main() . If 'a' is NULL to begin with, or if realloc takes place, a new pointer address is returned.)

Zeroing the new memory after realloc is not required, but it can help prevent an inadvertent read from an uninitialized value if you later iterate over the array with an index greater than the last stored value index.

Also note the current reallocation scheme doubles the amount of memory each time it calls realloc . You can choose to add as little or as much as you need.

With that, a short example putting it all together could be:

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

#define MAXI 64

int *readarr ( FILE *fp, int *a, size_t *idx, size_t *max);

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

    int *array = NULL;                  /* pointer to array        */
    size_t i = 0, n = 0, maxi = MAXI;   /* index, initial elements */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* valildate file pointer */
        fprintf (stderr, "error: file open failed. '%s'\n",
                argc > 1 ? argv[1] : "stdin");
        return 1;
    }

    array = readarr (fp, array, &n, &maxi); /* read values from file */

    if (fp != stdin) fclose (fp);           /* close file */


    for (i = 0; i < n; i++)                 /* print array */
        printf (" array[%3zu] : %d\n", i, array[i]);

    free (array);   /* free memory */

    return 0;
}

/* read array of unknown size int 'a' from 'fp' */
int *readarr (FILE *fp, int *a, size_t *idx, size_t *max)
{
    int tmp;

    if (!a) /* if a not allocated -- allocate/validate */
        if (!(a = calloc (1, *max * sizeof *a))) {
            fprintf (stderr, "readarr() virtual memory exhausted.\n");
            exit (EXIT_FAILURE);
        }

    while (fscanf (fp, " %d", &tmp) == 1) {     /* for each int in file */
        if (*idx == *max) {                     /* check idx and max    */
            void *ap = realloc (a, 2 * *max * sizeof *a);   /* realloc  */
            if (!ap) {      /* validate realloc success */
                fprintf (stderr, "realloc() error: memory exhausted.\n");
                break;      /* if failure, return with exising data */
            }
            a = ap;     /* assign new mem block, zero new mem */
            memset (ap + *max * sizeof *a, 0, *max * sizeof *a);
            *max *= 2;  /* update allocation size */
        }
        a[(*idx)++] = tmp;  /* add int to array, update index */
    }

    return a;   /* return array */
}

Example Input 100 Int

$ cat dat/100int.txt
27086
29317
32736
...
16892
8270
6444

Example Use/Output

$ ./bin/fscanf_array_dyn dat/100int.txt
 array[  0] : 27086
 array[  1] : 29317
 array[  2] : 32736
 array[  3] : 3356
...
 array[ 97] : 16892
 array[ 98] : 8270
 array[ 99] : 6444

Memory Error Check

In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.

For Linux valgrind is the normal choice. There are many subtle ways to misuse a new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding out a problem exists through a segfault . There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/fscanf_array_dyn dat/100int.txt
==7273== Memcheck, a memory error detector
==7273== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7273== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7273== Command: ./bin/fscanf_array_dyn dat/100int.txt
==7273==
 array[  0] : 27086
 array[  1] : 29317
 array[  2] : 32736
...
 array[ 97] : 16892
 array[ 98] : 8270
 array[ 99] : 6444
==7273==
==7273== HEAP SUMMARY:
==7273==     in use at exit: 0 bytes in 0 blocks
==7273==   total heap usage: 3 allocs, 3 frees, 1,336 bytes allocated
==7273==
==7273== All heap blocks were freed -- no leaks are possible
==7273==
==7273== For counts of detected and suppressed errors, rerun with: -v
==7273== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts .

Look it all over and let me know if you have any further questions. Basic allocation and freeing of memory is the basis for much of C. It's better you take the time now to completely understand the code, then end up beating your head against your desk later... Good luck with your coding.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM