Kapil
Kapil

Reputation: 113

How to read a complete file with scanf maybe something like %[^\EOF] without loop in single statement

I want to know if I can read a complete file with single scanf statement. I read it with below code.

#include<stdio.h>

int main()
{
    FILE * fp;
    char arr[200],fmt[6]="%[^";
    fp = fopen("testPrintf.c","r");
    fmt[3] = EOF;
    fmt[4] = ']';
    fmt[5] = '\0';
    fscanf(fp,fmt,arr);
    printf("%s",arr);
    printf("%d",EOF);
    return 0;
}

And it resulted into a statement after everything happened

"* * * stack smashing detected * * *: terminated Aborted (core dumped)"

Interestingly, printf("%s",arr); worked but printf("%d",EOF); is not showing its output.

Can you let me know what has happened when I tried to read upto EOF with scanf?

Upvotes: 0

Views: 1013

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753645

If you really, really must (ab)use fscanf() into reading the file, then this outlines how you could do it:

  • open the file
  • use fseek() and ftell() to find the size of the file
  • rewind() (or fseek(fp, 0, SEEK_SET)) to reset the file to the start
  • allocate a big buffer
  • create a format string that reads the correct number of bytes into the buffer and records how many characters are read
  • use the format with fscanf()
  • add a null terminating byte in the space reserved for it
  • print the file contents as a big string.

If there are no null bytes in the file, you'll see the file contents printed. If there are null bytes in the file, you'll see the file contents up to the first null byte.

I chose the anodyne name data for the file to be read — there are endless ways you can make that selectable at runtime.

There are a few assumptions made about the size of the file (primarily that the size isn't bigger than can be fitted into a long with signed overflow, and that it isn't empty). It uses the fact that the %c format can accept a length, just like most of the formats can, and it doesn't add a null terminator at the end of the string it reads and it doesn't fuss about whether the characters read are null bytes or anything else — it just reads them. It also uses the fact that you can specify the size of the variable to hold the offset with the %n (or, in this case, the %ln) conversion specification. And finally, it assumes that the file is not shrinking (it will ignore growth if it is growing), and that it is a seekable file, not a FIFO or some other special file type that does not support seeking.

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

int main(void)
{
    const char filename[] = "data";
    FILE *fp = fopen(filename, "r");
    if (fp == NULL)
    {
        fprintf(stderr, "Failed to open file %s for reading\n", filename);
        exit(EXIT_FAILURE);
    }
    fseek(fp, 0, SEEK_END);
    long length = ftell(fp);
    rewind(fp);
    char *buffer = malloc(length + 1);
    if (buffer == NULL)
    {
        fprintf(stderr, "Failed to allocate %ld bytes\n", length + 1);
        exit(EXIT_FAILURE);
    }
    char format[32];
    snprintf(format, sizeof(format), "%%%ldc%%ln", length);
    long nbytes = 0;
    if (fscanf(fp, format, buffer, &nbytes) != 1 || nbytes != length)
    {
        fprintf(stderr, "Failed to read %ld bytes (got %ld)\n", length, nbytes);
        exit(EXIT_FAILURE);
    }
    buffer[length] = '\0';
    printf("<<<SOF>>\n%s\n<<EOF>>\n", buffer);
    free(buffer);
    return(0);
}

This is still an abuse of fscanf() — it would be better to use fread():

    if (fread(buffer, sizeof(char), length, fp) != (size_t)length)
    {
        fprintf(stderr, "Failed to read %ld bytes\n", length);
        exit(EXIT_FAILURE);
    }

You can then omit the variable format and the code that sets it, and also nbytes. Or you can keep nbytes (maybe as a size_t instead of long) and assign the result of fread() to it, and use the value in the error report, along the lines of the test in the fscanf() variant.

You might get warnings from GCC about a non-literal format string for fscanf(). It's correct, but this isn't dangerous because the programmer is completely in charge of the content of the format string.

Upvotes: 1

Related Questions