126

I have a text file named test.txt

I want to write a C program that can read this file and print the content to the console (assume the file contains only ASCII text).

I don't know how to get the size of my string variable. Like this:

char str[999];
FILE * file;
file = fopen( "test.txt" , "r");
if (file) {
    while (fscanf(file, "%s", str)!=EOF)
        printf("%s",str);
    fclose(file);
}

The size 999 doesn't work because the string returned by fscanf can be larger than that. How can I solve this?

bta
  • 43,959
  • 6
  • 69
  • 99
richardaum
  • 6,651
  • 12
  • 48
  • 64

9 Answers9

162

The simplest way is to read a character, and print it right after reading:

int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) {
    while ((c = getc(file)) != EOF)
        putchar(c);
    fclose(file);
}

c is int above, since EOF is a negative number, and a plain char may be unsigned.

If you want to read the file in chunks, but without dynamic memory allocation, you can do:

#define CHUNK 1024 /* read 1024 bytes at a time */
char buf[CHUNK];
FILE *file;
size_t nread;

file = fopen("test.txt", "r");
if (file) {
    while ((nread = fread(buf, 1, sizeof buf, file)) > 0)
        fwrite(buf, 1, nread, stdout);
    if (ferror(file)) {
        /* deal with error */
    }
    fclose(file);
}

The second method above is essentially how you will read a file with a dynamically allocated array:

char *buf = malloc(chunk);

if (buf == NULL) {
    /* deal with malloc() failure */
}

/* otherwise do this.  Note 'chunk' instead of 'sizeof buf' */
while ((nread = fread(buf, 1, chunk, file)) > 0) {
    /* as above */
}

Your method of fscanf() with %s as format loses information about whitespace in the file, so it is not exactly copying a file to stdout.

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • It is possible to read data from file without opening that file in c/c++?? – Sagar Patel Oct 27 '15 at 11:13
  • what if the the text file contains comma separated integer values? than what would be the code can you edit your answer with that too in it. – Mohsin Oct 21 '16 at 20:43
  • The above works for any kind of text file. If you want to parse the numbers from a CSV file, that's a different problem. – Alok Singhal Oct 22 '16 at 03:34
  • How do you know that every line is 1024 bytes? Ideally your code should convey an approach that every line is read. You are reading in terms of bytes and nothing more – overexchange Jan 02 '17 at 03:14
  • 1
    @overexchange The question doesn't talk about lines - it is about reading a file and copying its contents to `stdout`. – Alok Singhal Jan 03 '17 at 03:35
  • This is the best and correct method to read ( primitive ). if you want had a best code, in the CHUNK can you assign size from file. –  May 18 '17 at 08:23
  • `while ((c = getc(file)) != EOF)` - what if file contains EOF character? – shjeff Nov 22 '17 at 11:58
  • 1
    @shjeff A file cannot contain EOF character. Note that `c` is int, and C will guarantee that `EOF` is not equal to any valid character. – Alok Singhal Nov 22 '17 at 22:14
  • @AlokSinghal, ok - I see you're right. I had same issue as in link you pointed, so I asked about this. – shjeff Nov 24 '17 at 12:50
75

There are plenty of good answers here about reading it in chunks, I'm just gonna show you a little trick that reads all the content at once to a buffer and prints it.

I'm not saying it's better. It's not, and as Ricardo sometimes it can be bad, but I find it's a nice solution for the simple cases.

I sprinkled it with comments because there's a lot going on.

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

char* ReadFile(char *filename)
{
   char *buffer = NULL;
   int string_size, read_size;
   FILE *handler = fopen(filename, "r");

   if (handler)
   {
       // Seek the last byte of the file
       fseek(handler, 0, SEEK_END);
       // Offset from the first to the last byte, or in other words, filesize
       string_size = ftell(handler);
       // go back to the start of the file
       rewind(handler);

       // Allocate a string that can hold it all
       buffer = (char*) malloc(sizeof(char) * (string_size + 1) );

       // Read it all in one operation
       read_size = fread(buffer, sizeof(char), string_size, handler);

       // fread doesn't set it so put a \0 in the last position
       // and buffer is now officially a string
       buffer[string_size] = '\0';

       if (string_size != read_size)
       {
           // Something went wrong, throw away the memory and set
           // the buffer to NULL
           free(buffer);
           buffer = NULL;
       }

       // Always remember to close the file.
       fclose(handler);
    }

    return buffer;
}

int main()
{
    char *string = ReadFile("yourfile.txt");
    if (string)
    {
        puts(string);
        free(string);
    }

    return 0;
}

Let me know if it's useful or you could learn something from it :)

lfzawacki
  • 1,422
  • 11
  • 10
  • 2
    Shouldn't it read `buffer[string_size] = '\0';` instead of `string_size+1`? Afaik the actual string goes from `0` to `string_size-1` and the `\0` character thus needs to be at `string_size`, right? – Emilia Bopp Jun 03 '14 at 09:54
  • You're right, I corrected it. This answer has been here for quite some time and this issue went unnoticed, gotta love C :) – lfzawacki Jun 04 '14 at 04:48
  • Yep, that's a bit odd… I had to ran into a segfault until I noticed. C just way too low-level for my taste. – Emilia Bopp Jun 04 '14 at 14:37
  • 5
    Using `ftell` and `fseek` to find the size of a file is unsafe: https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file – Joakim Jul 01 '14 at 09:05
  • 1
    One thing to note on Windows, `string_size` might not be equal to `read_size` if the file is opened in text mode. I quote from this link (http://bytes.com/topic/c/answers/479976-interesting-thing-about-fread) `Under Windows, a text file has two characters for end-of-line (CR+LF -- "\r\n"), but the C library will strip the CR when reading a file open in text mode, so that the program will only see LF ('\n'). The stat() call is returning the "real" size if the file, while the returns from fread() have stripped the CR.` – vexe Sep 07 '15 at 00:45
  • 1
    This code contains a memory leak, you never close the file. There is a missing `fclose(handle)` – Joakim Apr 10 '16 at 22:16
  • 1
    There is a typo where you call fclose(handle), it should be fclose(handler) – Eduardo Cobuci Sep 21 '16 at 17:41
  • Your approach of reading file looks better than accepted answer. But `buffer` can be too large to handle. Is it practical to allocate a file size `buffer`? Despite this [answer](http://stackoverflow.com/a/11487960/3317808) supports it – overexchange Jan 02 '17 at 03:17
  • 1
    It is really strange, string_size and read_size for me always not equal. I tried update this functions with different logic statements but I am always getting incorrect read and string sizes. – Jevgenij Kononov Dec 20 '17 at 15:22
  • 3
    You could use `calloc(2)` rather than `malloc(1)` to skip having to set the null terminator. –  Feb 07 '18 at 07:55
16

Instead just directly print the characters onto the console because the text file maybe very large and you may require a lot of memory.

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

int main() {

    FILE *f;
    char c;
    f=fopen("test.txt","rt");

    while((c=fgetc(f))!=EOF){
        printf("%c",c);
    }

    fclose(f);
    return 0;
}
Sagar Shah
  • 391
  • 5
  • 11
9

Use "read()" instead o fscanf:

ssize_t read(int fildes, void *buf, size_t nbyte);

DESCRIPTION

The read() function shall attempt to read nbyte bytes from the file associated with the open file descriptor, fildes, into the buffer pointed to by buf.

Here is an example:

http://cmagical.blogspot.com/2010/01/c-programming-on-unix-implementing-cat.html

Working part from that example:

f=open(argv[1],O_RDONLY);
while ((n=read(f,l,80)) > 0)
    write(1,l,n);

An alternate approach is to use getc/putc to read/write 1 char at a time. A lot less efficient. A good example: http://www.eskimo.com/~scs/cclass/notes/sx13.html

DVK
  • 126,886
  • 32
  • 213
  • 327
  • 1
    `read` will let you read in a certain number of characters. Read in enough to fill your buffer, then dump your buffer to the screen, clear it out, and repeat until you get to the end of the file. – bta Aug 11 '10 at 23:00
3

You can use fgets and limit the size of the read string.

char *fgets(char *str, int num, FILE *stream);

You can change the while in your code to:

while (fgets(str, 100, file)) /* printf("%s", str) */;
7vujy0f0hy
  • 8,741
  • 1
  • 28
  • 33
Edu
  • 1,949
  • 1
  • 16
  • 18
2

Two approaches leap to mind.

First, don't use scanf. Use fgets() which takes a parameter to specify the buffer size, and which leaves any newline characters intact. A simple loop over the file that prints the buffer content should naturally copy the file intact.

Second, use fread() or the common C idiom with fgetc(). These would process the file in fixed-size chunks or a single character at a time.

If you must process the file over white-space delimited strings, then use either fgets or fread to read the file, and something like strtok to split the buffer at whitespace. Don't forget to handle the transition from one buffer to the next, since your target strings are likely to span the buffer boundary.

If there is an external requirement to use scanf to do the reading, then limit the length of the string it might read with a precision field in the format specifier. In your case with a 999 byte buffer, then say scanf("%998s", str); which will write at most 998 characters to the buffer leaving room for the nul terminator. If single strings longer than your buffer are allowed, then you would have to process them in two pieces. If not, you have an opportunity to tell the user about an error politely without creating a buffer overflow security hole.

Regardless, always validate the return values and think about how to handle bad, malicious, or just malformed input.

RBerteig
  • 41,948
  • 7
  • 88
  • 128
1

You can use getline() to read your text file without worrying about large lines:

getline() reads an entire line from stream, storing the address of the buffer containing the text into *lineptr. The buffer is null-terminated and includes the newline character, if one was found.

If *lineptr is set to NULL before the call, then getline() will allocate a buffer for storing the line. This buffer should be freed by the user program even if getline() failed.

bool read_file(const char *filename)
{
    FILE *file = fopen(filename, "r");
    if (!file)
        return false;
    
    char *line = NULL;
    size_t linesize = 0;

    while (getline(&line, &linesize, file) != -1) {
        printf("%s", line);
        free(line);
    }
    
    free(line);
    fclose(file);

    return true;
}

You can use it like this:

int main(void)
{
    if (!read_file("test.txt")) {
        printf("Error reading file\n");
        exit(EXIT_FAILURE);
    }
}
Zakk
  • 1,935
  • 1
  • 6
  • 17
1

I use this version

char* read(const char* filename){
    FILE* f = fopen(filename, "rb");
    if (f == NULL){
        exit(1);
    }
    fseek(f, 0L, SEEK_END);
    long size = ftell(f)+1;
    fclose(f);
    f = fopen(filename, "r");
    void* content = memset(malloc(size), '\0', size);
    fread(content, 1, size-1, f);
    fclose(f);
    return (char*) content;
}
0

You could read the entire file with dynamic memory allocation, but isn't a good idea because if the file is too big, you could have memory problems.

So is better read short parts of the file and print it.

#include <stdio.h>
#define BLOCK   1000

int main() {
    FILE *f=fopen("teste.txt","r");
    int size;
    char buffer[BLOCK];
    // ...
    while((size=fread(buffer,BLOCK,sizeof(char),f)>0))
            fwrite(buffer,size,sizeof(char),stdout);
    fclose(f);
    // ...
    return 0;
}
sam hocevar
  • 11,853
  • 5
  • 49
  • 68
rigon
  • 1,310
  • 4
  • 15
  • 37