Password input always returns "not eligible" - c

I'm learning C right now.
I have been working on a program that will check user's input (password eligibility). In order for password to be considered as eligible and considerably strong, it needs to have at least of one from the list of following items:
Uppercase letter;
'$' sign;
Alphanumerical character;
In my program I have created three integer variables that will keep count of upper mentioned requirements.
Unfortunately, whenever I input the "correct" version of password, the program keeps printing that the password is not eligible.
Please give me a clue, where I might be wrong.
//challenge:
//build a program that checks when user enters a password for an uppercase letter, a number, and a dollar sign.
//if it does output that password is good to go.
int main()
{
char passwordInput[50];
int alphaNumericCount = 0;
int upperCharacterCount = 0;
int dollarCount = 0;
printf("Enter you password:\n");
scanf(" %s", passwordInput);
//int charactersAmount = strlen(tunaString);
for (int i = 0; i < 49; i++){
//tunaString[i]
if( isalpha(passwordInput[i]) ) {
alphaNumericCount++;
//continue;
}else if( isupper(passwordInput[i]) ) {
upperCharacterCount++;
//continue;
}else if( passwordInput[i] == '$' ) {
dollarCount++;
//continue;
}
}
if( (dollarCount == 0) || (upperCharacterCount == 0) || (alphaNumericCount == 0) ){
printf("Your entered password is bad. Work on it!\n");
}else{
printf("Your entered password is good!\n");
}
return 0;
}

The isalpha function returns true if the character is either upper case or lower case. You do that before the condition that calls isupper. Since an upper case character will satisfy the first condition, the second condition will never evaluate to true.
Since being upper case is a subset of being alphanumeric, you need to revise your requirements. If instead you want to check for (for example):
upper case
numeric
"$"
Then you would have one condition use isupper, one use isdigit and one compare with '$'.
Also, you loop through all elements of the passwordInput array, even if they're not all populated. Instead of testing i<49, use i<strlen(passwordInput).

Related

How can I loop a string?

In an attempt to familiarize myself with fundamentals, I've been trying to write a simple program for choosing a password. The password is suppose to conform to the 5 listed conditions. The code is designed to loop through the password to determine if the conditions are satisfied and prompt users of any issues.
If the conditions are satisfied its coinciding variable is set to 1. Any variable left 0 is intended to prompt an invalid password. Unfortunately, it would appear that only the first character is being identified. All other conditions, aside from the first character, will fail regardless of the string.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int main(void) {
char password[21];
int loop;
int dollar = 0;
int digit = 0;
int upperCase = 0;
int lowerCase = 0;
printf("Requirements for a valid password:\n\n");
printf("1. Contain at least one $ sign.\n");
printf("2. Contain at least one number.\n");
printf("3. Contain at least one uppercase letter.\n");
printf("4. Contain at least one lowercase letter.\n");
printf("5. Contain no more than 20 characters.\n\n");
printf("Enter password: ");
scanf(" %s", &password);
printf("\n");
for (loop = 0; loop < 21; loop++) {
if (password[loop] == '$') {
dollar = 1;
break;
}
else {
printf("Invalid password, recheck for condition 1.\n\n");
break;
}
}
for (loop = 0; loop < 21; loop++) {
if (isdigit(password[loop])) {
digit = 1;
break;
}
else {
printf("Invalid password, recheck for condition 2.\n\n");
break;
}
}
for (loop = 0; loop < 21; loop++) {
if (isupper(password[loop])) {
upperCase = 1;
break;
}
else {
printf("Invalid password, recheck for condition 3.\n\n");
break;
}
}
for (loop = 0; loop < 21; loop++) {
if (islower(password[loop])) {
lowerCase = 1;
break;
}
else {
printf("Invalid password, recheck for condition 4.\n\n");
break;
}
}
if ((dollar * digit * upperCase * lowerCase) != 0) {
printf("Password saved!");
}
system("pause");
return(0);
}
So, let's do a simple dry run here. Let's take your code for the first condition (and as remaining all same in logic, so that should do).
for (loop = 0; loop < 21; loop++) {
if (password[loop] == '$') {
dollar = 1;
break;
}
else {
printf("Invalid password, recheck for condition 1.\n\n");
break;
}
}
Let's begin with loop value equals zero. Now the following conditions exist:
The first character is a $. In this case the if condition is satisfies, and we exit out after setting the dollar flag.
The first character is not a $ (this is where we primarily go wrong). The if condition fails, as expected - and so we move to the else straightaway. Here we go on to print the error message and break out of the loop, without checking the remaining characters! Logically, we should wait to print the error till we have processed ALL the characters and not found the condition to be satisfied.
So, now we understand the problem - we are printing the error and breaking out after just checking for the first character. But, how can we fix this?
Well, for starters we should wait till we search through all the characters. So the else part must move out of the loop. Also, we know that if we do indeed have the condition satisfied, we will set the dollar variable, and exit the loop. What if we don't find the condition satisfied? Well, in that case dollar will remain zero - and that's how we detect the error!
So, we could possibly do something like:
// Loop through the characters, and as soon as we find $ we set the dollar variable and break
for (loop = 0; loop < 21; loop++) {
if (password[loop] == '$') {
dollar = 1;
break;
}
}
// If dollar is still zero, we didn't encounter the $ character
if (dollar == 0) {
printf("Invalid password, recheck for condition 1.\n\n");
}
There are a couple of other simple mistakes in the code you posted, but primarily the logical flaw is the above. Look out for the comments to understand the other possible loopholes. Cheers!

Infinite loop with scanf in while loop

I'm a very beginner in C, and I'm trying to put a range in the numbers, but the infinite loop appears when a letter out of range is typed (for example: p).
If I type a number out of range, it works.
I read about and I think its a problem with the buffer and the function scanf, when I press "Enter" I include \n. So I tried to clean by scanning with a char, but didn't work.
I also tried to put a space (' ') before the %x, and didn't work.
The scanf it's been ignored after I put an invalid letter; if I put an invalid number, the program works.
#include <stdio.h>
int main()
{
int validity, pos; //validity 0:not valid, repeat the loop; validity 1: valid number
char enter;
while (validity == 0) {
printf("Player1 1, type the position in hexadecimal (1 to f):\n");
scanf(" %x%c", &pos, &enter); //here, the number pos is already in decimal
if (pos == NULL){
pos = 99; //if the letter typed is improper, I set 99, because is out of range
}
if(pos<=15 && pos >=0){
validity = 1;
} else {
printf("Invalid number, out of range\n");
validity = 0;
}
}
}
The very first time in your loop, validity does not have a defined value at all.
It might be 1, it might be 0, it might be 65,532.
You should assign it an initial value.
int validity = 0, pos; //validity 0:not valid, repeat the loop; validity 1: valid number
You declared pos as an int; it will never be NULL. It will have integer values.
This line makes no sense:
if (pos == NULL){
You should initialize that variable as well.
int validity = 0, pos = 99; //validity 0:not valid, repeat the loop; validity 1: valid number
C doesn't have default values for primitive data types. Hence in the line
int validity, pos;
You should probably set validity to some default value like 0 so that the loop runs at least once no matter what the input is(valid or invalid). Alternatively using a do while loop in this situation seems to be a more logical decision.
do
{
//Your code
} while(validity == 0);
In that case you won't have to worry about setting a default value to validity.
Also as #abelenky pointed out earlier, checking pos for NULL is a technically bad idea as well. You could as well just skip this part
if (pos == NULL)
{
pos = 99;
}
As pos is declared to be int, if the value of pos doesn't lie between 0 and 15 then it is automatically declared invalid by this part of your code.
if(pos<=15 && pos >=0)
{
validity = 1;
}
else
{
printf("Invalid number, out of range\n");
validity = 0;
}
Hence there is no need to make another block for checking it's validity.
The cause of the infinite loop is that if you input a letter, the "\n" that follows it is taken as the input of scanf(...) function within the next loop. So you can simply add "getchar()" to fix it. Note the ā€œ\nā€ after a number is ignored.
Differently, when you type a number out of range, it can be resolve with hex, and you can print it to see. But the letter can't(must be between a-f).
You can reference this:
#include <stdio.h>
int main(int argc, char *argv[])
{
int validity=0, pos=99;
while (validity == 0){
printf("Player1 1, type the position in hexadecimal (1 to f):\n");
scanf("%x", &pos);
if(pos<=15 && pos >=0){
validity = 1;
}else {
printf("Invalid number, out of range\n");
getchar();
}
}
}
Good luck.

Print output just beside the user input

According to the question, The user needs to enter the no of hours the vehicle is parked and the total charge for the hours should get printed beside it.
for example:
I created this simple program
#include<stdio.h>>
#include<math.h>
float calculateCharges(float hurs);
int main()
{
float hours;//total no of hours vehicle is parked
int i;
printf("%s%10s%10s", "Car", "Hours", "Charges");
for (i = 1; i <= 3; i++)
{
printf("\n%d\t", i);
scanf("%f", &hours);
printf("\t%f\n", calculateCharges(hours));
}
getch();
return 0;
}
float calculateCharges(float hurs)
{
float charges;
hurs = ceil(hurs);
if (hurs >= 24) charges = 10;
else
{
if (hurs <= 3) charges = 2;
else
{
hurs = hurs - 3;
charges = 2 + 0.5*hurs;
}
}
return charges;
}
But now every time I enter hours the charges are getting printed below it instead of beside it. As shown in the image:
Is there is a way to consume the newline after scanf? So that charges can be printed beside the scanf?
I have modified my code this way too, but it didn't make any difference.
printf("%s%10s%10s", "Car", "Hours", "Charges");
for (i = 1; i <= 3; i++)
{
printf("\n%d\t", i);
printf("\t%f\n",(scanf("%f", &hours),calculateCharges(hours)));
}
Let me know if the original question is required. I'm using Visual studio 2017 RC.
You can use something like this:
#include <iostream>
#include <windows.h>
//This will set the position of the cursor
void gotoXY(int x, int y) {
//Initialize the coordinates
COORD coord = {x, y};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
return;
}
void getCursorXY(int &x, int&y) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
x = csbi.dwCursorPosition.X;
y = csbi.dwCursorPosition.Y;
}
}
I found it here.
As already written in one of the answers this solution is not platform independent.
But i guess there are similar solutions on other platforms and you can easy set the cursor on the position you want.
Example usage in your main:
for (i = 1; i <= 3; i++)
{
printf("\n%d\t", i);
scanf("%f", &hours);
gotoXY( 20, i + 1);
printf("\t%f\n", calculateCharges(hours));
}
Workarounds for scanf can be found here.
scanf_s always generates a new line upon enter and unfortunately other user input capturing platform independent functions I know of (getc & getchar) do so too. Anyway on Windows it could be done using _getch() from conio header.
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
int getIntFromUser()
{
char readCharacters[10];
int index = 0;
for (int currentChar = _getch(); currentChar != '\r'; currentChar = _getch())
{
if (currentChar == EOF)
{
// Some error that shouldn't occour in your simple homework program
}
if (index > 9)
{
// Another possible error case where you would start to write beyond 'readCharacters' array
}
// We might as well disallow anything but digits, enter & backspace (You don't need anything else, do you?)
if ((currentChar < '0' || currentChar > '9') && currentChar != '\b')
{
continue;
}
else if (currentChar == '\b')
{
if (index > 0)
{
// Delete last character
printf("\b \b");
readCharacters[index] = '\0';
--index;
}
}
else
{
printf("%c", currentChar);
readCharacters[index] = currentChar;
++index;
}
}
if (index == 0)
{
// User pressed enter without having entered a number, let's give him a zero then
return 0;
}
readCharacters[index] = '\0';
int retVal = atoi(readCharacters);
// Worth noting that the value of converted user given string shouldn't be greater than what a signed int can hold
return retVal;
}
int main(int argc, char* argv[])
{
// Unlike scanf_s this will not generate a new line on enter
printf("getIntFromUser() sample (enter a number)\n");
int someValue = getIntFromUser();
printf(" -- This will be printed on the same line. (someValue is %d)\n\n", someValue);
// scanf_s sample
int anotherValue;
printf("scanf_s() sample (Insert a number.)\n");
scanf_s("%d", &anotherValue);
printf("This will be printed on a new line\n\n");
printf("Press any key to exit.");
_getch();
return 0;
}
EDIT
I feel like the above would become less readable if I were to add a comment over every code line. Instead I'm going to paste some blocks of code 1 by 1.
But first about the _getch function: It waits for the user to type something into the console and then returns the user given char as an int. char implicitly converts to int, so you may compare the _getch result to a character as I did many times in getIntFromUser (e.g. if (currentChar == '\b') { ... }).
You should also know about the values a char can hold and what their values are as an int (check out http://en.cppreference.com/w/cpp/language/ascii).
Going by the table the char '0' would be value 48 as an int, which is what _getch would return if the user were to type a 0.
First declare an array/string of 10 elements. Hope you know about them already. In this case the array is basically a chain of 10 elements that are all of type char, which are also referred to as string.
char readCharacters[10];
An indexer for the string is required.
int index = 0;
Below we have the usual for loop that...
1st: creates a variable of type int and assigns the result of _getch to it.
2nd: will determine if the loop shall keep executing. In this case the loop will break when currentChar is not '\r', which is an escape sequence that represents enter as a character.
3rd: will execute stuff inside once and then update currentChar with a new _getch.
for (int currentChar = _getch(); currentChar != '\r'; currentChar = _getch())
Checks if the user input (retrieved via _getch) is smaller than '0' (value 48 as an int) and greater than '9' (value 57 as an int). If either of them is true it will additionally check if the value of currentChar is not '\b' (value 8 as an int), which is the escape sequence for a backslash.
When that additional check evaluated to true as well then the keyword continue is used. Meaning that the rest of the block in the loop is not executed and instead the loop will start at the top again by getting a new user input and evaluating if the loop is to be continued by checking if obtained currentChar was enter.
if ((currentChar < '0' || currentChar > '9') && currentChar != '\b')
{
continue;
}
NOTE: You might want to read the comments on the else statement before you read these.
When the above if statement was false we get to the next if-statement (actually else if) that we see below.
As mentioned above: '\b' is backslash and if this is the user given char as well as string/array index being greater than 0 we move one character backwards in the console by "printing" '\b' and then write an empty character in order to delete what was written at that place previously. That puts us back to the position we were before so we print another backslash. At this point you might wonder why not just go back to the previous line that scanf_s causes, but that won't work. We must also not forget to replace the last string character with a null terminator and then set the index back by 1.
else if (currentChar == '\b')
{
if (index > 0)
{
// Delete last character
printf("\b \b");
readCharacters[index] = '\0';
--index;
}
}
When we hit this point we know that currentChar is something between 48 and 57 ('0' and '9').
_getch told the program what the user's input was, but we cannot see it in the console unless we print it there. So let's do that.
Also append the user's given character to the string as well as incrementing the index by 1.
else
{
printf("%c", currentChar);
readCharacters[index] = currentChar;
++index;
}
Lastly we call the atoi function that will convert our string/array to an integer.
int retVal = atoi(readCharacters);

What's the better way to check if input is string?

I'm making a program in which i ask for the username name, and i'd like to only accept strings with valid characters only (alphabetic).
I found that i can either use
do{
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n);
}while(1);
or
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name; i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
But i'm not sure if any of those work, and what would be better to check if there isn't anything except alphabetic letters.
Also though about making all input letters (after this verification) on lower case and make the first letter upper case with
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
Would this work?
if(scanf("%s", &name) reads in all non-white-space, not just letters, into name and does not return if input is only "\n".
if(isalpha(name[i]) == 0){ loop is not bad, but scanf("%s", &name) still does not return if input is only "\n" or just white-space.
for(i=1; i<strlen(name); i++) name[i] = tolower(name[i]) works to make all following letters lower case, but if inefficient as code repeatedly calculates the string length.
Separate reading data and parsing data. Use fgets() to read the data and various code to test the data for correctness.
char buf[200];
fgets(buf, sizeof buf, stdin);
int n = 0;
// Skip leading white-space
// Look for A-Z, a-z or space (like a space between first & last)
// Skip white-space like \n
// Save into 'n' the current scan position
sscanf(buf, " %*[A-Za-z ] %n", &n);
if (n > 0 && buf[n] == '\0') Success(); // #user3121023
Should code need to rid buf of a potential trailing "\n", suggest:
buf[strcspn(buf, "\n")] = 0;
Let's look at each option.
First option:
do {
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n");
} while(1);
This won't quite work the way you expect. First off, what exactly is name? I'm almost sure that you want scanf("%s", name) instead (name instead of &name), unless you declared it as char name;, which would be catastrophic anyway.
Anyway, the problem I see with this approach is that you don't really validate the string. Read the man page section about %s:
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.
Nothing says that the string is composed of alphabetic characters only.
Second option:
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name); i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
Again, you probably want name rather than &name. You also shouldn't be calling strlen() in the for loop condition, because it's inefficient (strlen() is O(n)). A smart compiler may optimize it away, but it's hard for the compiler to know when it is safe to do so. Just call strlen() before the loop and store the result in a variable.
isalpha() expects an integer as an argument, which is expected to be either EOF or an unsigned char converted to int. Again, you don't show the declaration for name, but assuming that it is a character array, you should cast name[i] to unsigned char before calling isalpha(), so that you don't get any sign extension surprises:
if (isalpha((unsigned char) name[i]) == 0) { /* ... */ }
In fact, gcc nowadays will most likely give you a warning if you call any of the ctype family macros / functions with a plain char. The macros are deliberately written in such a way that a warning is shown, precisely because this is a common mistake. It is implementation-defined whether a plain char is signed or unsigned. You would get problems in a platform with signed chars because of sign extension (this is because typically, things like isalpha() are implemented using lookup tables, and extending the sign yields a negative number that would index the lookup table with a negative index - Oops!)
Other than this, this approach seems ok to me.
A third, maybe better option:
Since you mentioned fgets(), I think you could do this easily by combining fgets() with sscanf(). First, you read a line with fgets(). Then, you use sscanf() to match a string consisting of only characters in the range [a-zA-Z]. This can be done with the format specifier %[a-zA-Z]s. Then, you just have to check if this matched the entire line. Here's a working program:
#include <stdio.h>
#include <string.h>
int main(void) {
static char buf[512];
static char name[512];
int is_valid = 0;
while (!is_valid) {
fgets(buf, sizeof(buf), stdin);
size_t line_len = strlen(buf);
if (line_len > 0 && buf[line_len-1] == '\n') {
buf[line_len-1] = '\0';
line_len--;
}
int n = 0;
if (sscanf(buf, " %[a-zA-Z] %n", name, &n) == 1 && buf[n] == '\0') {
is_valid = 1;
} else {
printf("Please enter a valid name.\n");
}
}
printf("Name: %s\n", buf);
return 0;
}
Make sure your buffers are large enough; this code is vulnerable to buffer overflow for arbitrarily long names / lines.
Now let's see the code to make the first letter upper case:
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
Again, remove strlen() from the loop condition. toupper() and tolower() also expect an int as an argument representing either EOF or an unsigned char converted to int. You should cast it to unsigned char to avoid problems with possible sign extension, as I said earlier with the other example.
This is wrong:
//first letter to upper
name[0] = toupper(name[i]);
It should be:
//first letter to upper
name[0] = toupper(name[0]);
(The argument to toupper() is name[0], not name[i]).
Finally, this is useless:
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
%s will never give you a string with whitespaces (refer to the manpage quote I pasted above).
Assuming that you want your name to have only characters a through z or A through Z then you could use this function
//Returns 1 if non alphabetic character is found, 0 otherwise
int NonAlphaCharsFound(char *name)
{
int FoundNonChar = 0;
int i, nameLength;
nameLength = strlen(name);
for(i = 0; i < nameLength; i++)
{
if((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || name[i] == ' ')
{
//do nothing if it's an alphabect character
//name[i] == ' ' is to allow for spaces if you want spaces in the name
}
else
{
FoundNonChar = 1;
break;
}
}
return FoundNonChar;
}

User Input - C (Not allowing certain characters)

I have a C program where the user will be entering integers or doubles into the command line as input. Negative numbers are allowed, but I have the issue of when a user enters some thing like this:
1-1
It just gets parsed as being a one. I created a function to test whether or not the user input is a valid number, but I'm not sure how to catch instances like that, or the same goes for input like 1+1, 1)2, so on.
Here is the function I created:
int check_number(char *userInput) {
int i;
//check each character
for (i = 0; userInput[i] != '\0'; i++){
if (isalpha(userInput[i])){
printf("Invalid input.\n");
return -1;
}
}
return 0;
}
What should I do to not allow any other random characters (besides letters, since those are already getting tested with isalpha) in the middle of user input?
Basically you want the first character to be either a minus or a digit (and maybe a period) all subsequent characters should be digits or a period. But you will probably also want to count the periods to make sure there is only one. If you want to accept scientific notation as well you will need to deal with that as well.
Checking for not isalpha is the wrong way around, you should check for the characters that are allowed.
Basically something like:
if (*userInput != '-' && !isdigit(*userInput))
return -1;
int periods = 0;
while (*++userInput != 0) {
if (*userInput == '.') {
if (periods >= 1)
return -1;
++periods;
continue;
}
if (!isdigit(*userInput))
return -1;
}
return 0;
if (isdigit(userInput[i]) || userInput[i]=='.') {
printf("Valid input.\n");
}
else return -1;

Resources