I have the following code that accepts a string that resembles hours.. I want to check if the string format is actually xx-yy where xx resembles an hour as does yy.. The code worked fine but when I input "02-13-" it returns true but I want it to return false cause it's not correct (cause it has - at the end)
bool hourIsValid(char * hours) {
int openH = 0;
int closeH = 0;
if ((sscanf(hours, "%d-%d", & openH, & closeH) == 2) && openH >= 0 && openH <= 24 && closeH >= 0 && closeH <= 24) {
if (openH >= closeH) {
return false;
}
return true;
} else {
return false;
}
}
The solution depends on how "pedantic" your code has to be when deciding if an input is valid or not. For example, you might expect "2-14", "02 - 15", " 2-14 " as valid, or you might not. It depends.
If you want to get a pedantic version that accepts only the exact format "dd-dd" with no leading or trailing characters or white spaces and with exactly a two-digit format for each hour value, you could check the string as follows before reading in the values with your sscanf-code:
if (strlen(hours) != 5)
return 0;
if (hours[2] != '-')
return 0;
if ( !isdigit(hours[0])
|| !isdigit(hours[1])
|| !isdigit(hours[3])
|| !isdigit(hours[4])
)
return 0;
Another way:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
bool hourIsValid(
char *I__hours
)
{
long openH, closeH;
openH=strtol(I__hours, &I__hours, 10);
if( '-' != *I__hours // Ensure that there is a dash between the two numbers in the string.
|| 0 >= openH // Ensure that the 1st number in the string is non-negative.
|| 24 <= openH // Ensure that the 1st number in the string is 24 or less.
)
return(false);
++I__hours; // Skip over the dash between the two numbers.
closeH=strtol(I__hours, &I__hours, 10);
if( *I__hours // Ensure that there are no additional characters beyond the 2nd number.
|| 0 >= closeH // Ensure that the 2nd number in the string is non-negative.
|| 24 <= closeH // Ensure that the 2nd number in the string is 24 or less.
|| openH >= closeH // Ensure that the 1st number is less or equal to the 2nd number.
)
return(false);
return(true);
}
Related
I'm trying to concatenate an inputted char array to another in order to reverse my string and check if it's a palindrome, but I want to skip over special characters. When my for loop hits a special character, it stops reading the rest of the string.
This is what my for loop looks like
for(i=0; i < strlen(original); i++)
{
if((original[i] >= 49 && original[i] <= 57) || (original[i] >= 97 && original[i] <= 122))
{
cleaned[i] = original[i];
}
else if(original[i] >= 65 && original[i] <= 90)
{
cleaned[i] = original[i] + 32;
}
else if((original[i] >= 33 && original[i] <= 47) || (original[i] >= 58 && original[i] <= 64) || (original[i] >= 91 && original[i] <= 96) || (original[i] >= 123 && original[i] <= 126))
{
continue;
}
}
"Cleaned" is meant to hold the original string characters with lowercased letters and ignored special characters.
Is there some way I can move to the next non-special character and replace the space of the first special character?
You need two indices: one for the array you're reading from and the other for the array you're writing to. If you use the same index for both, you'll end up with unset slots in your output array. Presumably these happen to be 0 in this case, prematurely terminating the output string.
Let's look at
0123456
original: a!bb!a␀
cleaned: abba␀
01234
You want the program to have the same effect as the following:
cleaned[0] = original[0];
// Skip original[1];
cleaned[1] = original[2];
cleaned[2] = original[3];
// Skip original[4];
cleaned[3] = original[5];
cleaned[4] = 0;
As you can see, cleaned[i] = original[i]; can't possibly be right. The offset in the original string is going to be different from the offset in the cleaned string, so you need to have two indexes or pointers.
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdbool.h>
_Bool check_domain(char *domain) {
int str_count = 0, x = strlen(domain), y = 0;
if (domain[x-1] == '.' || domain[0] == '.'){
return false; //if there is a period at start or end
}
else{
while (domain[y]){
if (str_count==2){
return false; //if 2 consecutive periods
}
if (domain[y] == 46 || 65<=domain[y]<=90 || 97<=domain[y]<=122){ //if character is period or alphanumeric
if (domain[y] == '.'){
str_count += 1;
}
else{
str_count = 0;
}
}
else{ // if character is non period non alphanumeric
return false;
}
y += 1;
}
}
return true;
}
int main(void){
char domain[] = "nus#edu.sg";
printf("%d",check_domain(domain));
}
EDIT:
Thank you for your responses, have a little more clarity on the subject now.
In addition to not having a period at the end or beginning, nor two consecutive periods, there should be no non-period, non-alphanumeric character inside the string.
For reasons I can't figure out, this code fails to return false for cases wherein there are non-period, non-alphanumeric characters inside the string.
Start here
if (domain[y] == 46 || 65<=domain[y]<=90 || 97<=domain[y]<=122){
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
C can't handle statement like n <= X <= m in the way you expect.
C treats this as:
(65<=domain[y]) <= 90
^^^^^^^^^^^^^^^ ^^
bool <= number
So first C will calculate a bool result from 65<=domain[y] and then it will compare that bool to a number. The bool is converted to either 0 or 1 so when comparing with 90, the result is always true.
If you had checked the compiler warnings, you would have seen something like:
main.cpp:18:49: warning: comparison of constant '90' with boolean expression is always true [-Wbool-compare]
18 | if (domain[y] == 46 || 65<=domain[y]<=90 || 97<=domain[y]<=122){
| ^~
which tells you that something is wrong.
You need to write
if (domain[y] == 46 || (65<=domain[y] && domain[y]<=90) || (97<=domain[y] && domain[y]<=122)){
BTW:
Don't use magic numbers like 65, 90 and so on. Use the character instead. Like:
(65<=domain[y] && domain[y]<=90) --> ('A' <= domain[y] && domain[y] <= 'Z')
It makes the code much easier to understand.
Further - consider using the function isalpha(domain[y]) for such checks.
Finally - Your text (and function name) says that that you want to check for a domain name. However, your input is more like an e-mail address.
I'm new to CS50 and C and I'm currently working on the Week 1 assignment 'Credit'. I've got most of it working, but I'm running into an issue where no matter how true I feel a statement should be, my if comparison is flagging it false. Is my logic off, or am I missing something? Is there a limit on the size of a number I can use in If comparisons?
Here's my code so far:
#include <cs50.h>
#include <stdio.h>
unsigned char cardArray[20];
unsigned long creditCard;
string cardType; //String used for checking what type of credit card has been submitted.
//int remainder;
int n = 0;//Increment array position
int x = 0;
int main(void)
{
creditCard = get_long("Input: "); //Prompt user for CC
printf("%lu", creditCard);
while(creditCard > 0 && creditCard != 0) //Take CC Int and convert to Char Array for math functions
{
cardArray[n] = (creditCard % 10); //Modulus creditCard remainder
//printf("%i\n", n); //Array indices check
creditCard = (creditCard / 10); //Reduce creditCard by factor of 10
printf("%d\n", cardArray[n]);
n += 1; //increment Array indices
}
printf("Pos 15: %d\n", cardArray[15]);
//Use if >= to check size of creditCard
if(creditCard > 1000000000000 && creditCard <= 9999999999999){printf("X3: %s\n", cardType); //Check for creditCard length of 13
if(cardArray[12] == 4){
cardType = "VISA\n";
}
}else printf("X: %s\n", cardType);
if(creditCard >= 100000000000000 && creditCard <= 999999999999999){ printf("X1: %s\n", cardType);//Check for creditCard length of 15
if(cardArray[14] == 3 && (cardArray[13] == 4 || cardArray[13] == 7)){//If 15 AND starts with 34 OR 37 then set card value to American Express
cardType = "AMEX\n";
}
}else printf("X: %s\n", cardType);
if(creditCard >= 1000000000000000 && creditCard <= 9999999999999999){printf("X2: %s\n", cardType); //Check for creditCard length of 16
if(cardArray[15] == cardArray[15]){//Does it start with 4? set card value to Visa
cardType = "VISA\n";
printf("%s", cardType);
}
else printf("X: %s\n", cardType);
if(cardArray[15] == 5 && (cardArray[14] == 1 || cardArray[14] == 2 || cardArray[14] == 3 || cardArray[14] == 4 || cardArray[14] == 5)){//Does it begin with 51, 52, 53, 54, or 55? set card value to Master Card
cardType = "MASTERCARD\n";
}
}
else cardType = "INVALID\n";
printf("%s", cardType);
}
I've tested with several printf lines to see where my code is going. If I change comparisons to something like 1 == 1 I can flag the comparison as true, but using comparisons like creditCard >= 1000000000000 will always fail, even if I'm passing along 10000000000000.
An unsigned long on your target is likely a 32 bit value. 232 = 4294967296, so clearly the comparison with 10000000000000 for example will always evaluate to false.
If your compiler did not issue any warnings, you should look at your compiler settings.
Change the type of creditCard to unsigned long long or better uint64_t (declared in <stdint.h>). Then you should also specify the the literal integer with the ULL suffix, eg:
creditCard >= 100000000000000ULL
However, the implementation is seriously flawed. You get the card number as an integer then convert it to a string. Since your data type will not hold a credit card number, both the string and the integer will be incorrect. Credit card numbers are not arithmetic objects, and storing as an integer is a bad idea for more reasons that just range - even a uint64_t is only good for 19 digits - which may be enough for now, but possibly not in future. The number should be received and processed as a string. The advantages are that you can perform more sophisticated validation, allow digit grouping spaces, and will not loose leading zero digits.
String comparisons on normalised digit strings (i.e. with spaces removed) will work as well as an arithmetic comparison. For example:
if( strcmp( cardNumberString, "10000000000" ) >= 0 )
achieves the same result as:
if( cardNumberUnsLongLong >= 10000000000ULL )
I'm a first time programmer trying to complete a simple command line program as part of the first assignment for an online course I am taking, but I seem to have hit a roadblock that I can't figure out with GDB or my own research.
After hours of rewrites, and hours of debugging, I finally got the code below to compile. The program is supposed to take a credit card number as an input, and then check whether it's valid per the specifications of the assignment. I used a test number from here: PayPal Test Credit Cards
The odd thing is, when I enter an AMEX card number, it correctly produces the text "AMEX", but when I try a Visa or a Master Card, it prints "INVALID".
In GDB I broke at the Verify function and it seems to incorrectly skip these two if/else if statements without proceeding to the Checksum function even though conditions appear to be met.
if (firstDigit == 4 && totalDigits == (13 | 16) && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Visa.
...
else if (firstDigit == 5 && secondDigit == (1 | 2 | 3 | 4 | 5) && totalDigits == 16 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Mastercard.
...
The AMEX line of code that correctly executes is:
else if (firstDigit == 3 && secondDigit == (4 | 7) && totalDigits == 15 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid American Express.
The arguments for all three lines seem to be formatted exactly the same. That is far as I could get in GDB though. I would print totalDigits, firstDigit, and secondDigit in GDB right before stepping through the above two non-executing lines and everything looked correct. So I'm stumped, why is the AMEX line executing, but not the others?
Thanks in advance everyone. This is the first program after hello.c that I've tried to write, so I am open to absolutely any criticism or suggestions if it looks like I'm doing something weird/wrong.
Full code:
checker.c
#include <stdio.h>
#include <stdlib.h>
int MAX = 16;
int* DigitSort(unsigned long long x, int* array);
int Verify(int* array);
int main (void)
{
int* output = malloc (sizeof(int) * (MAX + 2)); // creates a blank array for the individual digits of the card number.
unsigned long long userInput = 0;
do
{
printf("Please enter a credit card number:\n");
scanf("%lld", &userInput);
}
while (userInput <= 0); // checks to make sure the user entered a number.
switch(Verify(DigitSort(userInput, output))) // sorts the user's input into individual digits and verifies the card type and validity.
{
case 1 :
printf("VISA\n");
break;
case 2 :
printf("MASTERCARD\n");
break;
case 3 :
printf("AMEX\n");
break;
case 0 :
printf("INVALID\n");
break;
default :
printf("INVALID\n");
}
free(output);
return 0;
}
int Verify(int* array) // verifies whether or not a card number is valid. Must pass the function a sorted array of individual digits.
{
int* cardNumber = array;
int firstDigit = cardNumber[0];
int secondDigit = cardNumber[1];
int totalDigits = 0;
int Checksum(int* cardNumber, int totalDigits);
int i = 0;
while (firstDigit >= 1 && cardNumber[i] >= 0) // this step counts the number of digits in the array.
{
totalDigits = totalDigits + 1;
i++;
}
if (firstDigit == 4 && totalDigits == (13 | 16) && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Visa.
{
return 1;
}
else if (firstDigit == 5 && secondDigit == (1 | 2 | 3 | 4 | 5) && totalDigits == 16 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Mastercard.
{
return 2;
}
else if (firstDigit == 3 && secondDigit == (4 | 7) && totalDigits == 15 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid American Express.
{
return 3;
}
else // if the card number doesn't match any of the above conditions or fails the checksum, an 'I' for Invalid is returned.
{
return 0;
}
}
int* DigitSort(unsigned long long x, int* array) // takes a long long as input and sorts it into individual digits
{
int* arrayReversed = malloc (sizeof(int) * (MAX + 2)); // creates a new array to hold the reversed order of digits.
int i = 0;
arrayReversed[0] = 0;
if (i < (MAX - 1) && x >= 10)
{
do
{
arrayReversed[i] = x % 10;
x = x / 10;
i++;
}
while (i < (MAX -1) && x >= 10);
}
if (i < MAX && x >= 1 && x <= 9)
{
arrayReversed[i] = (int) x;
x = (x - x);
}
if (x == 0)
{
int j = 0;
do
{
array[j] = arrayReversed[i]; // sorts the digits from the reversed array and places them into the sorted array.
j++;
i--;
}
while (j < MAX && i >= 0);
array[j] = -1;
}
free(arrayReversed);
return array;
}
int Checksum(int* cardNumber, int totalDigits)
{
int sum1 = 0;
int sum2 = 0;
int i = (totalDigits - 2);
int j = (totalDigits - 1);
while (i >= 0)
{
sum1 = ((cardNumber[i] * 2)%10) + ((cardNumber[i] * 2)/10) + sum1;
i -= 2;
}
while (j >= 0)
{
sum2 = (cardNumber[j] + sum2);
j -= 2;
}
if (((sum1 + sum2) % 10) == 0)
{
return 0;
}
else
{
return 1;
}
}
Your first problem is here:
if (firstDigit == 4 && totalDigits == (13 | 16) && ...
You need to write:
if (firstDigit == 4 && (totalDigits == 13 || totalDigits == 16) && ...
Your first check is looking for 0x1D == 29 as the number of digits (because, as paisanco points out in a comment, the | operator is the bitwise OR operator), and no credit card needs 29 digits (yet, and not for a long time to come). Note the extra parentheses for clarity and accuracy. Don't mess around risking removing them — the code won't work properly again. And in general, be explicit if your condition has both && and || operators and use parentheses to group terms explicitly.
You have similar problems elsewhere. As it happens, (4 | 7) is the same value as 7, so the condition works when the second digit is 7 (but not when it is 4). But it doesn't mean what you intended it to mean.
Computer languages don't work the same as human languages. Get used to writing out the condition somewhat more verbosely. Some other languages provide shorthands for these conditions; C is not such a language.
I'm having a problem where I am wanting to go through an array and check that only positive numbers have been entered. I know that it is possible to use isDigit from ctype.h but I'd rather construct something myself. The way I think it is possible, is to iterate through each element of the array and see if the value stored there is between 0 and 9, but it isn't working. This is my code so far:
char testArray[11] = {'0'};
printf("Enter a string no longer than 10 chars");
scanf("%s", testArray);
int x;
int notanumber = 0;
for (x = 0; x < 11; x++) {
if ((testArray[x] < 0) || (testArray[x] > 9)) {
notanumber++;
}
}
printf("%i", notanumber);
It is not working because 0 and 9 are integers not characters.
Change your if condition to
if((testArray[x] >= '0') || (testArray[x] <= '9')){ ... }
to check the digits from 0 to 9.
this line
if((testArray[x] < 0) || (testArray[x] > 9)){
should be replaced by
if((testArray[x] < '0') || (testArray[x] > '9')){