How can I loop a string? - c

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!

Related

Password input always returns "not eligible"

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).

Interactive, restrictive, dynamic user input

I provided a link to a PDF of the assignment instructions.
[TL;DR]
Ask two questions: Are you afraid of the dark? Do you exercise?
Input: ‘Y’ for yes, ‘N’ for no.
If input to second question is 'Y', One additional question: Minutes per day exercised?
Input: Integer > 0. However, if less than 10 per day, unqualified.
Output: Tells the user whether they can to enter ninja training or not.
What I'm having difficulties with:
Dynamically Allocating a Multidimensional String Array
I have only been coding for about a week, and I understand it's probably overkill for this assignment. With that being said, I got an idea while doing this assignment, and while I find it rather challenging to articulate my idea with words, here's an image that I feel captures what I am "visualizing."
Visual posted on https://www.eskimo.com/~scs/cclass/int/sx9b.html by Steve Summit
In this particular assignment, I think it's a waste of memory to keep the user's answer's. Nevertheless, what if I want to write a program that ask the user for a series of inputs, and at the end, correlate them, or make spurious correlations for teh lulz?
Spurious Correlations http://www.tylervigen.com/spurious-correlations by Tyler Vigen
A more practical reason, however, an MBTI Personality Type test? I don't know exactly all the possibilities, but they seem exciting.
That's what I want to achieve with dynamically allocating a multidimensional string array
Will update later
Original post follows...
The code works but some inputs are still allowed, namely any characters entered if the the first element is either Y or N.
Here's the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char reference[][2] = { "Y", "N" };
char (*ptr_reference)[2] = reference;
int reference_minutes[1] = { 10 };
int *ptr_reference_minutes = reference_minutes;
char **user_input;
user_input = (char**)malloc(3 * sizeof(char*));
int i;
for (i = 0; i < 3; i++) {
user_input[i] = (char*)malloc(4 * sizeof(char));
}
if (!user_input) {
printf("Could not allocate memory!/n");
exit(1);
}
i = 0;
while (i == 0) {
printf("\nAre you afraid of the dark? Choose either [Y/N], and press enter when finished: \n");
fgets(user_input[i], 4, stdin);
user_input[i] = realloc(user_input[i], sizeof(char));
if (strncmp(user_input[i], *ptr_reference, 1) == 0) {
printf("\nPatience, Young Grasshoper! You are not ready to become a ninja.");
i = 3;
break;
} else if (strncmp(user_input[i], *(ptr_reference + 1), 1) == 0) {
i++;
break;
} else {
printf("\nPlease enter Y for yes or N for no.\n\n");
continue;
}
}
while (i == 1) {
printf("\nDo you exercise? Input [Y/N], and press enter when finished: \n");
fgets(user_input[i], 4, stdin);
if (strncmp(user_input[i], *ptr_reference, 1) == 0) {
i++;
break;
} else if (strncmp(user_input[i], *(ptr_reference + 1), 1) == 0) {
printf("\nDo you even lift, Bro?");
i = 3;
break;
} else {
printf("\nPlease enter Y for yes or N for no.\n\n");
continue;
}
}
while (i == 2) {
int sscanf_result, answer;
printf("\nHow many minutes a day do you exercise? Type an integer greater than 9 and press enter when finished.\n");
fgets(user_input[i], 4, stdin);
sscanf_result = sscanf(user_input[i], "%d", &answer);
if ((sscanf_result == 0) | (sscanf_result == EOF)) {
/* either a non-integer entered or an end-of-line */
printf ("\nYou have to enter an integer!\n");
i = 2;
continue;
} else if (answer < *ptr_reference_minutes) {
printf("\nCome on! You kids are soft! You lack discipline!\n");
i = 3;
break;
} else {
printf("\nYou are a good fit for ninja training.\n");
for (i = 0; i < 3; i++) {
free(user_input[i]);
}
free(user_input);
user_input = NULL;
break;
}
}
return 0;
}
Ok, I could have a look at your program. The problem is not really in allocation but is indeed in string management. In C a string is a null terminated char array (please copy this 100 times...).
When you read a line with fgets, you get the new line character (\n) in your buffer, so if user types Y Enter you get {'Y', '\n', '\0', undeterminated_char }. The following realloc is then plain wrong:
it is likely to be a noop: the compiler shall only give you a buffer at least as large as your requirement. As 4 > 1, it can (and my implementation did) give the original buffer unchanged
you are not allowed to use anything past what you have required, and in particular, you shall not assume that there is a null!
So if you insist in doing a string comparison, you should only ensure that the second char is null: user_input[i][2] = '\0';
But IMHO what is required here is just:
if (user_input[i][0] == 'Y') {
...
That is not all. You try to do a great job in input processing, but just forgot one detail: if a line is longer than the declared size, fgets fills its buffer and leaves the remaining part of the line available for next read.
What follows is only my advice:
You have tried to use everything you know (and probably things you do not fully master...) into a single and simple program. Don't. Keep each program as simple as possible (Keep It Stupid Simple is a general good practice...), and trust your learner to give you other assignments for other patterns. So here you should:
read a line until you find the \n (it might require several fgets)
test the first character of the buffer to be 'Y' or 'N'
test the second one to '\0'
As it is a common requirement in real world, and only after your program works, you could considere:
ignoring initial blank characters
accept lower case as upper case
accept any character after first one (in order to accept Yes and No)
Last advice if nice coding matters for you: once your program works correctly, you should considere posting it in Code Review to get interesting comments on it.
#Serge Ballesta #Serga Ballesta Thank you kindly for your feedback. It was extremly helpful. After following your advice, I was able to write a better program, although, I'm not sure if I was successful in "Keep It Simple, Stupid."
I still can't figure out how to restrict characters after 'Y' or 'N', i.e., Nnb or Nq is accepted as N and goes through to the second question.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (void) {
char *user_input = NULL;
user_input = (char*)malloc(3*sizeof(char));
int sscanf_result, answer;
int i;
while (!*user_input)
{
printf("\nAre you afraid of the dark? \n\nInput Specifications: Please type 'Y' to indicate yes, or 'N' to indicate no, and then press the 'Enter' or 'return' key when finished.\n\n");
i = 0;
fgets(user_input, sizeof(user_input), stdin);
/* trim off last character */
user_input[strlen(user_input)-1] = '\0';
if (user_input[i] == 'Y')
{
printf("\nFear of the dark? Fear those who lurk in the dark, my friend. Ninjas! You may not train as a ninja!");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
else if (user_input[i] == 'N')
{
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
printf("\nDo you exercise?\n\nInput Specifications: Please type 'Y' to indicate yes, or 'N' to indicate no, and then press the 'Enter' or 'return' key when finished.\n\n");
i = 0;
fgets(user_input, sizeof(user_input), stdin);
user_input[strlen(user_input)-1] = '\0';
if (user_input[i] == 'N')
{
printf("\nDo you even lift, Bro?\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
else if (user_input[i] == 'Y')
{
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
printf("\nHow many minutes a day do you exercise?\n\nInput Specifications: Please type a positive integer and press the 'Enter' or 'return' key when finished.\n\n");
i = 0;
fgets(user_input, sizeof(user_input), stdin);
user_input[strlen(user_input)-1] = '\0';
sscanf_result = sscanf(user_input, "%d", &answer);
if ((sscanf_result == 0) | (sscanf_result == EOF))
{
/* either a non-integer entered or an end-of-line */
printf ("\nPlease type a positive integer and then press the 'Enter' or 'return' key when finished.\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
continue;
}
if (answer < 10)
{
printf("\nCome on! You kids are soft! You lack discipline!\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
else
{
printf("\nYou may begin training as a ninja!\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
}
else
{
printf("\nInput Error: Please carefully read the input specifications that are provided after each question prompt and then try again.\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
continue;
}
}
else
{
printf("\nInput Error: Please carefully read the input specifications that are provided after each question prompt and then try again.\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
continue;
}
}
return 0;
}

How to loop through a users input using getchar() and check to see if the characters are an alphabet or a digit

Im totally new to programming, I picked up a C manual to learn on my own. I dont want to use an array as Im trying to practice with getchar(). I want to be able to output an error message if the user enters anything other than a digit or an alphabet. I am also trying to practice the C library function isalpha() and isdigit(). This is what I wrote so far, but my output is not quite right.
Input 1: "hello"
Expected output : "valid output"
Input 2: "hello5"
Expected output : "valid output"
Input 3: "hello!"
Expected output : "invalid output"
But my program returns "valid input" for all the three inputs above
Please help a newbie try to learn. I greatly appreciate it.
#include <stdio.h>
#include <ctype.h>
int main ()
{
char ch;
int len;
int valid;
printf("Enter a word: ");
for(length = 0; (ch = getchar()) != '\n'; len++)
{
if(isalpha(ch) || isdigit(ch))
{
valid = 1;
}
else
{
valid = 0;
}
}
printf("Input length: %d\n", len);
if (valid == 1)
{
printf("Valid\n");
}
if(valid == 0)
{
printf("\n");
}
return 0;
}
You were almost there. Few pitfalls:
1st there is a typo for your variable name “length” instead of “len”.
2nd As Mitchel0022 stated, your program will always display “valid” providing the last character entered is valid because you reassign a new value for the variable ‘valid’ on each iteration. but you don’t have to use a ‘break statement since you need the loop to continue in order to get your length, so stick with your flag.
Now your program should run fine. Copy and paste the code below:
#include <stdio.h>
#include <ctype.h>
int main ()
{
char ch;
int len;
//flag is set true
int valid = 1;
printf("Enter a word: \n");
for(len = 0; (ch = getchar()) != '\n'; len++)
{
//change flag if character is not number nor letter
if(!isalpha(ch) && !isdigit(ch))
{
valid = 0;
}
}
printf("Input length: %d\n", len);
if (valid == 1)
{
printf("Valid\n");
}
else
{
printf("Invalid\n");
}
return 0;
}
The error here is when checking every character, by toggling valid to 0 and 1 really you are only looking at the last character. What you want to do is for every character as soon as you find one character that is not a digit/alpha character then exit
you can replace your if/else with something like this
vlaid = 1; //always assume its valid
for(int length = 0; (ch = getchar()) != '\n'; length++)
{
if(!isalnum(ch)) //if not a valid character
{
valid = 0; // string is not valid anymore
break; // we can exit the loop
}
}

Bucky Robert's first task, validating password with isupper(), isdigit(), and the containment of a '$' character

Ok so I have been watching Bucky Robert's tutorials on C programming and the first task he gives the viewers is to make a program that checks if a password has at least one upper case character, one digit, and one dollar sign.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int main()
{
int upper = 0;
int digit = 0;
int dollar = 0;
char password[16];
int loop = 1;
while(loop == 1){
printf("Enter your password: ");
scanf(" %s", password);
int i;
for(i = 0; i <= 16; i++){
printf(" %c", password[i]);
if(isupper(password[i])){
upper = 1;
printf("\t is upper");
}
if(isdigit(password[i])){
digit = 1;
printf("\t is digit");
}
if(password[i] == '$'){
dollar = 1;
printf("\t is dollar");
}
printf("\n");
}
if((upper == 1) && (digit == 1) && (dollar == 1)){
printf("Your password is valid\n");
loop = 0;
} else {
printf("Your password is invalid\n");
}
}
return 0;
}
The program prints this in the console after an input of 'P4sswoRd':
Enter your password: P4sswoRd
P is upper
4 is digit
s
s
w
o
R is upper
d
╨
#
ö
 
`
$ is dollar
Your password is valid
None of the passwords i entered contain the '$' character yet the program still finds a way to detect it. The reason it prints "is digit" or "is dollar" after the character was to check what had gone wrong in the code and see why the passwords were all valid. I have no idea why all those random characters are printed and I would rather know what is going wrong with my program rather than taking a new approach to the task at hand.
This loop is wrong:
for(i = 0; i <= 16; i++){
If you only type 4 characters, you should only be checking the first 4 characters of the string. This is why you're seeing lots of random characters -- those are the garbage that's in the remaining elements of password. It should be:
size_t pw_len = strlen(password);
for (i = 0; i < pw_len; i++) {
Also, remember that since arrays are zero-based, the last element of the array has the index length-1. So if you do want to process all the elements of an array declared password[16], the loop criteria should be i < 16, not i <= 16 -- that will try to access outside the array on the last iteration.
You also need to initialize the upper, digit, and dollar variables at the beginning of the while loop. Otherwise, if you type a password with numbers and $, then a new password with uppers, the second password will be called valid because it still has the digit and dollar settings from the previous password.
So it should be:
while(loop == 1){
upper = digit = dollar = 0;

Read the digits for a number one at a time, checking for duplicates

The program is meant to receive a number, one digit at a time, and use that digit as an index for the array to check if it's true; if so then break out of the loop and if not, set it to true and continue scanning the other digits til it reaches the last. It's supposed to tell only if a digit was repeated or not at this point.
I have this code so far but I can't seem to get it working. Can anyone help me? I noticed while troubleshooting on my own by testing the value of the variables after execution that sometimes the digits aren't even read, only the first digit entered is read.
Here's the code:
#include <stdio.h>
#define true 1
#define false 0
typedef int bool;
int main(void)
{
// Variables to contain the seen digits
bool seendig[10] = { false };
long entered;
int container;
printf("This Program Is Designed To Determine If Any Digits Has Been Repeated!\n Please Enter a Number: ");
scanf("%1d", &entered);
while (entered > 0)
{
container = entered;
if (seendig[container])
break;
seendig[container] = true;
entered /= 10;
}
if (entered > 0)
printf("\nThe Digit Was Repeated\n\n");
else
printf("The Digit Was Not Repeated\n\n");
system("pause");
return 0;
}
The part
container = entered;
if (seendig[container])
will cause out-of-range access if 10 or larger integer is inputted.
I guess
container = entered;
should be
container = entered % 10;
to get the least significant digit in decimal.
Is your code only trying to let the user input 1 number? or many number? If it is the latter then your scanf("%1d",&entered); should be inside of a loop and also I would recommend that you use a post-test loop or do-while loop since you need to let the user input a number first before checking it.
You should also consider 0 since it is a valid index value in an array
do {
printf("This Program Is Designed To Determine If Any Digits Has Been Repeated!\nPlease Enter a Number: ");
scanf("%d", &temp);
entered = temp;
if(temp >= 0){
while(temp > 0){
entered = temp % 10;
temp/=10;
}
}else{
break;
}
if(entered >= 0 && seendig[entered] != true){
seendig[entered] = true;
}else{
flag = 1;
}
printf("\n");
}while(flag == 0);
if (entered >= 0){
if(flag == 1){
printf("\nThe Digit Was Repeated\n\n");
}else{
printf("The Digit Was Not Repeated\n\n");
}
}else{
printf("The Digit You have Inputted is a Negative Value\n\n");
}
you should add this line of code to end of your while loop
scanf_s("%d", &entered);

Resources