user1556464
user1556464

Reputation: 47

How to check for '\n' using scanf

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

Answers (4)

VietHTran
VietHTran

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

Michael Veksler
Michael Veksler

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

0___________
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

chux
chux

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

Related Questions