I am a C beginner trying to understand some nuances of scanf. This seems really trivial but I am struggling with reading inputs from stdin in "the correct way".
When terminal input is like string1 string3 string3 and I hit return, It works correctly and gives 3 in the result.
But when I give input like string1 and I hit return, I want the program to return 1 in the result variable and break the loop. Which doesn't happen. The program just expects me to enter more input into the terminal.
#include <stdio.h>
#define nameBufferLen 20
int main () {
int result;
char name[nameBufferLen];
char opens[nameBufferLen];
char closes[nameBufferLen];
while(1) {
result = fscanf(stdin,"%s %s %[^\n]s", name, opens, closes);
printf("%s|%s|%s| AND Result len is : %d\n", name, opens, closes, result);
if (result!=3) {
break;
}
}
return 0;
}
I am curious to know what could be the approach and regex that enables me to do this with scanf.
Here is the implementation that #Barmar mentioned:
#include <stdio.h>
#define nameBufferLen 19
#define str(s) str2(s)
#define str2(s) #s
int main (void) {
for(;;) {
char s[3 * (nameBufferLen + 1)];
if(!fgets(s, sizeof(s), stdin)) {
printf("err\n");
return 1;
}
char name[nameBufferLen+1] = { 0 };
char opens[nameBufferLen+1] = { 0 };
char closes[nameBufferLen+1] = { 0 };
int result = sscanf(s,
"%" str(nameBufferLen) "s"
"%" str(nameBufferLen) "s"
"%" str(nameBufferLen) "s",
name, opens, closes
);
if(result == 1)
break;
}
}
and example run:
a b c
a b
a
If you enter strings longer than 19 the excess will spill into the next variable. You can detect that with strlen() of each of the variables. Alternatively you can parse the string s with, for example, strpbrk().
Related
I am a beginner coder and writing a code that
asks for the user's name
check if the length is >15, if it is, it will ask the user to input a shorter name when they restart the program
if the length is valid, upper case the first letter of the entered name
display something like "Hi name"
However, The program keeps exiting no matter what I enter. Here is what I did :
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>
int main(void)
{
char str_name[15];
printf("Please enter your first name to begin: ");
scanf_s("%14s", str_name, _countof(str_name));
getchar();
if (strlen(str_name) > 15)
{
printf("The entered name is too long, please restart the program and try again.");
getchar();
exit(0);
}
else
{
str_name[0] = toupper(str_name[0]);
printf("Hi %s.\n", str_name);
getchar();
}
return 0;
}
You can simply use fgets() to read an input buffer.
char *fgets(char *str, int n, FILE *stream) reads a line from the specified stream and stores it in a buffer pointed to by str. It stops when either n-1 characters are read, the newline character is read, or the EOF is reached.
Some things to note about fgets():
Returns NULL on error.
Appends \n character at the end of buffer. Can replaced with \0 instead.
Buffer must be pointer to an array of characters. Either allocated on the stack or the heap.
Reads from stdin or FILE objects.
Here is some example code which shows this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NAMESTRLEN 16
int main(void) {
char str_name[NAMESTRLEN] = {'\0'};
size_t slen;
printf("Please enter your first name to begin: ");
if (fgets(str_name, NAMESTRLEN, stdin) == NULL) {
fprintf( stderr, "Error from fgets()\n");
return 1;
}
slen = strlen(str_name);
if (slen > 0 && str_name[slen-1] == '\n') {
str_name[slen-1] = '\0';
} else {
fprintf( stderr, "Too many characters\n");
return 1;
}
if (str_name[0] == '\0') {
fprintf( stderr, "No name entered\n");
return 1;
}
str_name[0] = toupper((unsigned char)str_name[0]);
printf("Hi %s.\n", str_name);
return 0;
}
There are several issues in your code:
The buffer size is too short: scanf() will not read more than 14 bytes into str_name, so it is not possible to test if the user entered a name longer than 15 characters.
toupper() should not be given a char argument because it is only defined for values of type unsigned char and the special value EOF. Cast the char as unsigned char.
your attempt at pausing before the terminal window closes fails on windows for long names because the extra characters entered by the user are still pending after the scanf_s(). getchar() reads one and returns immediately, and the program exits and the terminal window closes. Open the terminal window and run the program manually so it does not close automatically. With this approach, you can remove the getchar() and make the program more portable.
Here is a corrected version:
#include <ctype.h>
#include <stdio.h>
int main(void) {
char str_name[17];
printf("Please enter your first name to begin: ");
if (scanf_s("%16s", str_name, sizeof(str_name))) {
printf("Premature end of file.\n");
return 1;
}
if (strlen(str_name) > 15) {
printf("The entered name is too long, please restart the program and try again.");
return 1;
}
str_name[0] = toupper((unsigned char)str_name[0]);
printf("Hi %s\n", str_name);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
char str_name[16] = {0};
size_t len = 0;
int ch;
printf("Please enter your first name to begin: ");
while ((EOF != (ch = fgetc(stdin))) && (ch != '\n') && (len<sizeof(str_name)))
{
if (isalpha(ch))
{
str_name[len++] = ch;
}
}
if (len==sizeof(str_name))
{
fprintf(stderr, "Name too long\n");
}
else
{
str_name[len] = '\0';
printf ("Hi %c%s.\n", toupper(str_name[0]), &str_name[1]);
}
return 0;
}
As you can see I changed the input grabbing function. To check the input str len you should read byte per byte, not a whole string: fgetc do it. Using scanf with a format specifier like %14s it will return you always a trimmed string and you are not able to warn user about a too long Name.
Another point is to check that inserted chars are letters and not other kind of chars: isalpha do the job.
Moreover a c-string is composed by chars plus a null terminator ('\0', 0x00 ), so: a 15 chars string wants a 16 bytes array.
Lastly the code you wrote to mahe the first letter uppercase is completely wrong: you are passing to print a 1 char array and not a c string. My solution is one of many.
The problem is:
char Name[1]; // Declare a single element array.
Name[0] = toupper(str_name[0]); // Set it to the upper-case first letter.
// Try and print the nul-terminated string in Name - but there is no
// trailing zero (and the rest of the name is missing).
printf("Hi %s.\n", Name);
What you need is:
str_name[0] = toupper(str_name[0]);
printf("Hi %s.\n", str_name);
There is another problem that str_name[0] is a char value, and char may be signed. If you are using Windows-1252 for example, then Ä is 196, or as a signed char, -60. toupper takes an int, which must be positive¹. When -60 as a signed char is converted to int, you will get -60, and an out of range error. You need to write this as:
str_name[0] = toupper((unsigned char)str_name[0]);
printf("Hi %s.\n", str_name);
(Sorry.)
¹ Chrqlie points out the requirement is actually that the argument must be one of the values of unsigned char (which are - by definition - non-negative) or EOF (which is negative)"
Hi I learned in class about the string.h library and specifically about the strcmp function that compares between to strings .
and if the first string appears first in the dictionary it will return a number bigger than 0 , if the second string is bigger than first than it will return a number smaller than 0 , and if they are equil its supposed to return a 0.
ive used it like this :
strcmp(strArr , strrev(strArr));
Feel free to educate me.
The code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 100
#define PALINDROM_TRUE 0//because in strcmp if both strings compared are equile the strcmp will return 0.
int main(void)
{
char strArr[MAX_LENGTH];
printf("Enter string (max length 100 chars): ");
fgets(strArr , MAX_LENGTH , stdin);
int pali = strcmp(strArr , strrev(strArr));
if(pali == PALINDROM_TRUE)
{
printf("Palindrom\n");
}
else
{
printf("Not Palindrom\n");
}
system("PAUSE");
return 0;
}
my problem is that the code below when I enter i.e "abc" it prints to the screen Palindrom which it should print Not Palindrom , it never prints Not Palindrom
The reason for this is your call to strrev(). strrev() function works in-place. In other words, strrev works on the same buffer(i.e. strArr array) as the original array, and does not allocate a new space for the reversed string. As a result, once you call strrev() with strArr, the string in strArr is reversed, and all you are doing is comparing the two identical strings: the one in strArr, and again, the one in strArr, both of which are now reversed. (Note that the term both may be misguiding. Actually, there is just a single buffer and both arguments to strcmp point to that one.)
One way to correct this issue is to allocate a second array strArr2 and copy the string into it. Then, reverse the string in strArr2, and call strcmp with strArr and strArr2. Below is how this solution would look like in code.
...
char strArr2[MAX_LENGTH];
...
strcpy(strArr2, strArr);
strrev(strArr2);
int pali = strcmp(strArr , strArr2);
if(pali == PALINDROM_TRUE)
{
printf("Palindrom\n");
}
else
{
printf("Not Palindrom\n");
}
...
Note that you may also have to check the end of strArr for any trailing whitespace characters, due to your specific method of taking input. Any such trailing whitespace character could render your reversing and comparing strategy useless by affecting the result of the comparison.
If you do not have that much space, or if MAX_SPACE is too large a value, another way would be to use a custom comparison function for checking if a string is palindrome without having to use strcmp or strrev.
int pali = strcmp(strArr , strrev(strArr));
This line is obviously the issue.
The call to strrev(strArr) reverses strArr so afterwards when strcmp is called, do you expect strArr to be different? But it's the same variable...
You must create another char array to store the reversed string.
EDIT:
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 100
int main(void)
{
char str[MAX_LENGTH];
char rev[MAX_LENGTH];
puts("Enter string (max length 100 chars): ");
scanf("%s", str);
strcpy(rev, str);
strrev(rev);
if(strcmp(str, rev) == 0) {
puts("Palindrom");
} else {
puts("Not Palindrom");
}
}
ive added
strArr[strcspn(strArr, "\n")] = 0;
after
fgets(strArr , MAX_LENGTH , stdin);
And it worked , its basicly rewriting the (null) at the end. which was messing up the reverse to include null (0) at the start of the string.
Thanks everyone.
Finsihed Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 100
#define PALINDROM_TRUE 0//because in strcmp if both strings compared are equile the strcmp will return 0.
int main(void)
{
char strArr[MAX_LENGTH], strArrRev[MAX_LENGTH];
printf("Enter string (max length 100 chars): ");
fgets(strArr , MAX_LENGTH , stdin);
strArr[strcspn(strArr, "\n")] = 0;
strcpy(strArrRev, strArr);
int pali = strcmp(strArr , strrev(strArrRev));
if(pali == PALINDROM_TRUE)
{
printf("Palindrom\n");
}
else
{
printf("Not Palindrom\n");
}
system("PAUSE");
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void displayString (const char *sPtr);
void getString (char *[]);
int determinIfConvert (char);
int main ()
{
char originalString[11] = { 0 };
char convertedString[11];
getString (originalString);
displayString (originalString);
// this loop runs through the "originalString" to check for the char: 'a'
for (int i = 0; i < 11; i++) {
determinIfConvert (originalString[i]);
}
system ("pause");
}
void getString (char *a[]) // this function gets a string
{
printf ("enter 11 char string: \n");
scanf ("%s", a);
}
// this program displays the inputstring
void displayString (const char *sPtr)
{
for (; (*sPtr != '\0'); ++sPtr) {
printf ("%c", *sPtr);
}
}
int determinIfConvert (char *a)
{
if (a == 97) // this is a test condition. The goal is to
// check for all lowercase, but now i'm
// only entering "aaaaa"
{
printf ("Works"); // if it prints multiple"works"
// then i can continue my program
// but it only prints ONE "works" and freezes.
}
}
At the moment I have a problem with my For Loop in main() not finishing. The goal is to enter a string of characters, and then check for lowercase ones. This will be done with the function DeterminIfConvert(char). However, when I run through the loop element by element, it freezes after the second element. My test data is "aaaa" and it prints the "aaaa," so I know that my first two functions work just fine. I get to the loop, it goes through the first element, prints "works" and then freezes. :/
Multiple mistakes
void getString(char *a[])
should be
void getString(char a[])
Since you're sending the base address of an array of char, not an array of pointer to char
char *a[]; // array of pointer to char
char a[]; // array of char
int determinIfConvert(char *a)
should be
int determinIfConvert(char a)
Since you're sending a char, not a pointer to char
char * a; // pointer to char
char a; // char
NOTE:
Use the standard definition of main()
int main(void) //if no command line arguments.
If you are inputting an 11-char string, then you should be doing:
char originalString[12] = { 0 };
This is because you need 1 more character to store the null character '\0'.
That is probably why in your function getString(...), the pointer exceeds the array bounds and might invoke undefined behavior.
Finally, your function prototype for getString(...) should be
void getString(char a[]); //without the *
In addition to the other answers, you have several other areas where you can improve your code.
Avoid using magic numbers in your code (e.g. 11). Instead define a constant for the maximum characters in your string #define MAXC 11 or you can use an enum instead enum { MAXC = 11 };
As it currently sits, you do not protect against overflowing your 11 character array (which means your user can enter no more than 10 characters plus room for the nul-terminating character). To protect against the user entering something more than 10, you should use a field-width specifier with scanf:
scanf ("%10s", a);
That doesn't solve your problems with scanf. You must check the return every time to insure the expected number of conversions takes place, e.g.:
if (scanf ("%10s", a) != 1) {
fprintf (stderr, " -> error: invalid input.\n");
exit (EXIT_FAILURE);
}
That's better, but using %s, you cannot read a string containing whitespace, and you are still leaving a trailing '\n' in the input buffer. If the users enters "my dog", you store "my" only. To fix part of the problem you can use a format specifier of "%10[^\n]%*c". However, you must protect against an endless-loop if the user presses [Enter] without other input. To resolve all issues, and prevent leaving the trailing newline in the input buffer, you can use something like:
int getString (char *a) // this function gets a string
{
int c, rtn = 0;
printf ("enter string (10 char or less): ");
while ((rtn = scanf ("%10[^\n]%*c", a)) != 1) {
if (rtn == EOF)
break;
fprintf (stderr, " -> error: invalid input, try again..\n");
printf ("enter string (10 char or less): ");
/* flush input buffer - to avoid endless loop */
while ((c = getchar()) != '\n' && c != EOF) {}
}
return rtn;
}
All of which expose the difficulties using scanf for user input. A better approach may be to use fgets (or getline) to read the complete line of input.
Regardless whether you use scanf or fgets, etc.. you must take a bit of time and care in writing your input handlers to insure you try and cover all ways a user could muck up input. Below fgets is used just to present an alternative. You should also choose a return type that allows you to tell whether you have successfully received input or not. It might as well be a useful return such as the length of the input taken, etc..
The remainder of your level of pointer indirection issues have been addressed by other answers. Putting it all together, you could do something like:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 11
void displayString (const char *sPtr);
int getString (char *);
int determinIfConvert (char);
int main (void)
{
char originalString [MAXC] = "";
// char convertedString[MAXC] = ""; /* currently unused */
if (!getString (originalString)) {
fprintf (stderr, "error: getString failed.\n");
return 1;
}
displayString (originalString);
// this loop runs through the "originalString" to check for the char: 'a'
for (int i = 0; i < 11; i++) {
determinIfConvert (originalString[i]);
}
system ("pause");
return 0; /* main() is type 'int' and returns a value */
}
int getString (char *a) // this function gets a string
{
char *p = a;
int c;
size_t len = 0;
printf ("enter string (10 char or less): ");
for (;;) {
p = fgets (a, MAXC, stdin);
if (!p) break; /* handle [CTRL+D] */
if (*p == '\n') { /* handle empty str */
fprintf (stderr, " -> error: invalid input, try again..\n");
printf ("enter string (10 char or less): ");
continue;
}
/* trim newline/flush input buffer */
len = strlen (p);
if (len && a[len - 1] == '\n')
a[--len] = 0;
else /* user entered more than 10 chars */
while ((c = getchar()) != '\n' && c != EOF) {}
break;
}
return (int) len;
}
// this program displays the inputstring
void displayString (const char *sPtr)
{
for (; *sPtr; sPtr++) {
printf ("%c", *sPtr);
}
putchar ('\n');
}
int determinIfConvert (char a)
{
if (a == 97)
printf ("Works\n");
return 0;
}
Example Use/Output
$ ./bin/getdispstr
enter string (10 char or less): my dog has fleas
my dog has
Works
$ ./bin/getdispstr
enter string (10 char or less):
-> error: invalid input, try again..
enter string (10 char or less): my dog has fleas, my cat has none.
my dog has
Works
With CTRL+D (EOF)
$ ./bin/getdispstr
enter string (10 char or less): error: getString failed.
There are many ways to do this, this is just an example. Look over all the answers and let me know if you have questions.
This
char originalString[11] = { 0 };
followed by this
for (int i = 0; i < 11; i++)
{
determinIfConvert(originalString[i]);
}
is causing the problem. You see the array of char does not have elements post index 0. And yeah I believe what you are trying to attempt with
getString(originalString); seems like you want to get originalString from user input which is not correctly executed in your case.
You pass object of type char to a function accepting char*
char originalString[11] = { 0 };
determinIfConvert(originalString[i]);
int determinIfConvert(char *a)
A string is nothing but a null terminated set of characters, so if you wish to have 11 characters in you string, you should be allocating 12 bytes to your
array, ie you may change :
char originalString[11] = { 0 };
to
char originalString[12] = "";
/* Here is the string is empty but because you use double quotes
* compiler understands that you are initializing a string, so '\0' is auto
* appended to the end of it by the compiler to mark the end of the string.
*/
So is the case with convertedString[11] change it to
char convertedString[12] = "";
Change
void getString(char *a[]);
to
void getString(char a[]); //char *a is also fine
Change
int determinIfConvert(char *a)
to
int determinIfConvert(char a) // You wish to check a character
You may wish to replace
scanf("%s", a);
with
fgets(a,12,stdin);
because scanf can't check for overflows but fgets can. Here you can have up to 11 characters in the string. If an overflow occurs, the rest of the input is trimmed and '\0' is assigned to the 12th byte.
You may wish to use the islower function to check is a character is lowercase. So you may change
if (a == 97)
to
if (islower(a)) // check if a character is lowercase.
Remember you may need to include the string.h header to use islower()
I can't understand what's happening with buf1 and pass in main(). I understand that after buffer overflow in gets(buf1):
Firstly (by input more then 15 characters), we are actually
changing calling frame for calling function main()
Secondly (if keep input more then 19 characters), then we will start change return address of calling function main().
But why after 16 character in gets (buf1) (123456789012345**6**) we get pass equal 54 (which is ASCII code for 6). We do not overflow pass variable so why we get this pass = 54?
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
int CommandInjection(char *varCommand)
{
char cat[] = "cat ";
char *command;
size_t commandLength;
commandLength = strlen(cat) + strlen(varCommand) + 1;
command = (char *) malloc(commandLength);
strncpy(command, cat, commandLength);
strncat(command, varCommand, (commandLength - strlen(cat)) );
system(command); //The function system is executed with the input entered by the user. The input can be dangerous.
return (0);
}
int main(void)
{
char buf1[15];
char varCommand[30];
bool pass = 0;
printf("\nEnter the password: \n(If you enter more than 15 characters you can break the security)\n");
gets(buf1); //Function that does not make bound checking
if(strcmp(buf1, "thepassword"))
{
printf ("\nWrong Password\n PASS=%d", pass);
if(pass==true)
printf ("\nHowever, there was memory corruption and you can enter to other part of the program\n pass=%d", pass);
}
else
{
printf ("\nCorrect Password\n");
pass = true;
}
if(pass == true)
{
// Don't must enter here if the password is wrong
printf ("\nEnter the file name (for example: text.txt; ls -l)\n");
gets(varCommand); //There is no input validation
CommandInjection(varCommand);
}
return 0;
}
So I have been taking classes on C and one of the exercises was to program a caesar cipher program that both encrypts and decrypts. And when the input is "ab cd", the output should be "de#fg" but instead it outputs "de?g?". So my guess is the spacebar messes everything up. But also another error was found when I inputted "a" and it outputted "d?ad?". Thanks in advance.
#include <stdio.h>
#include <string.h>
void cipher(char plain_str[], char cipher_str[]);
void decipher(char cipher_str[], char decipher_str[]);
int main() {
char plain_str[30];
char cipher_str[30];
char decipher_str[30];
printf("Enter plain string: ");
scanf("%s", plain_str);
cipher(plain_str, cipher_str);
decipher(cipher_str, decipher_str);
}
void cipher(char plain_str[], char cipher_str[]) {
int i = 0;
while(plain_str[i] != '\0') {
if((plain_str[i]+3) >= 0 && (plain_str[i]+3) <= 127) {
cipher_str[i] = plain_str[i] + 3;
} else {
cipher_str[i] = plain_str[i] - 124;
}
i++;
}
printf("%s\n", cipher_str);
}
void decipher(char cipher_str[], char decipher_str[]) {
//asdf
}
The %s operator in scanf only reads a single word, not a whole line. So if you enter ab cd, only ab is put into plain_str. To read a whole line, use fgets():
fgets(plain_str, sizeof(plain_str), stdin);
size_t len = strlen(plain_str);
if (plain_str[len-1] == '\n') {
plain_str[len-1] = '\0'; // Remove newline
}
The other problem is that you're never adding the null terminator to cipher_str, so you're printing whatever garbage is in it after the encoded characters. The simplest way to resolve this is to initialize it to an all-zero array when you declare the variable:
char cipher_str[30] = {0};