I have been working CS50X pset2 substitution. I think it almost has done. When I input single char such as A or B D and so on... the ciphertext will get the right result. (e.g A will get J, "B" will get T and D will get E and so on...
However, if I input ABC, the ciphertext will only show J and the other cannot show. What did I do wrong?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int ciphertext = 0;
//Key
//JTREKYAVOGDXPSNCUIZLFBMWHQ
int main(int argc, string argv[])
{
//Check that program was run with one command-line argument
if (argc == 2)
{
string key = argv[1];
//check the key does it validate
for (int i = 0, n = strlen(key); i < n; i++)
{
string plaintext = get_string("plaintext: ");
printf("ciphertext: \n");
int u = 64;
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]) != 0)
{
for (int j = 0; j < 26; j++)
{
u = u + 1;
//printf("u is %c\n", u);
if (u == plaintext[k])
{
ciphertext = key[j];
printf("Key is %c\n", key[j]);
printf("Plaintext is %c\n", plaintext[k]);
printf("ciphertext is %c\n", ciphertext);
//break;
}
}
}
else
{
printf("%c", plaintext[k]);
}
}
return 0;
}
}
}
Your problems are logic errors that appear to result from changes you attempted to make to the code that got out of hand. You have unnecessary loops and unused variable in your code. You need to change the scope of the u declaration so it is reinitialized on each iteration. You simply need to remove the for (int i = 0, n = strlen(key); i < n; i++) loop altogether, neither i or n is used. The same for ciphertext and l (ell).
You need to move int u = 64; immediately after if (isupper(plaintext[k])) so it is reset on each iteration.
I suspect all the printf statements were for debugging and you really only want the key output. Putting it altogether, you can rearrange your code to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, string argv[]) {
if (argc == 2) { /* validate key given */
string key = argv[1];
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]))
{
int u = 64;
for (int j = 0; j < 26; j++)
{
u = u + 1;
if (u == plaintext[k]) {
printf("%c", key[j]);
break;
}
}
}
else
printf("%c", plaintext[k]);
}
putchar ('\n');
}
}
Example Use/Output
$ ./bin/cs50_cypher2 JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: ABD
ciphertext: JTE
Simplify Your Logic
If you think about what the code above is actually doing, it can be reduced to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main (int argc, string argv[]) {
if (argc < 2) { /* validate key given */
fputs ("usage: ./program key\n", stderr);
return 1;
}
string key = argv[1];
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]))
putchar (key[plaintext[k] - 'A']);
else
putchar (plaintext[k]);
}
putchar ('\n');
}
Or if you use a ternary operator, your for loop condenses to simply:
for (int k = 0, p = strlen(plaintext); k < p; k++)
putchar (isupper(plaintext[k]) ? key[plaintext[k] - 'A'] : plaintext[k]);
And since there is no need to call strlen(plaintext), you can eliminate string.h entirely and just loop for (int k = 0; plaintext[k]; k++) since plaintext is a nul-terminated string, your entire program can reduce to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int main (int argc, string argv[]) {
if (argc < 2) { /* validate key given */
fputs ("usage: ./program key\n", stderr);
return 1;
}
string key = argv[1];
string plaintext = get_string("plaintext : ");
printf("ciphertext: ");
for (int k = 0; plaintext[k]; k++)
putchar (isupper(plaintext[k]) ? key[plaintext[k] - 'A'] : plaintext[k]);
putchar ('\n');
}
Handling Lowercase Letters
You know your key entered as the first argument is all uppercase. So if you want to output a lowercase key, you must call tolower() on the key after you have applied the offsets. Since for an uppercase letter, you just want to know the offset within the 26-character key to use, you simply find out how many letters your current letter is from 'A' and get that offset in key[], e.g.
key[plaintext[k] - 'A']
For lowercase input, you need to find out the offset in key for the difference in lowercase letters, and then apply tolower() to the key, e.g.
tolower(key[plaintext[k] - 'a'])
See ASCII Table & Description
Putting that altogether, your for loop can be written as:
for (int k = 0; plaintext[k]; k++) {
if (isupper(plaintext[k]))
putchar (key[plaintext[k] - 'A']);
else if (islower(plaintext[k]))
putchar (tolower(key[plaintext[k] - 'a']));
else
putchar (plaintext[k]);
}
Example Use/Output
$ ./bin/cs50_cypher4 JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext : AbZ 50
ciphertext: JtQ 50
Look things over and let me know if you have further questions.
Related
I'm a newbie, so apologies if I don't explain myself well. If it helps, I'm doing this for the Caesar problem set as part of the Harvard CS50x OpenCourseWare.
I'm trying to convert user generated plain text to cipher text using a simple key. To accomplish this I'm attempting to use a wraparound counting formula in my last function. However, sometimes I get blanks that print out instead of the new characters... Help!
EDIT: I'm using a key of 5 and the plaintext "Helloz!" to test. Expect to see Mjqqte!
instead am seeing blank spaces.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
int convert(string n);
string k, text;
char text;
int r, c, t,x;
bool validate(string n);
//int encrypted(string n);
int main(int argc, string argv[])
{
//accept single command-line argument, non negative integer, k with appropriate error
k = argv[1];
if (argc > 1 && argc <= 2)
{
//printf("Success\n%s\n", argv[1]);
// print individual characters of argv[i]
validate(k);
}
else //if wrong input then print error message and main should return 1
{
printf("Usage: ./caesar key\n");
return 1;
}
text = get_string("plaintext:");
t = atoi(k);
printf("%i\n", t);
convert (text);
printf("\n");
}
//output "ciphertext:" without a newline, with the characters roated by k positions
//after output, print a newline and exit by returning 0 from main
bool validate(string n)
{
for (int i = 0; k[i] != '\0'; i++)
{
if (48 <= k[i] && k[i] <= 57)
{
//printf("%c\n", k[i]);
}
else
{
printf("./caesar key\n");
return 1;
// save for later: printf("%s \n", k);
}
}
return r;
}
int convert(string n)
{
//if fits within a range, Reads individual characters
for (int i = 0; i < text[i]; i++)
{
if (isalpha(text[i]))
{
x = text[i];
//printf("%i\n", x);
c = (x+t) % 26;
// printf("%i\n",c);
printf("%c", c);
}
else
{
printf("%i", text[i]);
}
}
return 0;
}
Here's an implementation that could work for you:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void convert(char *text, unsigned char k) {
for (unsigned int i = 0; i < strlen(text); i++) {
if (isalpha(text[i])) {
// Contains the 3 leftmost bits, containing the uppercase/lowercase part.
char c = (text[i] / 32) * 32;
// Perform the shifting with modulo on the alphabetic index of the letter.
text[i] = c + ((text[i] % 32) + k) % 26;
}
}
}
int main(int argc, char *argv[]) {
unsigned char k = strtol(argv[1], NULL, 10);
char text[64];
printf("Using key %d.\n", (int) k);
printf("Plaintext: ");
fgets(text, 64, stdin);
// Remove newline.
text[strlen(text) - 1] = 0;
convert(text, k);
printf("Ciphertext: %s.\n", text);
return 0;
}
Test run:
>>> cipher 4
Using key 4.
Plaintext: Test mE Right Away!!1
Ciphertext: Xiwx qI Vmklx Eaec!!1.
I was doing the cs50 pset 2 - substitution, where we have to encrypt the plaintext using the key given by the user in the command line, but the following code isn't prompting for an input. What am I doing wrong? Any help would be greatly appreciated!
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int check(int c, string key);
int main(int argc, string argv[])
{
int checkR = check(argc, argv[1]);
if(checkR == 1)
{
return 1;
}
else
{
string key = argv[1];
string ptext = get_string("plaintext: ");
int len = strlen(ptext);
char ctext[len];
for (int i = 0; i < len; i++)
{
if(isupper(ptext[i]))
ctext[i] = toupper(key[(int)ptext[i] - 65]);
else if(islower(ptext[i]))
ctext[i] = tolower(key[(int)ptext[i] - 97]);
else
ctext[i] = ptext[i];
}
printf("ciphertext: %s\n", ctext);
return 0;
}
}
int check(int c, string key)
{
int keyL = strlen(key);
if(c != 2)
return 1;
else if(keyL != 26)
return 1;
for(int i = 0; i < keyL - 1; i++)
{
for(int j = i; j < keyL; j++)
{
if(key[i] == key[j])
return 1;
}
}
return 0;
}
Answer for the question
In the check function, you initialized the inner loop as int j = i.
Therefore, in the first iteration, key[i] == key[j] will be always true.
Then, 1 is returned from check and it prevents main function from printing the prompt.
The initialization should be int j = i + 1.
Other mistakes
Firstly, it is bad to do int keyL = strlen(key); before checking c because key (argv[1]) may be NULL when c (argc) is less than 2.
Secondly, printf("ciphertext: %s\n", ctext); in this code will invoke undefined behavior because what is stored in ctext is not NUL-terminated.
char ctext[len]; should be char ctext[len+1]; and ctext[len]='\0'; should be added before printf("ciphertext: %s\n", ctext);.
Vigenere cipher
Takes in a text and output a cipher version of that text
the user inputs a keyword in the command line and a text for which the user wishes to encrypt. If you're familiar with the Ceasar cipher it's pretty much the same thing except a small minor change, instead of inputting the actual shift value we instead input a keyword and the letters in the keyword would represent the shift value instead.
#include <cs50.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int shift(char c);
int main(int argc, string argv[])
{
//checks for alphabetic characters
if (argc == 2)
{
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (isalpha(argv[1][i]) == false)
{
printf("Usage: ./vigenere keyword. \n");
return 1;
}
}
//promting the user for text
string text = get_string("plaintext: ");
printf("ciphertext: ");
//Intializing variables
string key = argv[1];
int l = strlen(key);
for (int j = 0, k = 0, m = strlen(text); j < m; j++)
{
if (islower(text[j]))
{
//lower cap letter enter here
printf("%c", 'a' + ( text[j] - 'a' + shift(key[k % l]) ) % 26);
k++;
}
else if (isupper(text[j]))
{
//Upper caps letter enter here
printf("%c", 'A' + ( text[j] - 'A' + shift(key[k % l]) ) % 26);
k++;
}
else
{
//The rest whatever left enter here
printf("%c", text[j]);
}
}
printf("\n");
return 0;
}
else
{
printf("Usage: ./vigenere keyword. \n");
return 1;
}
}
//This function calculates the shift value per character
int shift(char c)
{
return ( islower(c) - 'a' );
}
the problem is that the output isn't what I expect it to be. For instance, if the keyword is 'b' and the text is 'hello' the output must be 'ifmmp' but it's not. I don't know what goes wrong.
we are now working with caesar, vigenere. I mannaged to finish caesar but vigenere is not working as good. C gives me back: invalid operands to binary expression ('int *' and 'int'). I'm not sure what the program means exactly and what is wrong with my code. Can somebody help me out or give me advice? I think it's because I can not count with the different types of numbers? I'm not sure!
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("You have to input a key, try again!\n");
return 1;
}
string key = argv[1];
int keylength = strlen(key);
for (int i = 0; i < keylength; i++)
{
if (!isalpha(argv[1][i]))
{
printf("Please insert letters, nothing else\n");
return 1;
}
}
printf("plaintext: ");
string plain = get_string();
int keycipher[keylength];
for(int i = 0; i < keylength; i++)
{
keycipher[i] = toupper(key[i]) - 65;
}
if (plain != 0)
{
printf("ciphertext: ");
int i;
for (i = 0, keylength = strlen(key); i < keylength; i++)
{
if (isupper(plain[i]))
{
printf("%c", (plain[i] - 65 + keycipher) % 26);
}
else if (islower(plain[i]))
{
printf("%c", (plain[i] - 97 + keycipher) % 26);
}
else if (plain[i] == ' ')
{
printf(" ");
}
else
{
printf("%c", plain[i]);
}
}
}
return 0;
}
As mentioned in the comments, you have to use + keycipher[index] instead of + keycipher. This is because keycipher is an array and you need to use only one of the elements of the array at a time.
Further, you will need 2 counter variables instead of just one in the part where you find the ciphertext. This is because you need to increment the plaintext index and the key index in every iteration, but once the plaintext length crosses the key length, you will need to reset the key index.
If you check the specifications, you will understand what I am saying about using 2 counters.
I'm working my way through an online class teaching me how to code. I'm very new to this and have been slowly making my way through this class. I've run into an issue with the vingenere cipher. It doesn't iterate the key through the whole input.
Edit: the key should iterate through the user input and when it reaches the end of the key, loop back and begin again. The key should also skip over any special character(!##" ",etc)
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, string argv[])
{
if(argc !=2)
{
printf("please put in command line argument: example - ./vigenere command\n");
return 1;
}
string key = argv[1];
int keylength = strlen(key);
for (int i=0;i<keylength; i++)
{
if(!isalpha(key[i]))
{
printf("please make sure command is letter only. Please no numbers or special characters!\n");
return 1;
}
}
string input = GetString();
for (int i=0, k=0; i<keylength; i++)
{
if(isalpha(input[i]))
{
if(isupper(input[i]))
{
input[i]=((input[i]-'A')+(key[k%keylength]))%26+'A';
}
else
{
if(islower(input[i]))
{
input[i]=((input[i]-'a')+(key[k%keylength]))%26+'a';
}
}
}
}
printf("%s\n",input);
return 0;
}
I know string is not normal, but it's included in the header to help with new students. I guess we learn more as the class progresses.
You didn't change k in your for loop. And indeed I don't think you need k at all. And your loop only iterate through the length of key instead of the length of input.
int inputlength = strlen(input);
for (int i = 0; i < inputlength; ++i) {
if (isupper(input[i]))
input[i] = ((input[i]-'A') + (key[i%keylength])) % 26 + 'A';
/* ... ^ Use i here */
}
Regarding the issue that when the key is b and input is A, you must adjust the key.
input[i] = ((input[i]-'A') + (key[i%keylength]-'a')) % 26 + 'A';
To skip input special characters,
int inputlength = strlen(input);
for (int i = 0, k = 0; i < inputlength; ++i) {
if (isupper(input[i]))
input[i] = ((input[i]-'A') + (key[(k++)%keylength])) % 26 + 'A';
/* ... ^^^ */
else if (islower(input[i]))
input[i] = ((input[i]-'a') + (key[(k++)%keylength])) % 26 + 'a';
}