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.
Related
I am trying to print the output of the function "char rotate(char c, int n)" but it will print only the numbers and not the characters. any help is appreciate it.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
bool only_digits(string arg);
char rotate(char c, int n);
int main(int argc, string argv[])
{
// Make sure program was run with just one command-line argument
if (argc !=2)
{
printf("Usage: ./caesar key\n");
return 1;
};
// Make sure every character in argv[1] is a digit
if ( only_digits(argv[1])==0)
{
printf("Usage: ./caesar key\n");
return 1;
};
// Convert argv[1] from a `string` to an `int`
int key = atoi(argv[1]);
// Prompt user for plaintext//
string text = get_string("plaintext: \n");
// For each character in the plaintext:
printf("ciphertext: ");
for (int i = 0; i < strlen(text); i++)
{
char ch = (rotate(text[i], key));
printf("%c", ch);
};
}
bool only_digits(string arg)
{
// Rotate the character if it's a letter
for (int i = 0; i < strlen(arg); i++)
{
if (!isdigit(arg[i]))
{
return false;
}
}
return true;
}
char rotate(char c, int n){
if (isupper(c))
{
c -=65;
c = (c + n) % 26;
c = c + '0';
return c;
}
else if (islower(c))
{
c -=97;
c = (c + n) % 26;
c = c + '0';
return c;
}
else
return c;
}
I tried different ways of casting the integers into characters and experimenting with a debugger and using printf statement to figure out what's causing this to no avail.
Edit1:
in my original post I deleted bunch of my code thinking it's not relevant to my issue but some of the questions asked about those parts, so hopefully now it's more clear.
when I type abc it print no thing at all, just "plaintext: "
OP eventually determined a major issue.
Code was substring a '0' when an 'A' or 'a' should have been subtracted.
To handle other issues:
is...(ch) has trouble when ch < 0
Best to use unsigned char.
Avoid magic numbers like 65
Avoid negatives and overflow errors
c + n may overflow
The sum may be negative. So let us work with certain positive numbers.
Unneeded else
// In main(), bring `key` into the range [0...25];
int key = atoi(argv[1]);
key &= 26; // key now in the [-25 ... 25] range
if (key < 0) key += 26;
// n is in the 0...25 range.
static char rotate(char c, int n) {
unsigned char uch = (unsigned char) c;
if (isupper(uch)) {
uch = (uch - 'A' + n) % 26;
return uch + 'a';
}
if (islower(uch)) {
uch = (uch - 'a' + n) % 26;
return uch + 'a';
}
return c;
}
I'm working on project work and when I try checking if I've got everything in the code as expected, I see this error,
handles non-numeric key
timed out while waiting for the program to exit.
The code decrypts words or letters being passed into the input with a key. (I just thought I should let you know about that)
here is my actual code. everything seems right except that error code I keep getting each time I check to see all went well.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
//declaration of function prototype
bool only_digits(string s);
char rotate(char c, int n);
int main(int argc, string argv[])
{
// string s = argv[1];
//command line argument
if(argc != 2 || !only_digits(argv[1]))
{
printf("Usage: ./caesar key\n");
return 1;
}
//convert argv[1] to an int
int key = atoi(argv[1]);
//prompt user for plaintext
string text = get_string("plaintext: ");
//output of plaintext
printf("ciphertext: ");
for(int i = 0; text[i]; i++)
{
text[i] = rotate(text[i], key);
printf("%c", text[i]);
}
printf("\n ");
return 0;
}
bool only_digits(string s)
{
for(int i = 0; i < strlen(s); i++)
{
//check whether the character inputed is a digit 0 - 9
if(isdigit(s[i]))
{
return true;
}
else{
return false;
}
}
return false;
}
char rotate(char c, int n)
{
char cipher_text = c;
if(islower(c))
{
cipher_text = 'a' + ((c - 'a') + n) % 26;
return cipher_text;
}
else if(isupper(c))
{
cipher_text = 'A' + ((c - 'A') + n) % 26;
return cipher_text;
}
return cipher_text;
}
any help will be much appreciated.
Here is a working self-contained solution. It hard-codes the clear text:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool only_digits(const char *s);
char rotate(char c, int n);
int main(int argc, char *argv[]) {
if(argc != 2 || !only_digits(argv[1])) {
printf("Usage: ./caesar key\n");
return 1;
}
int key = atoi(argv[1]);
char *cleartext = "test";
printf("ciphertext: ");
for(; *cleartext; cleartext++) {
printf("%c", rotate(*cleartext, key));
}
printf("\n");
return 0;
}
bool only_digits(const char *s) {
for(; *s && isdigit(*s); s++);
return !*s;
}
char rotate(char c, int n) {
#define ROTATE2(a, c, n) (a) + (((c) - (a)) + (n)) % 26
if(islower(c)) {
return ROTATE2('a', c, n);
}
if(isupper(c)) {
return ROTATE2('A', c, n);
}
return c;
}
It fixes the only_digits() to look at all characters unless s contains a non-digit in which case it would fail early. Simplified the rotate a bit using a macro to avoid the duplicate code. You could write a 2nd function instead of the macro if you so choose.
I know that this question have been asked before, but I still can't find the clue to the problem in my code.
My program works apparently fine, but I'm not able to pass the check50 test. From what I understand, the issue may be related to the fact that the null \0 is printed. But I don't know how to modify that. Could you please help me?
This is my code:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
bool only_digits(string s);
char rotate(char c, int n);
int main(int argc, string argv[])
{
string h = argv[1];
if (argc != 2 || !only_digits(h) || h <= 0)
{printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext = get_string("plaintext: ");
int f = strlen(plaintext);
printf("ciphertext: ");
for(int q = 0; q < f; q++)
{
printf("%c", rotate(plaintext[q], key));
}
printf("\n");
}
return 0;
}
bool only_digits(string s )
{
for (int i = 0, n = strlen(s); i < n; i++)
{
char digit = s[i];
if (!isdigit(digit))
return false;
}
return true;
}
char rotate(char c, int n)
{
if(isupper(c) && (c != '\0'))
{
printf("%c", (((c - 65) + n) % 26) + 65);
}
else
if(islower(c) && (c != '\0'))
{
printf("%c", (((c - 97) + n) % 26) + 97);
}
else
printf("%c", c);
return 0;
}
This is a caption of check50's check:
check50
rotate always returns 0, so printf("%c", rotate(plaintext[q], key)); is causing the letters you output to be interspaced with NUL characters.
I would keep that printf, but change rotate to return the character instead of printing it.
I have almost finished my code, but ran into the following problem. The code, luckily, works, but when I write as input ZQ, with a key of 1, it gives me the next character of the ASCII chart. Is there a way to stop it from overflowing? If it reaches Z, I'd like to go back to the first index character, which would be the A. Additionally, it seems to work better when I get rid of the % 26 in line 39, even though I need to use the formula ci = (pi + k) % 26. It gives me no characters as an output otherwise. Thanks!
If a character is uppercase, it should remain uppercase, vice versa with lowercase.
#include <stdio.h>
#include <cs50.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
int k = 0;
string key = argv[1];
if (argc == 2)
{
for (k = 0; k < strlen(key); k++)
{
if (!isdigit(key[k]))
{
printf("\nUsage: %s key\n", argv[0]);
return 1;
}
}
}
else
{
printf("\nUsage: %s key\n", argv[0]);
return 1;
}
string plaintext = get_string("\nplaintext: ");
printf("ciphertext: ");
for (int i = 0; i < strlen(plaintext); i++)
{
char c = plaintext[i];
int keycode = atoi(key);
if (isalpha(c))
{
printf("%c", (c + keycode) % 26);
}
else if (isspace(c) || isdigit(c) || ispunct(c))
{
printf("%c", c);
}
}
}
The number values for letters are offset from the values for the Cæsar cipher, depending on what case it is. You must transform them into numbers in the range of (0, 25), do the operation, and inverse transform the result back into the case selected. These transformations are fairly simple, eg for lower-case letters, F(c) = c - 'a'.
The formula is correct to handle wrapping from Z to A, but you must first convert letters to their index value in the alphabet. For the ASCII character set, this conversion is a simple subtraction but must be performed separately for uppercase (c = 'A') and lowercase letters (c - 'a').
Here is a modified version:
#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
int k = 0;
string key = argv[1];
if (argc == 2)
{
for (k = 0; key[k] != '\0'; k++)
{
if (!isdigit((unsigned char)key[k]))
{
printf("\nUsage: %s key\n", argv[0]);
return 1;
}
}
}
else
{
printf("\nUsage: %s key\n", argv[0]);
return 1;
}
string plaintext = get_string("\nplaintext: ");
int keycode = atoi(key);
printf("ciphertext: ");
for (int i = 0; plaintext[i] != '\0'; i++)
{
char c = plaintext[i];
if (isupper((unsigned char)c))
{
printf("%c", 'A' + (c - 'A' + keycode) % 26);
}
else if (islower((unsigned char)c))
{
printf("%c", 'a' + (c - 'a' + keycode) % 26);
}
else // leave other characters unchanged
{
printf("%c", c);
}
}
return 0;
}
I've been working on cs50 pset2, and I thought I had the vigenere cipher down after working on it for a few days. This code is meant to take an alphabetical argument(argv[]) given by the user, and use that as a key to crypt a phrase given by the user(string) by its number in the alphabetical index. For example, if you give the argument 'abc' and the string 'cat' then the output should be 'cbv'(a moving 0, b moving 1, c moving 2) The argument should also wrap around so that if the string is longer, the argument will wrap to its first character and continue until the string has ended.
This is what I have for code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if(argc != 2)
{
printf("Try again\n");
return 1;
}
string k = (argv[1]);
int klen = strlen(k);
for(int x = 0; x < klen; x++)
{
if(isalpha(k[x]))
{
if(isupper(k[x]))
{
k[x] = tolower(k[x]);
}
k[x] -= 'a';
}
else
{
printf("Try again\n");
return 1;
}
}
string code = GetString();
int clen = strlen(code);
for(int a = 0, b = 0; a < clen; a++)
{
if(isalpha(code[a]))
{
int key = k[b%klen];
if(isupper(code[a]))
{
printf("%c", (((code[a] - 'A') + key)%26) + 'A');
b++;
}
else
{
printf("%c", (((code[a] - 'a') + key)%26) + 'a');
b++;
}
}
else
{
printf("%c", code[a]);
}
}
printf("\n");
}
The code seems to work for the length of the key +1.
For example,
I input an argument of 'aaaa'
Then input a string of 'bbbbb'
and receive 'bbbbb' correctly.
However, if I input the same 'aaaa'
Then input a string longer than the key +1 'bbbbbbb'
I receive 'bbbbbNN'
I believe I have an issue with my order of operations but have tried moving parenthesis around to no avail. I was hoping someone could point me in the right direction as to why my key isn't wrapping properly.
Your biggest risk with code like this is all the similar, repetitive clauses. A bug in just one is hard to track done. And doing any processing on the key, while processing the code, is just inefficient.
Here's a rework that completely processes the key before processing the code and tries to get the processing down to just one case. See if it works any better for you:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
fprintf(stderr, "Try again\n");
return EXIT_FAILURE;
}
string key = strdup(argv[1]);
size_t key_length = strlen(key);
for (int x = 0; x < key_length; x++)
{
if (isalpha(key[x]))
{
if (isupper(key[x]))
{
key[x] = tolower(key[x]);
}
key[x] -= 'a';
}
else
{
fprintf(stderr, "Try again\n");
return EXIT_FAILURE;
}
}
string code = GetString();
int code_length = strlen(code);
for (int a = 0, b = 0; a < code_length; a++)
{
if (isalpha(code[a]))
{
int start = isupper(code[a]) ? 'A' : 'a';
printf("%c", (((code[a] - start) + key[b++ % key_length]) % 26) + start);
}
else
{
printf("%c", code[a]);
}
}
printf("\n");
free(key);
return EXIT_SUCCESS;
}