0

I have an assignment in which I need to write a function what reads numbers from file to an array and returns that array(pointer at the first element). I wrote a functions what reads from file to array but I have no idea how to return it.

void to_array(FILE* file, int bufsize){
char arr[bufsize][100];
fseek(file, 0, SEEK_SET);
int i = 0;
while(!feof(file)){
    fscanf(file, "%s", arr[i]);
    i++;
}

I would appreciate any ideas.

otan
  • 15
  • 2
  • 2
    Read [**Why is “while (!feof(file))” always wrong?**](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) – Andrew Henle Apr 30 '19 at 21:07
  • please look up formatting for code. A C function return value type is defined by putting the type in front of the function name. eg - int mySuperFunction(). – john elemans Apr 30 '19 at 21:10
  • Possible duplicate of [Read list of numbers in txt file and store to array in C](https://stackoverflow.com/questions/14802470/read-list-of-numbers-in-txt-file-and-store-to-array-in-c) – MFisherKDX Apr 30 '19 at 21:13
  • 1
    You can't return the address of a local variable (well, you *can* but using it is a bug). So you'll need to figure something else out instead or returning `arr`. Either have the caller pass in the array or create it dynamically and return it. Also, why on earth is the array of `char` instead of `int` and what is the point of `bufsize`? – MFisherKDX Apr 30 '19 at 21:17
  • Usually the user's prerogative where they want the file pointer, that is, you don't need `fseek`. Look at the documentation. https://pubs.opengroup.org/onlinepubs/007904875/functions/fscanf.html – Neil Apr 30 '19 at 21:20
  • `fscanf(file, "%s", arr[i]);` will read a all characters up to the next *whitespace* (or EOF) whichever occurs first. You must either (1) dynamically allocate within your function for `arr` or (2) pass a pointer to a sufficiently allocated `arr` as a parameter. Then `while (fgets (arr[i], 100, file) { arr[i][strcspn(arr[i], "\r\n")] = 0; i++; }` – David C. Rankin Apr 30 '19 at 22:32

3 Answers3

2

in

void to_array(FILE* file, int bufsize){
  char arr[bufsize][100];
  ...
}

your array is local, if you return its address it will not be able to use it

One way it to allocate it in the head by malloc, an other is to receive that array in argument to fill it. Note it cannot be static without having a constant size

Do not use feof check the result of fscanf

You read numbers, so the array must be an array of numbers, let consider of int

Example where the function receives the file descriptor and a pointer to a variable which will contain the number of elements placed into the array, the function return the array allocated in the heap as you wanted

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

int * to_array(FILE * fp, size_t * sz)
{
  int * a = malloc(0); /* initially empty */
  int v;

  *sz = 0; /* initially no element */

  while (fscanf(fp, "%d", &v) == 1) {
    a = realloc(a, (++*sz)*sizeof(int)); /* add an entry */
    a[*sz - 1] = v; /* set entry */
  }

  return a;
}

int main()
{
  size_t sz;
  int * a = to_array(stdin, &sz);

  /* check */
  for (size_t i = 0; i != sz; ++i)
    printf("%d\n", a[i]);

  /* free used memory */
  free(a);

  return 0;
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall a.c
pi@raspberrypi:/tmp $ echo 11 22 33 | ./a.out
11
22
33
pi@raspberrypi:/tmp $

Execution under valgrind :

pi@raspberrypi:/tmp $ echo 11 22 33 | valgrind ./a.out
==10080== Memcheck, a memory error detector
==10080== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10080== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10080== Command: ./a.out
==10080== 
11
22
33
==10080== 
==10080== HEAP SUMMARY:
==10080==     in use at exit: 0 bytes in 0 blocks
==10080==   total heap usage: 6 allocs, 6 frees, 5,144 bytes allocated
==10080== 
==10080== All heap blocks were freed -- no leaks are possible
==10080== 
==10080== For counts of detected and suppressed errors, rerun with: -v
==10080== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)

The function allocate an additional entry each time, it is possible to do by block of a given number of entries to not have to do that each time an entry has to be added.

bruno
  • 32,421
  • 7
  • 25
  • 37
0

Make sure to #include <stdio.h> when using it.

Usually the user's prerogative where they want the file pointer, that is, you don't need fseek.

One must accept the buffer size as well as the buffer, or one could use dynamic realloc, (more complicated error reporting and free.)

Use the return values to decide whether to continue, not feof. I think you have chosen the right input-reading function with fscanf; be sure to read the documentation. Specifically, %s is almost certainly not what you want; perhaps %d?

while risks overflowing the buffer; if you have a fixed-size buffer, something like this:

size_t i;
for(i = 0; i < bufsize && scanf("%d", buf + i) == 1; i++);

There are several things that can happen, including:

  • Stream (file) error. Use ferror to test.
  • You didn't hit the end of the file, this is appropriate to test with feof; if i == bufsize the programme has no more space to fit the number; otherwise, it's an invalid number. These conditions are awkward to handle, but I suggest errno.
  • Success.

Perhaps one wants to return a boolean value for success?

Neil
  • 1,767
  • 2
  • 16
  • 22
0

If you choose to use an array with automatic storage duration, you must declare the array in the calling function and pass a pointer to the array to your function. You can then fill the array in the function. However, you must also provide a way to return the number of strings read. Your choice of a void type for your function complicates this (slightly) as you would normally choose a meaningful return type such as size_t allowing you to return the number of strings read. That notwithstanding, you can pass a pointer to a size_t value and update the value at that address within your function thereby making the number of strings available back in the calling function.

A few notes:

/* if you need a constant, #define one (or more) */
#define STRSZ  32       /* maximum string stize in array */
#define NSTR  100       /* maximum number of strings in array */

(don't skimp on buffer size) The longest word in the unabridged dictionary (non-medical) is 29-characters and the longest 64-bit number is 20-character (including sign), so for normal use a buffer size of 30 will suffice, 32 will account for any trailing punctuation mark. (if space isn't a consideration for the number of strings you need to read, double STRSZ to 64) Adjust the number of strings and the maximum characters in each as needed.

Your function can then be written to take a pointer to an array of char [32], e.g.

 void to_array (char (*arr)[STRSZ], FILE *fp, size_t *nstr)
{
    *nstr = 0;  /* initialize number of strings to zero */

    /* protect array bounds while reading each string from file */
    while (*nstr < NSTR && fscanf (fp, "%31s" , arr[*nstr]) != EOF)
        (*nstr)++;  /* increment number of strings */
}

(note: the use of the field-width modifier "%31s" to protect the bounds of each string when reading from the file, and the use of *nstr < NSTR to protect the array bounds for the number of strings read from the file)

In main(), you can simply declare a 2D array with automatic storage duration and a variable to keep track of the number of strings filled, e.g.

    char arr[NSTR][STRSZ] = {{0}};  /* array to hold strings (max 31 char) */
    size_t n = 0;                   /* number of strings read */

Now you can call your to_array() function as follows:

    to_array (arr, fp, &n);     /* read strings from file, n holds number */

Putting it altogether along with proper validation that your file is open for reading before your call to to_array(), you could do:

#include <stdio.h>

/* if you need a constant, #define one (or more) */
#define STRSZ  32       /* maximum string size */
#define NSTR  100       /* maximum number of strings in array */

void to_array (char (*arr)[STRSZ], FILE *fp, size_t *nstr)
{
    *nstr = 0;  /* initialize number of strings to zero */

    /* protect array bounds while reading each string from file */
    while (*nstr < NSTR && fscanf (fp, "%31s" , arr[*nstr]) != EOF)
        (*nstr)++;  /* increment number of strings */
}

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

    char arr[NSTR][STRSZ] = {{0}};  /* array to hold strings (max 31 char) */
    size_t n = 0;                   /* number of strings read */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    to_array (arr, fp, &n);     /* read strings from file, n holds number */

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

    for (size_t i = 0; i < n; i++)  /* output each string in arr */
        printf ("arr[%2zu] : '%s'\n", i, arr[i]);
}

Example Input File

$ cat ../dat/10int_nl.txt
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495

Example Use/Output

$ ./bin/fscanffile ../dat/10int_nl.txt
arr[ 0] : '8572'
arr[ 1] : '-2213'
arr[ 2] : '6434'
arr[ 3] : '16330'
arr[ 4] : '3034'
arr[ 5] : '12346'
arr[ 6] : '4855'
arr[ 7] : '16985'
arr[ 8] : '11250'
arr[ 9] : '1495'

Look things over and let me know if you have further questions. Consider using fgets() if you need to read a line-at-a-time.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85