#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.
Related
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 need to use boolen expressions instead of using terms such as: c: b? A, switch, else ... if. and using for / while-do / while as if is also not allowed.
My question is when receiving a name, based on its value we decide what to print. For example, if the name is shorter than 6 letters we print "player A is sent", if the letters sum (in ascii values) is more than 20 we send player B and stuff like that. In addition, if we can send more than one player, we will choose to send the largest lexicographic player (playerC < playerB < playerA.)
what I tried to do is something like this
bool sum = AsciiSum(name)>=20;
bool len = NameLength(name)<6;
...
so the rest should be that
if(sum)
printf("%c\n" ,'playerA');
else if (len)
printf("%c\n" ,'playerB');
else
printf("%c\n" ,'no player was sent');
But as I said , I cant use if and else or similar conditions. Will be grateful for any help in that last part.
One way of doing this would be to reroute your input through a custom print function which return a boolean value. That was, you could then just use the AND (&&) operator to call it.
Example code:
bool printMessage(char* string)
{
printf("%s", string);
return true;
}
bool sum = AsciiSum(name)>=20 && printMessage("playerA");
bool len = NameLength(name)<6 && printMessage("playerB");
// ... etc
Why does this work? The AND operator will only allow the next condition to be checked if the first one was true. Since your condition is that sum must be true in order to print the message, this is a way to call the method to print without directly using any if/else statements
Edit: An even better way, as suggested by Johnny Mopp is to use stdio puts(), which returns an integer (that's what a boolean is functionally).
bool sum = AsciiSum(name)>=20 && puts("playerA");
bool len = NameLength(name)<6 && puts("playerB");
With this, you don't even have to create a printMessage function, though you should stay with whichever feels more comfortable to work with.
Given an output function that always returns true :
bool print( const char* msg )
{
puts( msg ) ;
return true ;
}
You can then exploit short-circuit evaluation thus:
bool sum = AsciiSum(name)>=20;
bool len = NameLength(name)<6;
(sum && print("playerA")) ||
(len && print("playerB")) ||
print("no player was sent") ;
Testing all combinations thus:
bool sum = false, len = false ;
(sum && print("playerA")) || (len && print("playerB")) || print("no player was sent") ;
sum = false, len = true ;
(sum && print("playerA")) || (len && print("playerB")) || print("no player was sent") ;
sum = true, len = false ;
(sum && print("playerA")) || (len && print("playerB")) || print("no player was sent") ;
sum = true, len = true ;
(sum && print("playerA")) || (len && print("playerB")) || print("no player was sent") ;
correctly outputs:
no player was sent
playerB
playerA
playerA
Your compiler may issue a diagnostic regarding the result of the expression being unused; that can be supressed by a dummy assignment:
bool dummy = (sum && print("playerA")) ||
(len && print("playerB")) ||
print("no player was sent") ;
or by casting the whole expression to void:
(void)((sum && print("playerA")) ||
(len && print("playerB")) ||
print("no player was sent")) ;
The only requirement of the output function is that it returns a non-zero value to short-circuit the ||, to that end printf() will work if the message is not an empty string, and avoid the need to define a specific output function.:
(void)((sum && printf("playerA\n")) ||
(len && printf("playerB\n")) ||
printf("no player was sent\n")) ;
puts() however only guarantees to return a non-negative value on success, which includes zero, so should not be used.
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);
}
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've a matrix with size 7x7 that represents a game board. When a player makes a move, the program has to check the positions around the coordinates where the piece is, in order to detect another piece aside.
I use this function:
int check_position(COORDINATES coordinates, char board[7][7]) {
int result = -1;
if (board[coordinates.x][coordinates.y] != 'O' && board[coordinates.x-1][coordinates.y] != 'O' && board[coordinates.x][coordinates.y-1] != 'O' && board[coordinates.x+1][coordinates.y] != 'O' && board[coordinates.x][coordinates.y+1] != 'O' && board[coordinates.x-1][coordinates.y-1] != 'O' && board[coordinates.x+1][coordinates.y+1] != 'O' && board[coordinates.x-1][coordinates.y+1] != 'O' && board[coordinates.x+1][coordinates.y-1] != 'O') {
result = 1;
}
return result;
}
The first parameter are the coordinates of the player's piece as a struct, with members x and y. The second parameter is the board array.
The if statement doesn't work to well, and I don't know which alternative can I take.
Can you help me? Thanks!
You forgot about your coordinates overflowing at the borders. You can either test for this, or:
Hint: Make the array two rows and columns larger than the board and fill the border with "empty" marker. The active board will the have coordinates 1...7 This way your coordinates cannot wrap (1 - 1 and 7 + 1 are still within the array) and you do not have to care about the borders.
Note: If you just want to return a boolean value, it would be better to use stdbool.h and return a bool result. That way, the caller can directly use that function as a condition:
#include <stdbool.h>
...
bool check_position(COORDINATES coordinates, const char board[9][9]) {
int x = coordinates.x - 1
for ( int xc = 0 ; xc < 3 ; xc++ ) {
int y = coodinates.y - 1;
for ( int yc = 0 ; yc < 3 ; yc++ ) {
if ( board[x][y] != '0' )
return true;
y++;
}
x++;
}
return false;
}
Note: as you only need one one non-empty field, you can terminate instantly if you found one. That is identical to the multiple conditions. Of course, that also works for your original int result.
Note2: I modified the type of board to being const, as it is not changed inside the function.
You could also solve the edge overflow like this. Edit improved after discussion with #Olaf
#define BOARD 7
int check_position(COORDINATES coordinates, char board[BOARD][BOARD]) {
int result = -1;
int left = coordinates.x == 0 ? 0 : coordinates.x - 1;
int top = coordinates.y == 0 ? 0 : coordinates.y - 1;
int right = coordinates.x == BOARD-1 ? coordinates.x : coordinates.x + 1;
int bottom = coordinates.y == BOARD-1 ? coordinates.y : coordinates.y + 1;
if (board[left] [top] != 'O' &&
board[coordinates.x][top] != 'O' &&
board[right] [top] != 'O' &&
board[left] [coordinates.y] != 'O' &&
board[coordinates.x][coordinates.y] != 'O' &&
board[right] [coordinates.y] != 'O' &&
board[left] [bottom] != 'O' &&
board[coordinates.x][bottom] != 'O' &&
board[right] [bottom] != 'O' && )
{
result = 1;
}
return result;
}