abhinavk
abhinavk

Reputation: 13

Two EOF required to actually end reading from terminal

I have been trying to understand how EOF works. In my code (on Windows) invoking EOF (Ctrl+Z and Enter) doesn't work the first time and I have to provide two EOF for it to actually stop reading input. Also, the first EOF gets read as some garbage character which gets displayed when I print the input. (We can see the garbage characters being display at the end in the output provided).

This is my code:-

#include<stdio.h>

#define Max 1000

int main()
{
    char c, text[Max];
    int i = 0;

    while((c = getchar()) != EOF)
    {
        text[i] = c;
        i++;
    }

    printf("\nEntered Text: \n");
    puts(text);

    return 0;
}

My Output:

My Output:

I have this doubt:-

Why are two EOFs being required? and how do I prevent the first one from being read (as some garbage) and stored as part of my input?

Upvotes: 1

Views: 422

Answers (4)

AnT stands with Russia
AnT stands with Russia

Reputation: 320371

The logic that Windows terminals follow with regard to ^Z in keyboard input (at least in their default configuration) is as follows:

  • The Ctrl-Z combination itself does not cause the input line buffer to get pushed to the waiting application. This key combination simply generates ^Z character in the input buffer. You have to press Enter to finish that line buffer and send it to the application.

    You can actually keep entering additional characters after ^Z and before pressing Enter.

  • If the input line does not begin with ^Z, but contains ^Z inside, then the application will receive that line up to and including the first ^Z character (read as \x1A character). The rest of the input is discarded.

    E.g. if you type in

    Hello^Z World^Z123
    

    and press Enter your C program will actually read Hello\x1A sequence. EOF condition will not arise.

  • If the input line begins with ^Z, the whole line is discarded and EOF condition is set.

    E.g. if you input

    ^ZHello World
    

    and press Enter your program will read nothing and immediately detect EOF.

This is the behavior you observe in your experiments. Just keep in mind that the result of getchar() should be received into an int variable, not a char variable.

Upvotes: 0

Clifford
Clifford

Reputation: 93456

The following solution fixes the Ctrl+Z problem and the garbage output and also blocks a buffer overrun. I have commented the changes:

#include <stdio.h>

#define Max 1000
#define CTRL_Z 26           // Ctrl+Z is ASCII/ANSI 26

int main()
{
    int c ;                  // getchar() returns int
    char text[Max + 1] ;     // +1 to acommodate terminating nul
    int i = 0;

    while( i < Max &&                 // Bounds check
           (c = getchar()) != EOF && 
           c != CTRL_Z )              // Check for ^Z when not start of input buffer
    {
        text[i] = c;
        i++;
    }

    text[i] = 0 ;        // Terminate string after last added character

    printf( "\nEntered Text:\n" );
    puts( text );

    return 0;
}

The reason for this behavior is somewhat arcane, but end-of-file is not the same as Ctrl-Z. The console generates an end-of-file causing getchar() to return EOF (-1) if and only if the console input buffer is empty, otherwise it inserts the ASCII SUB (26) character into the stream. The use of SUB was originally to do with MS-DOS compatibility with the even earlier CP/M operating system. In particular CP/M files were composed of fixed length records, so a ^Z in the middle of a record, was used to indicate the end of valid data for files that were not an exact multiple of the record length. In the console, the SUB is readable rather than generating an EOF if it is not at the start of the input buffer and all characters after the SUB are discarded. It is all a messy hangover from way-back.

Upvotes: 1

dgnuff
dgnuff

Reputation: 3557

Control-Z is only recognized as EOF when at the start of a new line. Therefore, if you want to detect it in the middle of a line, you'll need to do so yourself.

So change this line:

while((c = getchar()) != EOF)

to this:

while((c = getchar()) != EOF && c != CTRL_Z)

and then add:

#define CTRL_Z ('Z' & 0x1f)

at the top of your program.

You may still need to type a return after the Ctrl-z to get the buffered input to be read by the program, but it should discard everything after the ^Z.

Upvotes: 2

Try changing the type of c to int as EOF can be a negative number and commonly it is defined as -1. char might or might not be able to store -1. Also, do not forget to end the string with \0 before passing it to puts.

Upvotes: 1

Related Questions