User typing null terminator in scanf - c

Can a user type null terminator in an input, for which scanf is used, so the length of the input would be 0?
char msg[1000];
scanf("%1000s",msg); // the user would type nothing: '' or '\0'

On many systems, the answer is "Yes".
Usually, the magic sequence is Control-#.
The character code for # is usually 64, one less the that of A (65). Control-A is character code 1; one less is character code 0, aka '\0'.
Note that to get a zero-length input, you'd have to type the null byte as the first non-space character, and you would still need to hit return to enter the input. Your program would find it hard to reliably identify what else was entered after the null byte.

Some systems allow the terminal user to enter a NUL byte by pressing Ctrl-#. scanf() would then store this byte into the destination array, but would most likely not consider this byte to constitute a word separator, so input parsing would continue until end of file or a whitespace character such as newline is entered.
There is no way to tell from the destination array if a NUL byte was stored after other characters, nor precisely how many further characters have been read after this NUL byte. But if scanf() returned 1 and msg has a zero length, the only possibility is that the user entered a NUL byte after a possibly empty sequence of whitespace characters.
This would also be the only possibility for fgets() to return an empty string when passed a destination array with a size greater than 1.
If input is read from a file instead of a terminal, it is always a possibility for the file to contain NUL bytes and care should be taken to avoid undefined behavior if it happens. For example, here is a classic bug:
char msg[1000];
if (fgets(msg, sizeof msg, stdin)) {
int len = strlen(msg);
if (msg[len - 1] == '\n') { // potential undefined behavior!
msg[--len] = '\0' // potential undefined behavior!
}
...
}
Also note that your code has a flaw: the maximum number of characters to store into the destination should be 999, not 1000:
char msg[1000];
if (scanf("%999s",msg) == 1) {
/* word was read from the user */
if (*msg == '\0') {
printf("user entered a NUL byte\n");
}
}

Can a user type null terminator in an input, for which scanf is used, so the length of the input would be 0?
In addition to a user typing in a null character, stdin could use re-directed input that contains null characters.
foo < data.bin
Detail: When the first character scanned is a null character or '\0', it is read like any other character and saved like any other non-white-space character. So the length of input is still more than 0, but the value reported by strlen(msg) is fooled by the embedded null character and returns zero.
fgets() has the same problem as scanf() in this regard: strlen(msg) does not alway report the number of characters read.
Non-standard getline() does return the number of characters read and is a way out of this.
#Stargateur mentions using "%n" to store the number of characters saved in msg. Perhaps like
char msg[1000 + 1];
int start, end;
if (1 == scanf(" %n%1000s%n", &start, msg, &end)) {
printf("%d characters read\n", end - start);
}
IAC, we are trying to cope with a corner weakness of scanf() and family. The best read input solution depends on additional factors, not mentioned by OP.

Yes, it possible because '\0' is a non-white-space. scanf() will consider that is the end of the string. So %s can match a empty string.
You can use the m specifier to allocate a corresponding buffer to hold the input. Note this is POSIX 200112L standard.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *str;
int n;
if (scanf("%ms%n", &str, &n) == 1) {
printf("I read %d character(s).\n", n);
if (strlen(str) != 0) {
printf("str = %s\n", str);
}
else {
printf("str is empty");
}
free(str);
}
else {
printf("The user enter nothing");
}
}

Related

How does fgets treat enter key press?

What I am seeing is that while accepting a string - fgets takes the string till an enter key is pressed, and in the end it DOES NOT replace the enter key with \0 - rather appends \0 to the string including enter key.
In comparison, gets_s takes the string till enter key is pressed and in the end - replaces the last enter key with \0.
Is this correct. Please add, correct, confirm.
Also does this mean. my string handling operations that operate character by character till they encounter a \0 - will now - with fgets - operate on a newline character as well ?
#include <stdio.h>
#include <conio.h>
int main()
{
char i[32];
printf("Enter a string of size 32 or less characters \n");
gets_s(i, sizeof(i));
//fgets(i, sizeof(i), stdin);
printf("With right justification, field width = 32 and precision = 10 -> i = ||%32.10s||\n", i);
printf("With left justification, field width = 32 and precision = 10 -> i = ||%-32.10s||\n", i);
printf("With right justification, field width = 10 and precision = 32 -> i = ||%10.32s||\n", i);
printf("With left justification, field width = 10 and precision = 32 -> i = ||%-10.32s||\n", i);
_getch();
return 0;
}
How does fgets treat enter key press?
Just like any other key, other than it is 1 of 4 reasons to stop reading more.
" fgets takes the string till an enter key is pressed, " is a causal explanation.
Let us go deeper
char *fgets(char *s, int n, FILE *stream); does not read a string as C defines a string (characters up to and including the null character). fgets() reads a line of characters (characters up to and including the '\n'). It reads them up to a certain point - leaving the rest for later.
A '\n' is read (and saved).
The input buffer is nearly full. (all but 1)
End of file occurs.
Rare input error occurs.
In cases 1, 2 and 3 (if at least something was read), a null character is appended to the input. Now the input is certainly a string and the function return s. In other cases the function returns (char *) NULL.
Note the if fgets() reads null characters, they are treated like any other non-'\n'.
Enter causes '\n': That is a character too.
The '\n' might need to be discarded for later processing. In that case simply lop it off. Since the input might not contain '\n', robust code does not rely on its presence. Nor does robust code assume the first character is not a null character.
size_t len = strlen(s);
if (len > 0 && s[len-1] == '\n') {
s[--len] = '\0';
}
// OR
s[strcspsn(s, "\n")] = '\0';
OP's explanation of gets_s() has similar detail missing in OP's desciption: special functionality on buffer full, end-of-file and error.

How to check if user input numbers instead of string(array of char) in C

So, I got this assignment as a student that ask me to create a simple program using C.
This program input only allow you to input only characters A-Z, a-z, and (space).
and the length of the string should be no less than 1 character and no more than 100 characters.
So, I come with the conclusion that I should use if function to validate if the user input the allowed character.
#include <stdio.h>
#include <ctype.h>
int main()
{
char name[100];
scanf("%s",&name);
fflush(stdin);
if (isdigit(name))
^^^^
{
printf("Wrong answers");
getchar();
}
else
....
It was supposed to print "wrong answers" if you input numbers in there, but this program won't run.. It keeps saying :
error C2664: 'isdigit' : cannot convert parameter 1 from 'char [100]' to 'int'
I don't know what this error means.. Is there something I miss? Or am I using the wrong function?
I have also tried
if (((name>='A')&&(name<='Z'))||((name>='a')&&(name<='z')||)((name==' ')))
{
//this print what i want
}
else
{
printf("wrong answers");//this print "wrong answer"
}
but it always print "wrong answers" no matter I input the correct input or the wrong input.
Your help is highly appreciated.
Thank you.
*ps : I am a beginner at programming.
isdigit() takes an int as argument, not a char*:
int isdigit(int c);
You have to use a loop over the string and check each character in it.
Having said that, to achieve:
this program input only allow you to input only characters 'A'-'Z', 'a'-'z', and ' '(space)
you are better off using isalpha().
Try this out:
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char name[101], temp;
// take input one character at a time
while(scanf("%c", &temp)){
// stop when newline or carriage return
if(temp == '\n' || temp == '\0' || !isalpha(temp) ){
break;
}
// save character in array
name[i] = temp;
// move to the next position of the array
i++;
}
printf( "%s", temp );
return 0;
}
The problem you're seeing is that you're passing isdigit the wrong type of value - it expects an int, but you're passing it an array of char. You would have to loop over each and every character in your string to check if it's a digit or not.
But that is ultimately not what you're after as you're looking to confirm that the string contains letters or spaces - there are lots of characters that could be entered that aren't classed as digits that would be accepted incorrectly.
What would be the easiest solution for you, is to use the function strspn. It takes a string and returns the length of how many characters match the second parameter. If that length is the same length as your string, you know that it only contains valid characters.
size_t valid;
valid=strspn(name, "abcdefg(fill in with other valid characters)");
if(valid==strlen(name))
{
// Valid name
}
else
{
// Not valid
}
If you need to expand the accepted characters, it's just a simple case of adding them to the 2nd parameter.
OP's code fails as isdigit() test is a single character is a digit (0-9). It does not test a string.
int isdigit(int c);
The isdigit function tests for any decimal-digit character.
In all cases the argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value of the macro EOF.
OP's buffer is too small to save 100 characters read from the user. At least 1 more needed to detect if too many were read and 1 more for a null character to mark the end of a string.
fflush(stdin); has its problems too.
scanf("%s",&name); does not save white-space. The parameter should have been name too. (no &)
Read a line of user input with fgets() which saves the result as a string.
Test if the input meets the criteria.
Read
#define N 100
// No need to be stingy on buffer size reading user input. Suggest 2x
// We need +1 for extra character detection, \n and \0
char buf[2*N + 1 + 1];
fgets(buf, sizeof buf, stdin);
// lop off potential \n
size_t length = strlen(buf);
if (length > 0 && buf[length-1] == '\n') {
buf[--length] = '\0';
}
Test
only characters 'A'-'Z', 'a'-'z', and ' '(space).
for (size_t i = 0; i<length; i++) {
if (!isalpha((unsigned char)buf[i]) && buf[i] != ' ') {
puts("Invalid chracter");
break;
}
}
length of the string should be no less than 1 character and no more than 100 characters.
if (length < 1 || length > 100) {
puts("Input wrong length");
}
Others approaches can be used to disqualify very long inputs. IMO, very long inputs represent an attack and should be handled differently than a simple line that was a bit too long.
if (length < 1 || length > 100) {
if (length + 2 >= sizeof buf) {
puts("Input way too long");
exit (EXIT_FAILURE);
}
puts("Input wrong length");
}
name must have one extra space for the \0 (NUL) character.
So to store 100 characters, its size should be at least 101.
char name[101];
You could first use
fgets(name, sizeof(name), stdin);
to read into name character array.
Note that fgets() will read in the trailing newline (\n) as well which need be removed like
name[strlen(name)-1]='\0';
Then use sscanf(). Like
size_t l=strlen(name);
sscanf(name, "%100[A-Za-z ]", name);
if(strlen(name)!=l)
{
printf("\nInvalid input.");
}
Note the space after the A-Za-z.
The 100 in the %100[A-Za-z] denotes reading at most 100 characters into name. The [A-Za-z ] will make the sscanf() stop reading if a non-alphabetic character which is not a space is encountered.
First read into name. Then store its length in l. Now read everything till a non-alphabet other than a space occurs in name to name itself (thereby modifying name).
Now compare the length of this new string with l. If they are the same. The input is valid as per your need.
You could also use scanf() instead of fgets() like
scanf("%100[^\n]", name);
which instructs to read every character till a \n into name. If this is used, no \n will added at the end of name unlike the case with fgets().
Now I would like to point out some mistakes in your code.
scanf("%s",&name);
will lead to errors. Correct one is
scanf("%s",name);
as the second argument here must be an address and since an array name in C decays into its base address, just name would do instead of &name.
As others have pointed out, using fflush() on stdin is undefined and must be avoided.
If you are trying to clear the input buffer till the next newline (\n), you could do
int ch;
while((ch=getchar())!='\n');// && ch!=EOF)
The argument of isdigit() must be a char and not a character array (type char [100] if size is 100).
And if is a statement and not a function.

Search whitespace in string inC

problem is when I try to enter a string with space compiler render that as separate 2 strings. But requirement is whenever there is a space in string don't treat it as 2 strings,but rather a single string. The program should print yes only if my four inputs are MAHIRL,CHITRA,DEVI and C. my code is:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[10],str2[10],str3[10],str4[10];
scanf("%s",str1);
scanf("%s",str2);
scanf("%s",str3);
scanf("%s",str4);
if(strcmp(str1,"MAHIRL")==0 && strcmp(str2,"CHITRA")==0 && strcmp(str3,"DEVI")==0 && strcmp(str4,"C")==0 ){
printf("yes");
}
else{
printf("no");
}
return 0;
}
I tried using strtok() and strpbrk(), but I'm not quite sure how to implement them in my code. Any help or suggestion is appreciated. Thanks.
problem is when I try to enter a string with space compiler render that as separate 2 strings
That's not a problem, that's the feature / behaviour of %s format specifier with scanf(). You cannot read space-delimited input using that.
For conversion specifier s, chapter §7.21.6.2, C11
s Matches a sequence of non-white-space characters. [...]
So, the matching ends as soon as it hits a white-space character, here, the space.
If you have to read a line (i.e., input terminated by newline), use fgets() instead.
The %s directive matches characters up to a whitespace before storing them, so it is not possible to get lines of input this way. There are other ways to use scanf() to read lines of input, but these are error-prone, and this is really not the right tool for the job.
Better to use fgets() to fetch a line of input to a buffer, and sscanf() to parse the buffer. Since the requirement here is that four strings are entered, this is a simple problem using this method:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[10],str2[10],str3[10],str4[10];
char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
return 1;
}
if (sscanf(buffer, "%9s%9s%9s%9s", str1, str2, str3, str4) == 4) {
if (strcmp(str1,"MAHIRL") == 0 &&
strcmp(str2,"CHITRA") == 0 &&
strcmp(str3,"DEVI") == 0 &&
strcmp(str4,"C") == 0 ){
printf("yes\n");
} else {
printf("no\n");
}
} else {
printf("Input requires 4 strings\n");
}
return 0;
}
An additional character array is declared, buffer[], with enough extra space to contain extra input; this way, if the user enters some extra characters, it is less likely to interfere with the subsequent behavior of the program. Note that fgets() returns a null pointer if there is an error, so this is checked for; an error message is printed and the program exits if an error is encountered here.
Then sscanf() is used to parse buffer[]. Note here that maximum widths are specified with the %s directives to avoid buffer overflow. The fgets() function stores the newline in buffer[] (if there is room), but using sscanf() in this way avoids needing to further handle this newline character.
Also note that sscanf() returns the number of successful assignments made; if this return value is not 4, the input was not as expected and the values held by str1,..., str4 should not be used.
Update
Looking at this question again, I am not sure that I have actually answered it. At first I thought that you wanted to use scanf() to read a line of input, and extract the strings from this. But you say: "whenever there is a space in string don't treat it as 2 strings", even though none of the test input in your example code contains such spaces.
One option for reading user input containing spaces into a string would be to use a separate call to fgets() for each string. If you store the results directly in str1,...,str4 you will need to remove the newline character kept by fgets(). What may be a better approach would be to store the results in buffer again, and then to use sscanf() to extract the string, this time including spaces. This can be done using the scanset directive:
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, " %9[^\n]", str1);
The format string here contains a leading space, telling sscanf() to skip over zero or more leading whitespace characters. The %[^\n] directive tells sscanf() to match characters, including spaces, until a newline is encountered, storing them in str1[]. Note that a maximum width of 9 is specified, leaving room for the \0 terminator.
If you want to be able to enter multiple strings, each containing spaces, on the same line of user input, you will need to choose a delimiter. Choosing a comma, this can be accomplished with:
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, " %9[^,], %9[^,], %9[^,], %9[^,\n]", str1, str2, str3, str4);
Here, there is a leading space as before, to skip over any stray whitespace characters (such as \n characters) that may be in the input. The %[^,] directives tell sscanf() to match characters until a comma is encountered, storing them in the appropriate array (str1[],..., str3[]). The following , tells sscanf() to match one comma and zero or more whitespace characters before the next scanset directive. The final directive is %[^,\n], telling sscanf() to match characters until either a comma or a newline are encountered.
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[10],str2[10],str3[10],str4[10];
char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
return 1;
}
/* Each individual string str1,..., str4 may contain spaces */
if (sscanf(buffer, " %9[^,], %9[^,], %9[^,], %9[^,\n]",
str1, str2, str3, str4) == 4) {
if (strcmp(str1,"test 1") == 0 &&
strcmp(str2,"test 2") == 0 &&
strcmp(str3,"test 3") == 0 &&
strcmp(str4,"test 4") == 0 ){
printf("yes\n");
} else {
printf("no\n");
}
} else {
printf("Input requires 4 comma-separated strings\n");
}
return 0;
}
Here is a sample interaction with this final program:
test 1, test 2, test 3, test 4
yes
While reading strings from user, use __fpurge(stdin) function from stdio_ext.h. This flushes out the stdin. When you are entering a string, you press enter at last, which is also a character. In order to avoid that, we use fpurge function.
scanf("%s",str1);
__fpurge(stdin);
scanf("%s",str2);
__fpurge(stdin);
scanf("%s",str3);
__fpurge(stdin);
scanf("%s",str4);
__fpurge(stdin);
Also if you want to input a string from user containing spaces, use following:
scanf("%[^\n]", str1);
This will not ignore the spaces you enter while inputting string.
EDIT: Instead of using fpurge function, one can use following code:
while( getchar() != '\n' );

Using strstr to determine if a given string contains a string with spaces [C]

I'm working through an example of using the strstr() function.
If I input "Pamela Sue Smith", why does the program output ""Pamela" is a sub-string!" and not ""Pamela Sue Smith" is a sub-string!".
#include <stdio.h>
#include <string.h>
void main(void)
{
char str[72];
char target[] = "Pamela Sue Smith";
printf("Enter your string: ");
scanf("%s", str);
if (strstr( target, str) != NULL )
printf(" %s is a sub-string!\n", str);
}
main does not have return-type void but int.
scanf can fail. Check the return-value.
If successful, it returns the number of parameters assigned.
%s only reads non-whitespace, until the next whitespace (thus 1 word).
%s does not limit how many non-whitespace characters are read. A buffer-overflow can be deadly.
Use %71s (buffer-size: string-length + 1 for the terminator)
You swapped the arguments to strstr.
From the manual page for scanf:
“s” — Matches a sequence of non-white-space characters; the next
pointer must be a pointer to character array that is long enough
to hold the input sequence and the terminating null byte ('\0'),
which is added automatically. The input string stops at white
space or at the maximum field width, whichever occurs first.
So, the part “Sue Smith” never makes it to str. You could use fgets which allows you to read a whole line at a time:
if (fgets(str, sizeof str, stdin) == NULL) {
printf("End of file\n");
return;
}
Note that in this case, str contains the terminating end-of-line character. You could do
if (*str != '\0')
str[strlen(str) - 1] = '\0';
to remove it.
(Also, as some others already pointed out, the “haystack” argument to strstr goes first.)

Read a fixed amount of characters using fgets in C

I'm trying to use the function fgets() in C to read 8 characters, and has to be 8 characters from stdin.
So far, I've got this
char HEX[9];
while (fgets(HEX, 8, stdin) != NULL)
{
if (HEX[8] == '\0')
{
printf("GOOD\n");
break;
}
else
{
printf("Not 8 characters");
}
}
But it's not working. Any ideas? Also tried using if (strlen(HEX) > 8) but neither.
Thanks in advance.
From the man pages:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte (aq\0aq) is stored after the last character in the buffer.
You are only reading seven characters.
One reason why this might not work is because you may be reading fewer characters than eight. Say, the user enters three characters, and presses [Enter]. In that case the char at position 8 will remain unassigned, so reading it would be undefined behavior.
What you really want to ask is whether or not the eight characters preceding the first '\0' in the string are all non-zero. You can do that by calling strlen.
Note that you cannot get a string of length eight when you pass 8 for the length, because fgets will use the last position for '\0':
char HEX[9];
while (fgets(HEX, 9, stdin) != NULL)
{ // ^--- Pass 9, not 8
if (strlen(HEX) == 8)
{
printf("GOOD\n");
break;
}
else
{
printf("Not 8 characters");
}
}
this is why:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte (aq\0aq) is stored after the last character in the buffer.
That means if you want to read 8, you will actually only read 7. You want to read 9, have a buffer of size 9, and check to see if any of the values are null starting at the end.
char HEX[10];
memset(hex, 1, 10); //Will stop false positives since memory might be null to begin with
while (fgets(HEX, 9, stdin) != NULL)
{
if (HEX[9] == '\0')
{
printf("GOOD\n");
break;
}
else
{
printf("Not 8 characters");
}
}
If you only want to read 8 characters from standard input, the function fgetc() may serve your purposes better.
Consider the following code:
#include <stdio.h>
int main(void)
{
char str[8];
int incre1=0;
for (incre1=0;incre1<=7;incre1++) {
str[incre1] = fgetc(stdin);
if(str[incre1] == EOF) {
printf("less than 8 characters\n");
break; }
}
printf("%s\n",str);
return(0);
}
This code will read the first 8 characters from standard input (including newlines), either from the console or from piped output of another program, and place them into the str buffer. By adding a check in the middle of the for loop for an End-of-FILE character, you can make your program respond accordingly should the input consist of less than 8 characters.
Note that if you are trying to code a program with interactive console input, you can also include an appropriate break condition should a newline character be read.
fgets() store a \n on the 8th position in your case.
So , You have two options,
Check for \0 at 9th position.
Replace \n on the 8th position with \0 then check for \0 on the 8th position.

Resources