Problem with passing ESCAPE key into stdin buffer in C Windows console - c

I want to put the text into my string and process it in an infinite loop, but I want to break the loop if the first character of the input is an ESCAPE key, but getchar returns 10 when I press ESCAPE followed by ENTER.
printf correctly prints the inputted character, but if I press ESCAPE nothing happens. I also don't want to use _getch() nor getche() which actually would solve my problem, but they also remove c from the buffer and doesn't display it properly.
What can I do?
int c;
while( 1 )
{
c = getchar();
printf( "c = %d\n", c ); // just for debug
if( c == 27 ) break;
else ungetc( c, stdin );
fgets( StrIn, BUF_SIZE - 1, stdin );
// REST OF CODE ********
}
EDIT I've just found Microsoft function
if( GetAsyncKeyState( VK_ESCAPE ) )
break;
that works fine for me. Thanks for reading and for your time, my problem is solved.

Pressing the ESCAPE key on a keyboard does not usually send an ESCAPE character to a console, the application or OS will often process the character and not pass it on. I'd use a different character to represent the fact that you want to break, for example a '~'.
When you say it doesn't display right if you remove c from the buffer, perhaps you can just add the first character the buffer manually.
Add a macro for the break character
#define BREAK_CHAR '~'
The code spinet could then look like this:
int c;
while(1)
{
c = getchar();
/* check if we should break */
if(c == BREAK_CHAR)
break;
/* assign the first character that was already read in */
StrIn[0] = c;
/* get the rest of the string */
fgets( StrIn + 1, BUF_SIZE - 2, stdin );
}

if you use windows - use windows specific functions as well. In my function ESC cancels the input.
char *wingetsn(char *str, size_t maxlen)
{
size_t len = 0;
int ch;
int exit = 0;
while (len < maxlen - 1 && !exit)
{
while(!_kbhit());
switch((ch = _getch()))
{
case 27:
str[0] = 0;
exit = 1;
break;
case '\r':
exit = 1;
break;
case '\n':
break;
default:
printf("%c", ch);
str[len++] = ch;
break;
}
}
str[len++] = 0;
return str;
}

Related

How to accept string input only if it of certain length in C else ask user to input the string again

How to accept set of strings as input in C and prompt the user again to re-enter the string if it exceeds certain length. I tried as below
#include<stdio.h>
int main()
{
char arr[10][25]; //maximum 10 strings can be taken as input of max length 25
for(int i=0;i<10;i=i+1)
{
printf("Enter string %d:",i+1);
fgets(arr[i],25,stdin);
}
}
But here fgets accepts the strings greater than that length too.
If the user hits return, the second string must be taken as input. I'm new to C
How to accept string input only if it of certain length
Form a helper function to handle the various edge cases.
Use fgets(), then drop the potential '\n' (which fgets() retains) and detect long inputs.
Some untested code to give OP an idea:
#include <assert.h>
#include <stdio.h>
// Pass in the max string _size_.
// Return NULL on end-of-file without input.
// Return NULL on input error.
// Otherwise return the buffer pointer.
char* getsizedline(size_t sz, char *buf, const char *reprompt) {
assert(sz > 0 && sz <= INT_MAX && buf != NULL); // #1
while (fgets(buf, (int) sz, stdin)) {
size_t len = strlen(buf);
// Lop off potential \n
if (len > 0 && buf[--len] == '\n') { // #2
buf[len] = '\0';
return buf;
}
// OK if next ends the line
int ch = fgetc(stdin);
if (ch == '\n' || feof(stdin)) { // #3
return buf;
}
// Consume rest of line;
while (ch != '\n' && ch != EOF) { // #4
ch = fgetc(stdin);
}
if (ch == EOF) { // #5
return NULL;
}
if (reprompt) {
fputs(reprompt, stdout);
}
}
return NULL;
}
Uncommon: reading null characters remains a TBD issue.
Details for OP who is a learner.
Some tests for sane input parameters. A size of zero does not allow for any input saved as a null character terminated string. Buffers could be larger than INT_MAX, but fgets() cannot directly handle that. Code could be amended to handle 0 and huge buffers, yet leave that for another day.
fgets() does not always read a '\n'. The buffer might get full first or the last line before end-of-file might lack a '\n'. Uncommonly a null character might be read - even the first character hence the len > 0 test, rendering strlen() insufficient to determine length of characters read. Code would need significant changes to accommodate determining the size if null character input needs detailed support.
If the prior fgets() filled its buffer and the next read character attempt resulted in an end-of-file or '\n', this test is true and is OK, so return success.
If the prior fgetc() resulted in an input error, this loops exits immediately. Otherwise, we need to consume the rest of the line looking for a '\n' or EOF (which might be due to an end-of-file or input error.)
If EOF returned (due to an end-of-file or input error), no reason to continue. Return NULL.
Usage
// fgets(arr[i],25,stdin);
if (getsizedline(arr[i], sizeof(arr[i]), "Too long, try again.\n") == NULL) {
break;
}
This code uses a buffer slightly larger than the required max length. If a text line and the newline can't be read into the buffer, it reads the rest of the line and discards it. If it can, it again discards if too long (or too short).
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define INPUTS 10
#define STRMAX 25
int main(void) {
char arr[INPUTS][STRMAX+1];
char buf[STRMAX+4];
for(int i = 0; i < INPUTS; i++) {
bool success = false;
while(!success) {
printf("Enter string %d: ", i + 1);
if(fgets(buf, sizeof buf, stdin) == NULL) {
exit(1); // or sth better
}
size_t index = strcspn(buf, "\n");
if(buf[index] == '\0') { // no newline found
// keep reading until end of line
while(fgets(buf, sizeof buf, stdin) != NULL) {
if(strchr(buf, '\n') != NULL) {
break;
}
}
if(feof(stdin)) {
exit(1); // or sth better
}
continue;
}
if(index < 1 || index > STRMAX) {
continue; // string is empty or too long
}
buf[index] = '\0'; // truncate newline
strcpy(arr[i], buf); // keep this OK string
success = true;
}
}
printf("Results:\n");
for(int i = 0; i < INPUTS; i++) {
printf("%s\n", arr[i]);
}
return 0;
}
The nice thing about fgets() is that it will place the line-terminating newline character ('\n') in the input buffer. All you have to do is look for it. If it is there, you got an entire line of input. If not, there is more to read.
The strategy then, is:
fgets( s, size_of_s, stdin );
char * p = strpbrk( s, "\r\n" );
if (p)
{
// end of line was found.
*p = '\0';
return s; (the complete line of input)
}
If p is NULL, then there is more work to do. Since you wish to simply ignore lines that are too long, that is the same as throwing away input. Do so with a simple loop:
int c;
do c = getchar(); while ((c != EOF) && (c != '\n'));
Streams are typically buffered behind the scenes, either by the C Library or by the OS (or both), but even if they aren’t this is not that much of an overhead. (Use a profiler before playing “I’m an optimizing compiler”. Don’t assume bad things about the C Library.)
Once you have tossed everything you didn’t want (to EOL), make sure your input isn’t at EOF and loop to ask the user to try again.
Putting it all together
char * prompt( const char * message, char * s, size_t n )
{
while (!feof( stdin ))
{
// Ask for input
printf( "%s", message );
fflush( stdout ); // This line _may_ be necessary.
// Attempt to get an entire line of input
if (!fgets( s, n, stdin )) break;
char * p = strpbrk( s, "\r\n" );
// Success: return that line (sans newline character(s)) to the user
if (p)
{
*p = '\0';
return s;
}
// Failure: discard the remainder of the line before trying again
int c;
do c = getchar(); while ((c != EOF) && (c != '\n'));
}
// If we get this far it is because we have
// reached EOF or some other input error occurred.
return NULL;
}
Now you can use this utility function easily enough:
char user_name[20]; // artificially small
if (!prompt( "What is your name (maximum 19 characters)? ", user_name, sizeof(user_name) ))
{
complain_and_quit();
// ...because input is dead in a way you likely cannot fix.
// Feel free to check ferror(stdin) and feof(stdin) for more info.
}
This little prompt function is just an example of the kinds of helper utility functions you can write. You can do things like have an additional prompt for when the user does not obey you:
What is your name? John Jacob Jingleheimer Schmidt
Alas, I am limited to 19 characters. Please try again:
What is your name? John Schmidt
Hello John Schmidt.

Why does my code register "enter" as an input? [duplicate]

This question already has an answer here:
Canonical vs. non-canonical terminal input
(1 answer)
Closed 4 years ago.
I'm trying to write a simple program that asks a user to choose from a menu in a loop.
I use getchar() to get the input, however i've noticed that when I enter a char and press 'Enter' the program makes two loops (as if i pressed twice) one the char as an input and another for 'Enter' as an input.
How do I fix this?
getchar() returns the first character in the input buffer, and removes it from the input buffer. But other characters are still in the input buffer (\n in your example). You need to clear the input buffer before calling getchar() again:
void clearInputBuffer() // works only if the input buffer is not empty
{
do
{
c = getchar();
} while (c != '\n' && c != EOF);
}
The easiest way is to filter out the enter key as the return value from getchar
char c = (char)getchar();
if ( c != '\n' ) {
...
}
Add a getchar() after the getchar() :P
How about
#include <stdio.h>
/*! getline() reads one line from standard input and copies it to line array
* (but no more than max chars).
* It does not place the terminating \n in line array.
* Returns line length, or 0 for empty line, or EOF for end-of-file.
*/
int getline(char line[], int max)
{
int nch = 0;
int c;
max = max - 1; /* leave room for '\0' */
while ((c = getchar()) != EOF) {
if (c == '\n')
break;
if (nch < max) {
line[nch] = c;
nch = nch + 1;
}
}
if (c == EOF && nch == 0)
return EOF;
line[nch] = '\0';
return nch;
}
Source
You've kind of answered your own question; you have to deal with the newline character somehow.
There are several options. If your menu options are numbered, you can use scanf() to read in an integer value and switch based on that:
printf("Pick an option: ");
fflush(stdout);
scanf("%d", &option);
switch(option)
{
case 0 : do_something(); break;
case 1 : do_something_else(); break;
...
default: bad_option(); break;
}
The advantage of this option is that the %d conversion specifier skips over any leading whitespace, including newline characters, so you don't have to worry about any unread \n clogging up the input stream (in fact, most of the conversion specifiers skip leading whitespace; %c doesn't, making it behave a lot like getchar()).
The disadvantage of this option is that if someone fat-fingers a non-digit character in their input, it won't be read with the %d conversion specifier, and will stay stuck in the input stream until a call to getchar() or scanf() with a %s or %c conversion specifier.
A better option is to read all input as character strings using fgets(), then parse and validate as necessary.
/**
* Prints a prompt to stdout and reads an input response, writing
* the input value to option.
*
* #param prompt [in] - prompt written to stdout
* #param option [out] - option entered by user
*
* #return - 1 on success, 0 on failure. If return value is 0, then option
* is not changed.
*/
int getOption(const char *prompt, char *option)
{
char input[3]; // option char + newline + 0 terminator
int result = 0;
printf("%s: ", prompt);
fflush(stdout);
if (fgets(input, sizeof input, stdin))
{
/**
* Search for a newline character in the input buffer; if it's not
* present, then the user entered more characters than the input buffer
* can store. Reject the input, and continue to read from stdin until
* we see a newline character; that way we don't leave junk in the
* input stream to mess up a future read.
*/
char *newline = strchr(input, '\n');
if (!newline)
{
printf("Input string is too long and will be rejected\n");
/**
* Continue reading from stdin until we find the newline
* character
*/
while (!newline && fgets(input, sizeof input, stdin))
newline = strchr(input, '\n');
}
else
{
*option = input[0];
result = 1;
}
}
else
printf("Received error or EOF on read\n");
return result;
}
Yes, that's a lot of work to read in one stupid menu option, and that's the simple version. Welcome to the wonderful world of interactive input processing in C.

Ending a while loop when user hits enter, cannot use #include <string.h>

In this scenario, user input is taken from stdin using fgets. Normally to end a while loop when a user hits enter I would use strcmp between the fgets value and \n, but we are not allowed to use #include <string.h> in this particular assignment. Using C99.
You can't, since the fgets() function returns when it finds an \n
I'm assuming you mean when the user input a single \n and nothing else. Itmight be a better idea to use fgetc() instead, which will return the \n.
This means you need to buffer the inout yourself, something like this:
char inputBuffer[120] = "";
char ch;
char chCount = 0;
while (1) {
ch = fgetc(stdin);
if (ch == '\n') {
/* Empty buffer? */
if (inputBuffer[0] == '\0')
/* Oui! */
break;
/* Buffer isn't empty - do something with it... */
fprintf(stdout, "Input buffer: %s\n", inputBuffer);
/* Clear the buffer for the next line of input and reset the
* counter. */
inputBuffer[0] = '\0';
chCount = 0;
}
else {
if (chCount < 119) {
/* Add the byte to the buffer. */
inputBuffer[chCount++] = ch;
inputBuffer[chCount] = '\0';
}
}
}
The above loop will output any input string or break if a single \n is entered.

C read lines from stdin ending by pressing Enter

I'm writing a program for project in C, where I have such kind of input:
............xcx............
.........qeztodlea.........
.......ecnedivorpuzy.......
.....bqfjwxqindnrsatrs.....
....ucaamadisonoctoieax....
...ozkttqdxwltstaivcilex...
...ujknnakigzfasxninltxc...
..rabxaa...kohce...oelnyd..
..rithls...momrl...spayvh..
honolulu
oklahomacity
charleston
madison
montgomery
saltlakecity
springfield
First set of data is separated from second data set by empty line, I need on one Enter press process it.
If I copy-past this data in terminal window and press Enter and then Ctr+D ( which means end of input ) it works fine, but if to press only Enter I still need to enter data. I can't understand what to change so only on first Enter I'll finish input and proceed to my program? I know that this question sounds stupid, but in my function for reading line I use fgetc, because I need to check some letters, if to use e.g. fgets then it will stop on first nl, which function to use? Maybe I don't get something, is it possible in general?
I already have rLine function for reading line ( using fgetc ):
char * rLine( int * length, int * ha ){
char *buff = malloc( LMAX ), *old = buff;
int count = 0, maxlen = LMAX, len = maxlen, c;
while ( (c = fgetc( stdin ) ) != '\n' ){
if ( c == EOF ) { *ha = R_EOF; break; }
if ( /* some conditions for c */ ) *ha = R_FALSE;
*buff ++ = c;
count++;
if ( -- len == 0 ){
len = maxlen;
buff = (char *)realloc( old, maxlen *= 2 );
old = buff;
buff += count;
}
}
*length = count;
*buff = '\0';
return old;
}
, where ha some kind of error-message handler. Tnx
NOTE: OK, I've found out that end of input is driven same as CTRL + D combination. so actually the check if ( c == EOF ) ( or c == '\0' ) works fine for me. So actually the question can be closed by now.
Are you familiar with '\n' for a new line and '\r\n' for carriage return?
add this line and handle the new line case:
if ( c == '\n' ) { // that is a new line }
Have you seen this post:
How to read a line from the console in C?

Using getchar() on c gets the 'Enter' after input [duplicate]

This question already has an answer here:
Canonical vs. non-canonical terminal input
(1 answer)
Closed 4 years ago.
I'm trying to write a simple program that asks a user to choose from a menu in a loop.
I use getchar() to get the input, however i've noticed that when I enter a char and press 'Enter' the program makes two loops (as if i pressed twice) one the char as an input and another for 'Enter' as an input.
How do I fix this?
getchar() returns the first character in the input buffer, and removes it from the input buffer. But other characters are still in the input buffer (\n in your example). You need to clear the input buffer before calling getchar() again:
void clearInputBuffer() // works only if the input buffer is not empty
{
do
{
c = getchar();
} while (c != '\n' && c != EOF);
}
The easiest way is to filter out the enter key as the return value from getchar
char c = (char)getchar();
if ( c != '\n' ) {
...
}
Add a getchar() after the getchar() :P
How about
#include <stdio.h>
/*! getline() reads one line from standard input and copies it to line array
* (but no more than max chars).
* It does not place the terminating \n in line array.
* Returns line length, or 0 for empty line, or EOF for end-of-file.
*/
int getline(char line[], int max)
{
int nch = 0;
int c;
max = max - 1; /* leave room for '\0' */
while ((c = getchar()) != EOF) {
if (c == '\n')
break;
if (nch < max) {
line[nch] = c;
nch = nch + 1;
}
}
if (c == EOF && nch == 0)
return EOF;
line[nch] = '\0';
return nch;
}
Source
You've kind of answered your own question; you have to deal with the newline character somehow.
There are several options. If your menu options are numbered, you can use scanf() to read in an integer value and switch based on that:
printf("Pick an option: ");
fflush(stdout);
scanf("%d", &option);
switch(option)
{
case 0 : do_something(); break;
case 1 : do_something_else(); break;
...
default: bad_option(); break;
}
The advantage of this option is that the %d conversion specifier skips over any leading whitespace, including newline characters, so you don't have to worry about any unread \n clogging up the input stream (in fact, most of the conversion specifiers skip leading whitespace; %c doesn't, making it behave a lot like getchar()).
The disadvantage of this option is that if someone fat-fingers a non-digit character in their input, it won't be read with the %d conversion specifier, and will stay stuck in the input stream until a call to getchar() or scanf() with a %s or %c conversion specifier.
A better option is to read all input as character strings using fgets(), then parse and validate as necessary.
/**
* Prints a prompt to stdout and reads an input response, writing
* the input value to option.
*
* #param prompt [in] - prompt written to stdout
* #param option [out] - option entered by user
*
* #return - 1 on success, 0 on failure. If return value is 0, then option
* is not changed.
*/
int getOption(const char *prompt, char *option)
{
char input[3]; // option char + newline + 0 terminator
int result = 0;
printf("%s: ", prompt);
fflush(stdout);
if (fgets(input, sizeof input, stdin))
{
/**
* Search for a newline character in the input buffer; if it's not
* present, then the user entered more characters than the input buffer
* can store. Reject the input, and continue to read from stdin until
* we see a newline character; that way we don't leave junk in the
* input stream to mess up a future read.
*/
char *newline = strchr(input, '\n');
if (!newline)
{
printf("Input string is too long and will be rejected\n");
/**
* Continue reading from stdin until we find the newline
* character
*/
while (!newline && fgets(input, sizeof input, stdin))
newline = strchr(input, '\n');
}
else
{
*option = input[0];
result = 1;
}
}
else
printf("Received error or EOF on read\n");
return result;
}
Yes, that's a lot of work to read in one stupid menu option, and that's the simple version. Welcome to the wonderful world of interactive input processing in C.

Resources