Reputation: 47
Apologies C noob here,
I am trying to validate user input from stdin but cant find a way to handle a single \n
input. To completely simplify the problem I wrote the below. When \n
is entered scanf
finishes, if this is the first character entered e.g A user hits enter straight away, 6
will be output by the printf
statement.
int main(){
char x[6];
scanf("%[^\n]s", x);
printf("%i\n", strlen(x));
return 0;
}
My issue is I cant find a way of telling if a user entered a \n
or 6 legitimate characters. I would also like to set a MAX input check and Im not sure if scanf
is the write function to take user input with in this case or fgets
may be better suited.
Upvotes: 2
Views: 1465
Reputation: 2318
You can use getchar()
to check each individual input character and stop taking in input when the input size is 6 or the user enter \n
.
Edit: I've updated my code according to @chux's suggestion. You should increase the array size to 7 so that the last character can be the null terminator when the input exceeds 6 characters. I also used an int variable c
to store return values from getchar
instead of storing directly to x[i] so that it can identify EOF
.
#include<stdio.h>
#include<string.h>
int main(){
char x[7];
int c;
size_t i = 0;
while (i < 6 && (c=getchar()) != EOF) {
x[i] = c;
if (x[i] == '\n') {
break;
}
++i;
}
x[i] = '\0'; // null-terminate string
printf("%s\n", x);
printf("%lu\n", strlen(x));
return 0;
}
Upvotes: 1
Reputation: 8475
Besides the main question, the code has several issues:
int main(){ char x[6]; // 1. an attack vector: lines with more than 6 characters will exceed buffer size // 2. The s will never be matched, // since %[^\n] consumes everything until EOF or \n // the following char can't be 's' scanf("%[^\n]s", x); // if scanf fails, then x can be uninitialized: printf("%i\n", strlen(x)); return 0; }
Fixing problems one by one:
scanf("%[^\n]", x); // still not good
This will read until newline and stop, but still may overflow the buffer:
scanf ("%5[^\n]", x); // better
This will avoid buffer overflow, by limiting the read to 5 chars. Unfortunately, if the input is long, there is no guarantee that a null terminator will be written at the end. Also, if scanf fails (like in your case) then x will be uninitialized:
char x[6] = ""; // fill NUL chars
scanf ("%5[^\n]", x); // better
It will work badly if after reading 3 chars, for example, there will be an IO problem. To combat that sort of issues, check the return code:
char x[6] = ""; // fill NUL chars
int n_read = scanf ("%5[^\n]", x); // ok
if (n_read < 0) error...
else if (n_read == 0) newline or EOF...
else /*1*/ x is ready...
Note, that instead of initializing x = ""
, it is probably better to set x[5] = '\0'
after scanf. I have chosen the initialization option instead, since it was easier to present the answer gradually that way.
Upvotes: 0
Reputation: 67476
#include <stdio.h>
int islegitimate(int c)
{
if(c < ' ' || c > 127)
return 0;
return 1;
}
int isfirstNlegitimate(const char *str, size_t N)
{
for(size_t index = 0; index < N; index++)
{
if(!islegitimate(str[index])) return 0;
}
return 1;
}
int main(void) {
char x[7] ;
fgets(x, 7, stdin);
printf("The first 6 chars of the entered string are %slegitimate", isfirstNlegitimate(x,6) ? "" : "not ")
return 0;
}
You can also remove trailing end of the lines chars (\n in linux \r\n in Windows)
char *removetrailinglineends(char *str)
{
size_t len = strlen(str);
char *end = str + !len ? str : str + len - 1;
while(end > str)
{
if(*end == '\n' || *end == '\r') *end = 0;
end--;
}
}
Upvotes: 0
Reputation: 153458
When \n is entered scanf finishes, if this is the first character entered e.g A user hits enter straight away, 6 will be output by the printf statement.
Undefined behavior. Nothing was read into uninitialized x[]
, so strlen(x)
is UB.
scanf("%[^\n]s", x);
lacks width protection. It has no check of return value. Detecting a '\n'
only is not trivial.
Use fgets()
.
int main(){
// char x[6];
char x[7]; // add at least 1 (useful for the \n code may read)
// scanf("%[^\n]s", x);
if (fgets(x, sizeof x, stdin)) {
x[strcspn(x, "\n")] = '\0'; // lop off potential trailing \n
// printf("%i\n", strlen(x));
printf("%zu\n", strlen(x)); // use matching specifier
return 0;
}
}
Upvotes: 0