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;
}
Related
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.
Code compiles ok. I tried to use a debugger but couldn't figure out the problem.
Each iteration "c" is being filled, but it seems like the way I try to assign this variable "c", into the array nothing happens, so that the end print statement ends up "ciphertext: EMPTY NOTHING"
Any help or ideas very welcomed. I'm taking courses and trying to solve a problem set. I'm only begginer, pls do not judje hard :)
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, string argv[])
{
if(argc < 2 || argc<2 )
{
printf("Usage: ./caesar key \n");
return 1;
}
else if(argc == 2)
{
for (int i = 0; i < strlen(argv[1]); i++)
{
if(!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key \n");
return 1;
}
}
}
int key = atoi(argv[1]);
string initial_text = get_string("plaintext: ");
int cipher[strlen(initial_text)];
int i = 0;
int n = strlen(initial_text);
for (i = 0; i < n; i++)
{
int c = 0;
if (isupper(initial_text[i]))
{
c = (((int)initial_text[i] - 65 + key) % 26) + 65;
cipher[i] += c;
}
else if (islower(initial_text[i]))
{
c = (((int)initial_text[i] - 97 + key) % 26) + 97;
cipher[i] += c;
}
else
{
c=initial_text[i];
cipher[i] += c;
}
}
//////////////////
printf("ciphertext: %c\n", (char)cipher);
}
There are a number of issues:
Using int for cipher when you [probably] want char.
Not defining enough space in cipher for the trailing 0x00 [EOS] string terminator
Not setting the EOS at the end.
Using %c instead of %s in your printf.
if ((argc < 2) || (argc < 2)) is wrong and can be replaced with if (argc != 2).
Because that if does return, the subsequent else if check is no longer needed.
"hardwiring" decimal values (e.g. 65 for 'A').
Using strlen in the condition expression in a for loop. This increases the running time of the loop from O(n) to O(n^2).
This can be replaced with (e.g.) comparing the array element against 0x00.
Using strtol instead of atoi and checking the end can do decode and validation in a single operation.
Don't use string when defining argv -- use char **argv.
Your main lacks a return 0; at the end.
There's a lot of replicated code that can be simplified.
Here's the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// I don't have cs50 handy, so a quick hack ...
#define string char *
string
get_string(const char *prompt)
{
char buf[1000];
char *cp;
printf("%s",prompt);
fflush(stdout);
do {
cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
// strip newline
buf[strcspn(buf,"\n")] = 0;
// IIRC, [real] get_string does heap allocation (vs. static buffer) but
// for demo purposes here, doesn't really matter
cp = strdup(buf);
} while (0);
return cp;
}
int
main(int argc, char **argv)
{
// check if there is exactly 2 arguments passing, otherwise prompt for a
// valid key(+return 1, to signal mistake)
if (argc != 2) {
printf("Usage: ./caesar key \n");
return 1;
}
// otherwise check if the key is only a digit and prompt for a valid
// key(+return 1, to signal mistake)
// loop through each character in the second argument and validate
char *arg = argv[1];
// convert a string to int variable to be used in transformation of letters
int key = strtol(arg,&arg,10);
if (*arg != 0) {
printf("Usage: ./caesar key \n");
return 1;
}
// prompt for plaintext
string initial_text = get_string("plaintext: ");
// create an array of int to store encrypted letters to then be converted
// in print by (char)
// NOTE: we need one extra to make room for the trailing EOS
char cipher[strlen(initial_text) + 1];
const char *src = initial_text;
char *dst = cipher;
for (int chr = *src++; chr != 0; chr = *src++, ++dst) {
if (isupper((unsigned char) chr)) {
*dst = (((chr - 'A') + key) % 26) + 'A';
continue;
}
if (islower((unsigned char) chr)) {
*dst = (((chr - 'a') + key) % 26) + 'a';
continue;
}
// for all none alphabetic symbols leave them as they are
*dst = chr;
}
// set the trailing EOS
*dst = 0;
// ////////////////
printf("ciphertext: %s\n", cipher);
return 0;
}
You could write for example
if ( argc < 2 || argc<2 || argc < 2 )
instead of
if ( argc < 2 || argc<2 )
and this will be correct but does not make a sense.
Just write
if ( argc != 2 )
{
puts( "Usage: ./caesar key" );
return 1;
}
This else statement
else if(argc == 2)
is redundant. Remove it.
Instead of this for loop
for (int i = 0; i < strlen(argv[1]); i++)
{
if(!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key \n");
return 1;
}
}
it is simpler to write
char *p;
unsigned long ley = strtoul( argv[1], &p, 10 );
if ( *p || key == ULLONG_MAX )
{
puts( "Usage: ./caesar key" );
return 1;
}
There is no need to define a variable length array. You could change the original string pointed to by the pointer initial_text.
Introducing the variable n is redundant
int n = strlen(initial_text);
Instead of the loop
for (i = 0; i < n; i++)
you could use this loop
for ( size_t i = 0; initial_text[i] != '\0'; i++ )
Instead of these if statements
if (isupper(initial_text[i]))
and
else if (islower(initial_text[i]))
it will be more safer to write
if ( isupper( ( unsigned char )initial_text[i]))
and
else if ( islower( ( unsigned char )initial_text[i] ) )
Also using magic numbers like 65 makes the code unreadable. For example you could
write
c = ( initial_text[i] - 'A' + key ) % ( 'Z' - 'A' + 1 ) + 'A';
and
c = ( initial_text[i] - 'a' + key ) % ( 'z' - 'a' + 1) + 'a';
and instead of the compound assignment operator
cipher[i] += c;
you need to write
initial_text[i] = c;
Also in the call of printf you are using incorrect conversion specifier %c and casting
printf( "ciphertext: %c\n", (char)cipher);
As the string should be updated in place then the call of printf will look like
printf( "ciphertext: %s\n", initial_text );
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.
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