How to read arbitrary length input from stdin properly? - c

I want to ask user to input some string in each loop. However, the code below will skip loop multiple times if the length of input is greater than 2?
Why is this happening and what's the best way to read arbitrary length input from stdin?
#include <stdio.h>
int main(void)
{
char in[2];
while (in[0] != 'q') {
puts("Enter: ");
fgets(in, 3, stdin);
}
return 0;
}

First, your buffer is not large enough to hold a 2 character string. Strings in C need to have an extra '\0' appended to them to mark the end. Second, fgets will leave chars in the input buffer when the input string is longer than the buffer passed to it. You need to consume those characters before calling fgets again.
Here is a function, similar to fgets in that it will only read buffer_len - 1 chars. And it will also consume all chars until the newline or EOF.
int my_gets(char *buffer, size_t buffer_len)
{
// Clear buffer to ensure the string will be null terminated
memset(buffer, 0, buffer_len);
int c;
int bytes_read = 0;
// Read one char at a time until EOF or newline
while (EOF != (c = fgetc(stdin)) && '\n' != c) {
// Only add to buffer if within size limit
if (bytes_read < buffer_len - 1) {
buffer[bytes_read++] = (char)c;
}
}
return bytes_read;
}
int main(void)
{
char in[3]; // Large enough for a 2 char string
// Re-arranged to do/while
do {
puts("Enter: ");
my_gets(in, sizeof(in)); // sizeof(in) == 3
} while (in[0] != 'q');
return 0;
}

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.

C - Is there a way to read a single character of user input, and not have the rest "pushed down" to the next request for input?

So, I'm working on a simple hangman game in C, and I have the function read_guess, shown below.
void read_guess(char *guesses, char *p_current_guess)
{
int valid_guess = 0;
// Repeatedly takes input until guess is valid
while (valid_guess == 0)
{
printf(">>> ");
fgets(p_current_guess, 2, stdin);
if (!isalpha(*p_current_guess)) printf("Guesses must be alphabetic. Please try again.\n\n");
else
{
valid_guess = 1;
// Iterates over array of guesses and checks if letter has already been guessed
for (int i = 0; guesses[i] != '\0'; i++)
{
if (guesses[i] == *p_current_guess)
{
printf("You have already guessed this letter. Please try again.\n\n");
valid_guess = 0;
break;
}
}
}
}
}
I've tried all the standard input functions (including getchar), but with all of them, when an input larger than one character is supplied, instead of taking just the first character and moving on (or asking again), the rest of the input is "pushed back", and the next time input is requested, whether it be because the input contained a non-alphabetic character or the next round begins, the rest of the input is automatically processed. This repeats for each character of the input.
How can I avoid this?
You are using fgets which is good, but unfortunately not the right way...
fgets reads up to an end of line or at most 1 less the the number of character asked. And of course remaining characters are left for the next read operation...
The idiomatic way would be to ensure reading up to the end of line, whatever the length, or at least up to a much larger length.
Simple but could fail in more than SIZE characters on input:
#define SIZE 64
...
void read_guess(char *guesses, char *p_current_guess)
{
char line[SIZE];
int valid_guess = 0;
// Repeatedly takes input until guess is valid
while (valid_guess == 0)
{
printf(">>> ");
fgets(line, SiZE, stdin); // read a line of size at most SIZE-1
p_current_guess[0] = line[0]; // keep first character
p_current_guess[1] = '\0';
...
Robust but slightly more complex
/**
* Read a line and only keep the first character
*
* Syntax: char * fgetfirst(dest, fd);
*
* Parameters:
* dest: points to a buffer of size at least 2 that will recieve the
* first character followed with a null
* fd : FILE* from which to read
*
* Return value: dest if one character was successfully read, else NULL
*/
char *readfirst(dest, fd) {
#define SIZE 256 // may be adapted
char buf[SIZE];
char *cr = NULL; // return value initialized to NULL if nothing can be read
for (;;) {
if(NULL == fgets(buff, sizeof(buff), fd)) return cr; // read error or end of file
if (0 == strcspn(buff, "\n")) return cr; // end of file
if (cr == NULL) { // first read:
cr = dest; // prepare to return first char
dest[0] = buff[0];
dest[1] = 0;
}
}
}
You can then use it simply in your code:
void read_guess(char *guesses, char *p_current_guess)
{
int valid_guess = 0;
// Repeatedly takes input until guess is valid
while (valid_guess == 0)
{
printf(">>> ");
fgetfirst(p_current_guess, stdin);
You can discard all input until end-of-line, each time you want to ask for input.
void skip_to_eol(FILE* f, int c)
{
while (c != EOF && c != '\n')
c = fgetc(f);
}
...
char c = getchar(); // instead of fgets
skip_to_eol(stdin, c);
You can use getch() function on windows to get single character. and this is linux equivalent
What is the equivalent to getch() & getche() in Linux?

C: how to read many strings from stdin

I need to read many strings with a variable length from stdin up to EOF.
The strings are made of consecutive chars, not separated by blanks and there is not a maximum length for the strings.
Using:
char st[101];
while(scanf("%100s",st) != EOF){ //divide the input into parts of 100 chars
int i;
for(i=0; i<strlen(st);i++){
printf("%c",st[i]);
}
}
I can not tell if the current string is part of a previous one or a new one because it is printed all in a single line (and I've no idea where to print "\n").
Sample input:
aaaaaaaaa[...]aaaaa
bbbbbbbbb[...]bbbbb
output:
aaaa[...]aabbbbbb[...]bbbb
Any solution?
P.S. Using getchar() I'm not able to handle the EOF.
Here's how you handle EOF with getchar:
int c;
while ((c = getchar()) != EOF) {
putchar(c);
}
This code will replicate its input on stdout.
If you want to deal with whole lines at a time, the easiest way is to use getline if you have it (it's POSIX, not standard C):
char *line = NULL;
size_t size = 0;
ssize_t nread;
while ((nread = getline(&line, &size, stdin)) != -1) {
// nread characters were read
do_stuff_with(line);
}
free(line);
If you don't have getline, you get to write your own (using getchar and realloc).

switching from getchar to fgets

I am trying to switching my use of getchar to fgets but, when using getchar, the entire code does not work.
//fgets(line, sizeof(line), stdin);
while(fgets(line, sizeof(line), stdin))
{
portNum[sizeof(line)] = (char)line;
}
while((c = getchar()) != '\n')
{
portNum[num++] = c;
}
portNum[num] = '\0';
How can I make equal for those two functions to work properly?
You usage of fgets is wrong.
fgets Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
In your case fgets will read all the characters until newline is encountered.
Also, the parameters usage is wrong.
char * fgets ( char * str, int num, FILE * stream );
str => Pointer to an array of chars where the string read is copied.
num => Maximum number of characters to be copied into str (including the
terminating null-character).
stream => Pointer to a FILE object that identifies an input stream.
stdin can be used as argument to read from the standard input.
Refer to the fgets documentation for more information.
fgets man page
OP's fgets() usage is unclear and portNum[sizeof(line)] = (char)line; is certainly in error.
Instead: how to make the below getchar() code more fgets()-like:
// assumed missing code
#define N 100
int c;
char portNum[N];
size_t num = 0;
// size and EOF detection added (which should have been there)
while(num + 1 < sizeof portnum && (c = getchar()) != '\n' && c != EOF) {
portNum[num++] = c;
}
portNum[num] = '\0';
// assumed missing code
if (c == EOF && num == 0) Handle_EndOfFile_or_InputError();
else ...
This can be replaced with fgets() code
#define N 100
char portNum[N+1]; // 1 larger for the \n
if (fgets(portNum, sizeof portNum, stdin)) {
// lop off potential trailing \n
portNum[strcspn(portNum, "\n")] = '\0';
...
} else {
Handle_EndOfFile_or_InputError();
}

How to use scanf() to capture only Strings

Hi i am new to C and i am trying to use the Character array type below to captures input from users. How do i prevent or escape numerical characters. I just want only strings to be captured.
char str_input[105];
In have tried
scanf("%[^\n]s",str_input);
scanf("%[^\n]",str_input);
scanf("%[^0-9]",str_input);
scanf("%[A-Zaz-z]",str_input);
str_input = fgetc(stdin);
None of the above worked for me.
Input
2
hacker
Expected Output
Hce akr
int main() {
char *str_input;
size_t bufsize = 108;
size_t characters;
str_input = (char *)malloc(bufsize * sizeof(char));
if (str_input == NULL)
{
perror("Unable to allocate buffer");
exit(1);
}
characters = getline(&str_input,&bufsize,stdin);
printf("%zu characters were read.\n",characters);
int i;
int len = 0;
for (i = 0, len = strlen(str_input); i<=len; i++) {
i%2==0? printf("%c",str_input[i]): 'b';
}
printf(" ");
for (i = 0, len = strlen(str_input); i<=len; i++) {
i%2!=0? printf("%c",str_input[i]): 'b';
}
return 0;
}
Error
solution.c: In function ‘main’:
solution.c:21:5: warning: implicit declaration of function ‘getline’ [-Wimplicit-function-declaration]
characters = getline(&str_input,&bufsize,stdin);
Since your buffer has limited size, then using fgets(3) is fine. fgets() returns NULL on failure to read a line, and appends a newline character at the end of the buffer.
In terms of preventing numerical characters from being in your buffer, you can simply create another buffer, and only add non-numerical characters to it. You could just delete the numerical characters from your original buffer, but this can be a tedious procedure if you are still grasping the basics of C. Another method would be just to read single character input with getchar(3), which would allow you assess each character and simply ignore numbers. THis method is by far the easiest to implement.
Since you asked for an example of using fgets(), here is some example code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define INPUTSIZE 108
int main(void) {
char str_input[INPUTSIZE], characters[INPUTSIZE];
size_t slen, char_count = 0;
printf("Enter input:\n");
if (fgets(str_input, INPUTSIZE, stdin) != NULL) {
/* removing newline from fgets() */
slen = strlen(str_input);
if (slen > 0 && str_input[slen-1] == '\n') {
str_input[slen-1] = '\0';
} else {
fprintf(stderr, "Number of characters entered exceeds buffer size\n");
exit(EXIT_FAILURE);
}
/* checking if string is valid */
if (*str_input == '\0') {
fprintf(stderr, "No input found\n");
exit(EXIT_FAILURE);
}
printf("Buffer: %s\n", str_input);
/* only adding non-numbers */
for (size_t i = 0; str_input[i] != '\0'; i++) {
if (!isdigit(str_input[i])) {
characters[char_count++] = str_input[i];
}
}
/* terminating buffer */
characters[char_count] = '\0';
printf("New buffer without numbers: %s\n", characters);
}
return 0;
}
Example input:
Enter input:
2ttt4y24t4t3t2g
Output:
Buffer: 2ttt4y24t4t3t2g
New buffer without numbers: tttytttg
Update:
You could just use this even simpler approach of ignoring non-number characters:
char str_input[INPUTSIZE];
int ch;
size_t char_count = 0;
while ((ch = getchar()) != EOF && ch != '\n') {
if (!isdigit(ch)) {
if (char_count < sizeof(str_input)) {
str_input[char_count++] = ch;
}
}
}
str_input[char_count] = '\0';
If you're using Linux, I would use the getline() function to get a whole line of text, then verify it. If it is not valid input, I would in a loop ask the user to enter a line of text again and again until you the input is acceptable.
If not using Linux, well, your best bet is probably to reimplement getline(). You can also use fgets() if you find a limited-size buffer acceptable. I don't find limited-size buffers acceptable, so that's why I prefer getline().
getline() is used according to the way explained in its man page: http://man7.org/linux/man-pages/man3/getdelim.3.html
Basically, your loop should be something similar to:
char *buf = NULL;
size_t bufsiz = 0;
while (1)
{
if (getline(&buf, &bufsiz, stdin) < 0)
{
handle_error();
}
if (is_valid(buf))
{
break;
}
printf("Error, please re-enter input\n");
}
use_buffer(buf);
free(buf);
Well that's not possible. Numbers are string too. But you can set loop to look for numbers and print error. like this :
char *str = "ab234cid20kd", *p = str;
while (*p) { // While there are more characters to process...
if (isdigit(*p)) { // Upon finding a digit, ...
printf("Numbers are forbidden");
return 0;
} else {
p++;
}
}

Resources