This code is supposed to encipher text based off the command argument input key and print out the enciphered text. However it doesn't print spaces and punctuation. Can someone explain what is wrong?
Example use:
$ ./caesar 12
world, say hello!
iadxp, emk tqxxa!
$
Code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc>2 || argc<2)
{
printf("Please enter a valid argument.\n");
return 1;
}
string input = GetString();
int key = atoi(argv[1]);
for(int i = 0, l = strlen(input); i < l; i++)
{
//if(isalpha(input[i]))
//{
char c = input[i];
int letternum = c;
if(isupper(c))
{
int upper = 'A';
int alphanum = letternum - upper;
int newint = (alphanum + key) % 26;
newint = newint + upper;
char newchar = newint;
printf("%c", newchar);
}
if(islower(c))
{
int lower = 'a';
int alphanum = letternum - lower;
int newint = (alphanum + key) % 26;
newint = newint + lower;
char newchar = newint;
printf("%c", newchar);
}
//}
}
printf("\n");
}
Add else after if() blocks.
Change from
if(isupper(c)) {
...
}
if(islower(c)) {
...
}
to
if(isupper(c)) {
...
} else if(islower(c)) {
...
} else {
putc(c);
}
Note: pedantic code would use the following as is...() functions are defined for all int values in the range of unsigned char and EOF.
isupper((unsigned char) c)
islower((unsigned char) c)
Note2: Code will have problems if alphanum + key < 0. May want to add a test to insure key is not too negative or use the following to insure key >= 0.
int key = atoi(argv[1]) % 26 + 26;
Note3: OP's code assumes A-Z and a-z are consecutive like in ASCII encoding (which is certainly the case 99.99+% of the time.) but not in EBCDIC
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 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'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.
C does some spooky things when I put the nonAlphaCount declaration above the for loop. I can't explain why the outputs are different.
For version 1 (int declaration above main method) my input output was:
INPUT: ./Vigenere.exe bacon
Enter plain text: Meet me at the park at eleven am
OUTPUT: Negh zf av huf pcfx bt gzrwep oz
For version 2 ( int declaration above for loop )
INPUT: ./Vigenere.exe bacon
Enter plain text: Meet me at the park at eleven am
OUTPUT: NRQQ M[L \M^^ KQXXZQZ M
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
const int INPUT_LEN = 255;
const int ALPHABET_LEN = 26;
int nonAlphaCount = 0;
int main (int count, char *args[])
{
char plainText[INPUT_LEN];
char *cipherText;
char *keyWord;
if ( count < 2 || count > 2)
{
printf("There is no key");
return 1;
}
strcpy(keyWord, args[1]);
int keyWord_LEN = strlen(keyWord);
printf("Enter plain text: ");
fgets (plainText, INPUT_LEN, stdin);
int strLength = strlen(plainText);
cipherText = malloc(strLength);
printf("%s", plainText);
for (int i = 0; i < strLength; i++ ){
if(plainText[i] == '\0' || plainText[i] == '\n'|| plainText[i] == '\r')
break;
if(isalpha(plainText[i]))
{
// Default lower
int asciiUpperOrLower = 97;
int keyUpperOrLower = 97;
if(isupper(plainText[i]))
asciiUpperOrLower = 65;
if(isupper(keyWord[i % keyWord_LEN]))
keyUpperOrLower = 65;
int Key = keyWord[(i - nonAlphaCount) % keyWord_LEN] - keyUpperOrLower;
int alphabetBaseletter = ((plainText[i] - asciiUpperOrLower + Key) % ALPHABET_LEN);
cipherText[i] = alphabetBaseletter + asciiUpperOrLower;
}
else{
cipherText[i] = plainText[i];
nonAlphaCount++;
}
}
// Set string terminator.
cipherText[strLength - 1] = '\0' ;
printf("%s", cipherText);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
const int INPUT_LEN = 255;
const int ALPHABET_LEN = 26;
int main (int count, char *args[])
{
char plainText[INPUT_LEN];
char *cipherText;
char *keyWord;
if ( count < 2 || count > 2)
{
printf("There is no key");
return 1;
}
strcpy(keyWord, args[1]);
int keyWord_LEN = strlen(keyWord);
printf("Enter plain text: ");
fgets (plainText, INPUT_LEN, stdin);
int strLength = strlen(plainText);
cipherText = malloc(strLength);
printf("%s", plainText);
**int nonAlphaCount = 0;**
for (int i = 0; i < strLength; i++ ){
if(plainText[i] == '\0' || plainText[i] == '\n'|| plainText[i] == '\r')
break;
if(isalpha(plainText[i]))
{
// Default lower
int asciiUpperOrLower = 97;
int keyUpperOrLower = 97;
if(isupper(plainText[i]))
asciiUpperOrLower = 65;
if(isupper(keyWord[i % keyWord_LEN]))
keyUpperOrLower = 65;
int Key = keyWord[(i - nonAlphaCount) % keyWord_LEN] - keyUpperOrLower;
int alphabetBaseletter = ((plainText[i] - asciiUpperOrLower + Key) % ALPHABET_LEN);
cipherText[i] = alphabetBaseletter + asciiUpperOrLower;
}
else{
cipherText[i] = plainText[i];
nonAlphaCount++;
}
}
// Set string terminator.
cipherText[strLength - 1] = '\0' ;
printf("%s", cipherText);
return 0;
}
Both programs exhibit undefined behaviour in
char *keyWord;
...
strcpy(keyWord, args[1]);
which is revealed by the compiler warning: "uninitialized local variable 'keyWord' used". You have not allocated any memory.
If one of the programs happened to work, so be it.
The cipher code actually works; it's just that I get some odd three digit codes separated with slashes too.
Any help would be greatly appreciated. Here's my code.
The codes look like this but have random numbers /354/233/645/810/236
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cs50.h"
int i, len;
string sentance, encrypted;
int k, argvLen;
int caesar (int k){
printf("Hi I'm Ceaser! What would you like me to cipher?\n");
sentance = GetString();
len = strlen(sentance);
char encrypted[len];
for (i=0; i<len; i++) {
if (sentance[i] >='a' && sentance[i] <='z') {
encrypted[i] = ((sentance[i] - 'a' + k) % 26) + 'a';
}
else if (sentance[i] >='A' && sentance[i] <='Z') {
encrypted[i] = ((sentance[i] - 'A' + k) % 26) + 'A';
}
else if (sentance[i] >=' ' && sentance[i] <= '#'){
encrypted[i] = sentance[i];
}
}
printf("%s", encrypted);
return 0;
};
int main (int argc, const char * argv[]) {
if (argc==2) {
k = atoi(argv[1]);
argvLen = strlen(argv[1]);
for (i=0; i<argvLen; i++){
if (isdigit(argv[1][i])){
caesar(k);
}
else {
printf("please enter a number for the key!");
return 1;
}
}
return 0;
}
};
You are not terminating the encrypted string properly.
You need:
To make sure you have room for the terminating character, by using char encrypted[len + 1];.
encrypted[len] = '\0'; after the loop.