0
FILE *fp;
char *f_array[256];
int f_length;
int *a = 0;

fp = fopen("test.txt", "r");
fseek(fp, 0, SEEK_END);
f_length = ftell(fp);
printf("%d\n\n", f_length);
int n = 1, i = 0;
while (n > 0)
{
    n = fscanf(fp, "%s", f_array[i]);
    i++;
}

I am trying to copy the contents of a .txt file into a char* array. Much like what would happen with InternetReadFile and lpbuffer. However, I cannot seem to get this right. I need my array to be filled with the contents of the .txt file character by character. Any suggestions? I need the array to be single-dimensional

Midge_Mong
  • 33
  • 2
  • 8

2 Answers2

1

The issue is mainly to do with your data types. You want to store the file in memory. That would be a character (byte) array, but you have created an "array of pointers":

char *f_array[256];

When you probably wanted just:

char f_array[256];

Then, to do as you asked and read character by character into that array, use fgetc. Note that fgetc will be far less efficient that just reading the whole file in a single function call with fread. The kernel:

while ( EOF != (c = fgetc( fp )) && ++i < MAX_LEN )
    f_array[ i ] = c;

In context of a working example:

#include <stdio.h>
int main ( ) {
    const size_t MAX_LEN = 255;
    FILE * fp;
    char f_array[ MAX_LEN +1];
    int c;
    size_t i = -1;
    f_array[ MAX_LEN +1] = 0;

    fp = fopen("test.txt","r");

    if ( NULL == fp )
        perror("Error opening file");
    else {
        while ( EOF != (c = fgetc( fp )) && ++i < MAX_LEN )
            f_array[ i ] = c;

        fclose (fp);
    }
    f_array[ i ] = 0;
    printf("%zu bytes read\n\n", i);
    printf("Content read:\n%s\n", f_array);

    return 0;
}
hunteke
  • 3,648
  • 1
  • 7
  • 17
  • When explaining, please don't call it *"array of arrays"*, that's not an array of arrays, it's an array of pointers. That is huge difference. Calling it array of arrays is misleading. And the cast `(char) c` is unnecessary. – Pablo Feb 14 '18 at 02:44
  • @Pablo "array of arrays": fair. I wasn't focused on that particular detail, but the detail that his datatype wasn't fit for purpose. Mea culpa. Meanwhile, while answering, you beat me to it, so I'm inclined to just delete my answer. – hunteke Feb 14 '18 at 02:50
  • don't delete the answer, it's a good answer. Someone might find yours more understandable than mine. Just update the bit with the "array of arrays". – Pablo Feb 14 '18 at 02:51
  • @Pablo `(char) c` not necessary: That would be stale thinking as I was typing that up. The compiler would throw a warning if not there. But the real issue is that `int c` should be `char c`. Fixed. – hunteke Feb 14 '18 at 02:57
  • No no, `c` should definitely be `int`, otherwise you will get problems when comparing with `EOF`. And I don't get any a warning with `char c = 10;` although 10 is an integer. That's not a problem. – Pablo Feb 14 '18 at 03:01
  • Huh, different compilers? I definitely don't get a warning with `char c`, and do get a warning if `int c` and no cast. GCC 5.4.0, `gcc -o test -Wall test.c` – hunteke Feb 14 '18 at 03:04
  • I'm using GCC 6.4.0, also with `-Wall`. – Pablo Feb 14 '18 at 03:06
  • Then newer is better, I suppose. You win. – hunteke Feb 14 '18 at 03:06
0

You are calculating the length of the file but you don't use this information in any way.

char *f_array[256];

This is an array of dimension 256 of pointers to char. f_array[i] is uninitialized and it's pointing into the digital nirvana, passing it to fscanf yields undefined behaviour. You would need to declare it as something like this:

char f_array[255][255];

But then you are limiting yourself to max 255 strings, you are not storing it into a single string. Also you are storing max. 255 words. Use fgets or fread to get the whole content at once.

char file[f_length + 1];

rewind(fp);

fread(file, f_length, 1, fp);

file[f_length] = 0; // terminate the string

printf("Whole file is: %s\n", file);

Here you are storing the whole file in an array of chars. Also after setting the file at the end, you'll need to rewind the file to the beginning, otherwise you are not reading anything.

Pablo
  • 13,271
  • 4
  • 39
  • 59
  • This code is not reliable. `fp = fopen("test.txt", "r");` opens the file in text mode. But `ftell()` on a stream in text mode does not return the number of bytes in the file, or even the number of bytes that can be read from the file. Per [**7.21.9.4 The `ftell` function**, paragraph 2](https://port70.net/~nsz/c/c11/n1570.html#7.21.9.4p2) of [the C standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf): – Andrew Henle Feb 14 '18 at 11:06
  • (cont) "For a text stream, its file position indicator contains unspecified information, usable by the `fseek` function for returning the file position indicator for the stream to its position at the time of the `ftell` call; the difference between two such return values is **not necessarily a meaningful measure of the number of characters written read**" For example, on Windows `file[f_length] = 0; // terminate the string` will not properly terminate the string. – Andrew Henle Feb 14 '18 at 11:06
  • @AndrewHenle this is new to me, I've read the `ftell` man page on my system at work (debian 8) and the page does not mention this at all. I've been doing this on linux for years without a problem. In fact in Linux `b` is ignored (my man page even says *all POSIX conforming systems*) . To be portable, what would you suggest, `fopen("test.txt", "rb");`? – Pablo Feb 14 '18 at 12:53
  • @Pablo It works on Linux because Linux is (mostly) POSIX-compliant, and [POSIX defines `ftell()` to return a byte offset](http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftell.html): "`ftell()` ... shall return the current value of the file-position indicator for the stream measured in bytes from the beginning of the file." But there's [a portability problem with `fseek()` on a binary stream](https://port70.net/~nsz/c/c11/n1570.html#7.21.9.2p3): "A binary stream need not meaningfully support `fseek` calls with a whence value of `SEEK_END`." – Andrew Henle Feb 14 '18 at 13:01
  • @Pablo (cont) [Additionally](https://port70.net/~nsz/c/c11/n1570.html#note268): "Setting the file position indicator to end-of-file, as with `fseek(file, 0, SEEK_END)`, has **undefined behavior for a binary stream** ..." So, there is no *portable* way that's strictly compliant with the C standard to get the size of a file other than to read it `char`-by-`char` and count them. There are some downright *strange* storage systems out there... – Andrew Henle Feb 14 '18 at 13:03
  • @AndrewHenle Oh I see, I was also reading [this](https://stackoverflow.com/a/2076043/1480131) which basically says the same. So the only portable way of knowing how long you text file is, is by reading the whole file and count the characters yourself? Does that also mean that POSIX's `ftell` contradicts/behaves differently from the standard? – Pablo Feb 14 '18 at 13:07
  • @Pablo POSIX specifies byte offsets for `fseek()` and `ftell()`, *and* [POSIX `fopen()` states](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html): "The character `'b'` shall have no effect ...`. So POSIX streams are pure bytes with no character translation. Windows also specifies bytes *but* translates between `"\n"` in memory and `"\r\n"` on disk, which throws the byte counts off for streams not opened in binary mode. So, yes, the only portable way to measure the size of file is to count bytes. But technically, you're just counting the number of bytes you can read. – Andrew Henle Feb 14 '18 at 13:36