This code is for the CS50 Harvard course Pset 2 substitution.
one section of my program requires a check on the key to make sure characters are not repeated. I am failing this check as it reads ' :( Handles duplicate characters in Key - timed out while waiting for program to exit'
What needs to be fixed in my code to pass this final check?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[])
{
printf("\n");
//check if we have correct number of command line arguments
if (argc != 2)
{
printf("Usage: ./substitution key\n");
printf("(Please enter only 2 command line arguments)\n");
return 1; //error
}
//initialize global variables for use//
//length of key
int s = strlen(argv[1]);
//copy of key may be altered if it is not valid yet
string k = argv[1];
// if else to check if our key is 26 characters
if (s == 26)
{
for (int i = 0; i < 26; i++)
{
//checks each index in k to see if it is alpha or not, throws error if not
if (!isalpha(k[i]))
{
printf("Usage: ./substitution key\n");
printf("(Key must be alphabetical)\n");
return 1; //error
}
}
for (int i = 0; i < s; i++)
{
for (int j = i + 1; j < s; j++)
{
if (isupper(k[i]))
{
k[i] = tolower(k[i]);
}
if (k[i] == k[j])
{
printf("Usage: ./substitution key\n");
printf("(Key can not have repeating characters)\n");
return 1; //error
}
}
}
}
else
{
//if we dont have 26 characters
printf("Usage: ./subsitution key\n");
printf("Key must be 26 characters\n");
return 1;
}
//true key for our cipher
string key = k;
//ask user for plaintext
string plaintext = get_string("Plaintext: ");
int n = strlen(plaintext);
printf("ciphertext: ");
char *ciphertext = malloc(n);
for (int i = 0; i < n; i++)
{
//plaintext = Hello (H - 65 = 7 (8TH INDEX) IN OUR KEY)
if (isalpha(plaintext[i]))
{
if (isupper(plaintext[i]))
{
int j = plaintext[i] - 'A';
printf("%c", toupper(key[j]));
}
if (islower(plaintext[i]))
{
int q = plaintext[i] - 'a';
printf("%c", tolower(key[q]));
}
}
else if (isdigit(plaintext[i]))
{
printf("%c", plaintext[i]);
}
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
}
check50 tells you what argument it uses for the test. When you run your code with that argument, does it complain about duplicate characters in the key?
This program will not find a duplicate if the second occurance of a character is in upper case.
Can someone explain if I switch
// 3.3 Must not contained repeated letters |
if (toupper(argv[1][j] == toupper(argv[1][k])))
to
if (tolower(argv[1][j] == tolower(argv[1][k])))
why it doesn't work anymore, even though the letters could be the same the computer doesn't recognize it?
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc,string argv[])
{
int count_key_char = 0;
int count_repeated_char = 0;
//Exactly 1 command line argument
if (argc == 2)
{
//Must all be letters
for (int i = 0; i < strlen(argv[1]); i++)
{
if (isalpha(argv[1][i]))
{
count_key_char++;
}
}
// 3.3 Must not contained repeated character/s
for (int j = 0, n = strlen(argv[1]); j < n; j++)
{
for (int k = j + 1; k < n; k++)
{
if (toupper(argv[1][j] == toupper(argv[1][k])))
{
count_repeated_char++;
}
}
}
// Must be 26 characters (Fail criteria)
if (strlen(argv[1]) != 26)
{
printf("Key must contain 26 characters.\n");
return 1;
}
// Must be all letters (Fail criteria)
else if (count_key_char != strlen(argv[1]))
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
// Must not contained repeated character/s (Fail criteria)
else if (count_repeated_char != 0)
{
printf("Key must not contain repeated characters.\n");
return 1;
}
// 4. Get plaintext; Prompt user for plaintext
else
{
string plaintext = get_string("Plaintext: ");
printf("ciphertext: ");
for (int w = 0; w < strlen(plaintext); w++)
{
// If character are letter & uppercase
if (isalpha(plaintext[w]) && isupper(plaintext[w]))
{
int upper = (plaintext[w] -65);
printf("%c", toupper(argv[1][upper]));
}
// If character are lower & lowercase
else if (isalpha(plaintext[w]) && islower(plaintext[w]))
{
int lower = (plaintext[w] - 97);
printf("%c", tolower(argv[1][lower]));
}
// If character are not letter, print as it is
else
{
printf("%c", plaintext[w]);
}
}
printf("\n");
return 0;
}
}
else
{
printf("Usage: ./substitution key\n");
return 1;
}
}
There is a misplaced close paren ) in this line
if (toupper(argv[1][j] == toupper(argv[1][k])))
The toupper will fail if the letters are lower case; the tolower will fail if the letters are upper case.
It is evaluating this
argv[1][j] == toupper(argv[1][k])
which will return 1 (true) if both characters are the same upper case letter. If the key is entered in lower case line will return 0 (false) and program will not consider it a repeated character.
Properly enclose the toupper argument in parentheses and the problem will be solved.
I created a code for pset2 problem, named substitution in C.
The programs takes an String of Characters in command-line and apply it to a given text and changes it, printing the ciphertext.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
//First part, checks KEY input
if (argc != 2) // checks quantity of command-line arguments
{
printf("Usage: ./substitution KEY\n");
return 1;
}
int i = 0;
int largo = strlen(argv[1]);
while (i < largo) //checks if char are alphabetic
{
if (isalpha(argv[1][i]) == 0)
{
printf("Key must contain only alphabetic characters\n");
return 1;
}
else //changes to uppercase
{
argv[1][i] = toupper(argv[1][i]);
}
i++;
}
if (i != 26) //checks length of Key
{
printf("Key must be 26 characters long\n");
return 1;
}
for (int j = 0; j < largo - 1; ++j) //checks repetition of characters
{
for (int k = j + 1; k < largo; ++k)
{
if (argv[1][j] == argv[1][k])
{
printf("Key must not have duplicated characters\n");
return 1;
}
}
}
//second part asks for input
//declaration of strings to get and transform
string plaintext = get_string("plaintext:");
string alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string ciphertext = plaintext;
//third part: cifrado
for (int x = 0; x < strlen(plaintext); ++x)
{
if (isalpha(plaintext[x]) != 0)
{
int lower = 0;
if (islower(plaintext[x]) != 0) // is lower case?
{
lower = 1;
plaintext[x] = toupper(plaintext[x]);
}
for (int y = 0; y < strlen(alfabeto); ++y)
{
//printf("alfa: %c text: %c\n", alfabeto[y], plaintext[x]);
if (alfabeto[y] == plaintext[x])
{
ciphertext[x] = argv[1][y];
//printf("c: %s\n", ciphertext);
break;
}
}
if (lower == 1)
{
ciphertext[x] = tolower(ciphertext[x]);
}
}
}
printf("ciphertext: %s", ciphertext);
printf("\n");
return 0;
}
The issue is that the last if statement, kind of assigns the character in "ciphertext" to the "plaintext" variable, so i had to put a break statement in it, to stop that.
Any guesses why this program doesn't work as intended when there's no "break" statement?
if (alfabeto[y] == plaintext[x])
{
ciphertext[x] = argv[1][y];
//printf("c: %s\n", ciphertext);
break;
}
I would expect that the values in the array would repeat in every 3rd character but I do not understand why to out put are different if they are referencing the same index.
Input:
Command line argument: baz
User input: barfoo
Output: caqmut
Expected output: caqgon
Values of key_number[] after first for loop:
key_number[0] = 1
key_number[1] = 0
key_number[2] = 25
key_number[3] = 32767
key_number[4] = 32
key_number[5] = 57
Code used:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if(argc<=1)
{
printf("Usage: ./vigenere k");
exit(1);
}
// convert command line input from string to int varible keyword
string keyword = argv[1];
//prompt user input and initialized
printf("plaintext: ");
string plaintext = get_string();
printf("ciphertext: ");
//create lower_case_array
char lower_case[26] = "abcdefghijklmnopqrstuvwxyz";
//create upper_case_array
char upper_case[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//convert key from string to int[]
//int array_length =strlen(plaintext);
int key_number[6]; // [array_length]
int key_length = strlen(keyword);
//loop though key_number for the length of plaintext
for (int i = 0; i < (strlen(plaintext)); i++)
{
//if char is uppercase
if (keyword[i] >= 65 && keyword[i] < 91 )
{
key_number[i] = (keyword[i % key_length]) - 65;
}
// if char is lowercase
else if (keyword[i] >= 97 && keyword[i] < 123)
{
key_number[i] = (keyword[i % key_length]) - 97;
}
}
//print out letters accoring to ceasar cryptography
for (int i = 0; i < strlen(plaintext); i++)
{
//branch if letter is upper case
if (plaintext[i] >= 65 && plaintext[i] < 91 )
{
printf("%c", upper_case[((plaintext[i] - 65 + key_number[i]) % 26)]);
}
//branch if letter is lower case
else if (plaintext[i] >= 97 && plaintext[i] < 123)
{
printf("%c", lower_case[((plaintext[i] - 97 + key_number[i]) % 26)]);
}
//branch if any other character
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
}
what puzzled me when I read your code is that you're using keyword[i % key_length] sometimes and sometimes keyword[i].
So when you're using i, you can go past the keyword table: undefined behaviour because plaintext is probably longer than keyword
You could have avoided that by rewriting your code more cleanly:
for (int i = 0; i < (strlen(plaintext)); i++)
{
char kc = keyword[i % key_length];
//if char is uppercase
if (kc >= 'A' && kc <= 'Z' ) // aka isupper(kc)
{
key_number[i] = kc - 'A';
}
// if char is lowercase
else if (kc >= 'a' && kc <= 'z') // aka islower(kc)
{
key_number[i] = kc - 'a';
}
}
I keep making changes to the looping part of this code and my check50 always fails. I don't know what's going on. Below is my code:
#include <stdio.h>
#include <ctype.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[])
{
// declare variables
int cipherText;
if (argc != 2)
{
printf("Usage: ./vigenere keyword");
printf("\n");
return 1;
}
// keyword is the second command line argument
string key = argv[1];
int keylen = strlen(argv[1]);
// iterate through keyword to check if alphabetical
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if ((key[i] >= '0') && (key[i] <= '9'))
{
printf("Keyword must consist only of letters.");
return 1;
}
}
// get the plaintext
string plainText = GetString();
// encypher - iterate over the characters in string, print each one encrypted
for (int i = 0, j = 0, n = strlen(plainText); i < n; i++, j++)
{
// start the key again if key shorter than plainText
if (j >= strlen(key))
{
j = 0;
}
// skip key[j] if plainText[i] is not an alpha character
if (!isalpha(plainText[i]))
{
j = (j-1);
}
// makes Aa = 0, Zz = 25 for the uppercase letters
if (isupper(key[j]))
{
key[j] = (key[j] - 'A');
}
// makes Aa = 0, Zz = 25 for lowercase letters
else if (islower(key[j]))
{
key[j] = (key[j] - 'a');
}
if (isupper(plainText[i]))
{
cipherText = (plainText[i] - 'A');
cipherText = ((cipherText + key[j%keylen])%26) + 'A';
printf("%c", cipherText);
}
else if (islower(plainText[i]))
{
cipherText = (plainText[i] - 'a');
cipherText = ((cipherText + key[j%keylen])%26 + 'a');
printf("%c", cipherText);
}
else
{
printf("%c", plainText[i]);
}
}
printf("\n");
return 0;
}
Some answered this: "The first for loop has a problem. The condition is checking for i > keylen when it should be checking for i < keylen".
Also when computing the next output value, the steps should be
(p[i]-65) results in a number between 0 and 25
adding (key[i % keylen]) results in a number between 0 and 50
apply modulo 26 so the number is between 0 and 25 (this is the missing step)
then add 65 to get the output"
and it's what I tried to do.
Given this code:
int keylen = strlen(argv[1]);
// iterate through keyword to check if alphabetical
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if ((key[i] >= '0') && (key[i] <= '9'))
{
printf("Keyword must consist only of letters.");
return 1;
}
}
Your test inside the loop identifies digits as 'not a letter' (which is valid), but ignores punctuation, spaces and so on. You should probably be using if (!isalpha(key[i])) for the test (and it is courteous to print the erroneous character in the error message, which should be printed on standard error, not standard output, and should end with a newline:
fprintf(stderr, "Keyword must consist only of letters (%c found at %d)\n",
key[i], i+1);
You could refine that so it doesn't try printing non-printable characters with %c, but this is a huge step in the right direction.
You really don't need to set n in the loop; you just set keylen before the loop, so you could have written:
for (int i = 0; i < keylen; i++)
However, that is mostly cosmetic. Your real problem lies here:
// start the key again if key shorter than plainText
if (j >= strlen(key))
{
j = 0;
}
// makes Aa = 0, Zz = 25 for the uppercase letters
if (isupper(key[j]))
{
key[j] = (key[j] - 'A');
}
// makes Aa = 0, Zz = 25 for lowercase letters
else if (islower(key[j]))
{
key[j] = (key[j] - 'a');
}
You modify the key string on each iteration through the key. Unfortunately, though, if any of the letters in the key is a or A, you've converted that to '\0', which means that strlen(key) returns a different answer from before. So, you should use keylen in place of strlen(). AFAICS, if there isn't an a or A, that part of the code is OK.
Later, you have:
if (isupper(plainText[i]))
{
cipherText = (plainText[i] - 'A');
cipherText = ((cipherText + key[j%keylen])%26) + 'A';
printf("%c", cipherText);
}
The j % keylen is superfluous; j is already limited to 0 .. keylen-1. Similarly with the code for lower-case text.
Putting these changes together, and dummying up a GetString() function using fgets(), I get:
#include <stdio.h>
#include <ctype.h>
// #include <cs50.h>
#include <stdlib.h>
#include <string.h>
typedef char *string;
static char *GetString(void)
{
static char buffer[4096];
if (fgets(buffer, sizeof(buffer), stdin) == 0)
{
fprintf(stderr, "EOF detected in GetString()\n");
exit(EXIT_SUCCESS);
}
buffer[strlen(buffer) - 1] = '\0';
return buffer;
}
int main(int argc, string argv[])
{
// declare variables
int cipherText;
if (argc != 2)
{
printf("Usage: ./vigenere keyword");
printf("\n");
return 1;
}
// keyword is the second command line argument
string key = argv[1];
int keylen = strlen(argv[1]);
// iterate through keyword to check if alphabetical
for (int i = 0; i < keylen; i++)
{
if (!isalpha(key[i]))
{
printf("Keyword must consist only of letters (%c at %d)\n",
key[i], i+1);
return 1;
}
}
// get the plaintext
string plainText = GetString();
// encypher - iterate over the characters in string, print each one encrypted
for (int i = 0, j = 0, n = strlen(plainText); i < n; i++, j++)
{
// start the key again if key shorter than plainText
if (j >= keylen)
{
j = 0;
}
// skip key[j] if plainText[i] is not an alpha character
if (!isalpha(plainText[i]))
{
j = (j - 1);
}
// makes Aa = 0, Zz = 25 for the uppercase letters
if (isupper(key[j]))
{
key[j] = (key[j] - 'A');
}
// makes Aa = 0, Zz = 25 for lowercase letters
else if (islower(key[j]))
{
key[j] = (key[j] - 'a');
}
if (isupper(plainText[i]))
{
cipherText = (plainText[i] - 'A');
cipherText = ((cipherText + key[j]) % 26) + 'A';
printf("%c", cipherText);
}
else if (islower(plainText[i]))
{
cipherText = (plainText[i] - 'a');
cipherText = ((cipherText + key[j]) % 26 + 'a');
printf("%c", cipherText);
}
else
{
printf("%c", plainText[i]);
}
}
printf("\n");
return 0;
}
Sample run:
$ ./vigenere bakedalaska
What a wonderful world! The news is good, and the Vigenere cipher is solved.
Xhkx d wznvorguv arrwd! Lre oegw ls rogn, aod dlh Vtgwxese mmshpr ac splfig.
$