Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
My solution to CS50 pset2 - "Hail, Caesar!"
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
/**
* Caesar.c
* A program that encrypts messages using Caesar’s cipher. Your program must
* accept a single command-line argument: a non-negative integer. Let’s call it
* k for the sake of discussion. If your program is executed without any
* command-line arguments or with more than one command-line argument, your
* program should yell at the user and return a value of 1.
*
* */
int main(int argc, string argv[])
{
// check for 2 arguments only
if (argc != 2)
{
printf("Nope\n");
return 1;
}
// once I check for correct argv put key into an int k
int k = atoi(argv[1]);
// check if the integer is non-negative
if (k < 0)
{
printf("Nope\n");
return 1;
}
else
{
// prompt user for a code to encrypt
string code = GetString();
for (int i = 0, n = strlen(code); i < n; i++)
{
//check if the letter is uppercase or lowercase then convert
if islower(code[i])
printf("%c", (((code[i] + k) - 97) % 26) + 97);
else if isupper(code[i])
printf("%c", (((code[i] + k) - 65) % 26) + 65);
//if neither then just print whatever it is
else
printf("%c", code[i]);
}
printf("\n");
return 0;
}
}
@CraigRodrigues

This comment has been minimized.

Copy link
Owner Author

commented May 31, 2016

Steps:

  1. Get a single command-line argument "key" from the user that is a non-negative integer.
  2. If not a single argument then yell at user.
  3. Take the "key" and turn it into an int with atoi since it starts as a string.
  4. Prompt user for a code they want to encrypt.
  5. Need to loop through the entire code letter by letter.
  6. Check if each letter is either lowercase, uppercase or neither.
  7. Standardize the ASCII value of the char to 26 then add the key. Then convert back into ASCII so that the code can wrap around properly.
  8. If neither a lowercase or uppercase letter then just print whatever the char is. This allows for spaces or special characters like ! or &.
  9. Once all of the above is complete print a new line.
  10. Return 0.

Notes:

  • Checking if the argv[1] was a non-negative integer (don't think I even needed to do this).
  • Figuring out how to standardize ASCII to the regular alphabet then converting back took a lot of time.
  • This ASCII chart was incredibly useful - http://www.kerryr.net/pioneers/ascii3.htm
  • Trying to put argv[1] into a variable before I check if there is even an argv[1] caused a segmentation fault.
  • Didn't notice that toupper/tolower already checks if the character is a letter. First I had two more checks to see if the character was a letter or not when that wasn't necessary.
  • ctype.h library is very useful - https://cs50.harvard.edu/resources/cppreference.com/stdstring/all.html
@drum35

This comment has been minimized.

Copy link

commented Jul 7, 2017

I love the elegance of your math!!
I'm going through the class too and went about it the idiot way:

    printf("plaintext:  ");
    string message = get_string();
    
    printf("ciphertext: ");
    for (int i = 0, n = strlen(message); i < n; i++)
    {
        int ci = message[i];

        int wrapcheck = ci + key;
      
        if (ci > 64 && ci < 91 && wrapcheck < 91) 
        {
            ci = wrapcheck;
            printf("%c", ci);
        }
        else if (ci > 64 && ci < 91 && wrapcheck > 90) 
        {
            int dif = key % 26;
            ci = ci + dif;
            if (ci > 122)
            {
                ci = ci - 26;
                printf("%c", ci);
            }
            else printf("%c", ci);
        }
        else if (ci > 96 && ci < 123 && wrapcheck < 123) 
        {
            ci = wrapcheck;
            printf("%c", ci);
        }
        else if (ci > 96 && ci < 123 && wrapcheck > 122) 
        {
            int dif = key % 26;
            ci = ci + dif;
             if (ci > 122)
            {
                ci = ci - 26;
                printf("%c", ci);
            }
            else printf("%c", ci);
        }

        else
        {
            printf("%c", ci);
        }
    }
    
}
else
{
    printf("ERROR: one command line argument only please!\n");
    return 1;
}
printf("\n");
@teebl

This comment has been minimized.

Copy link

commented Jul 20, 2017

Thanks for the code! Helped me understand the numbers game with the uppercase and lowercase characters!

@meetyourhomie

This comment has been minimized.

Copy link

commented Aug 3, 2017

Hey why haven't you performed this step?

string k = argv[1];

@valkukatov

This comment has been minimized.

Copy link

commented Sep 21, 2017

@meetyourhomie argv[] takes input as a string even if an integer was provided, so he did an int k = argv[1] instead, since that's the number that ascii alphabet will be incremented by.

@thegnord

This comment has been minimized.

Copy link

commented Nov 24, 2017

I love it!

(((code[i] + k) - 97) % 26) + 97);
...or 65 for uppercase...

^^^ This is very elegant and nice. At first I didn't understand how this could work for very large keys (1,000, 10,000, etc.), but after reading this post...
https://stackoverflow.com/questions/2664301/how-does-modulus-divison-work

...I think I truly understand how modulo can be such an easy, elegant solution to problems like this. If someone reading this still doesn't understand, think of it this way:

  1. code[i] + k = add the key, even an extremely large one
  2. -97 (or 65 for uppercase) = subtract the "base." This brings the modified value down to a "base" of 0, effectively removing any part of the ASCII system that doesn't apply. It makes the letter "a" or "A" start at 1.
  3. Now mod by 26. The math here automatically wraps around as many times as required, only returning the "remainder," or offset from the base (which is now 0 thanks to step #2)
  4. +97 (or 65 for uppercase) = add the "base" back in so that the value can be output correctly in ASCII.

Thanks for the post, I was struggling with this problem but hopefully I learned well.

@snekops

This comment has been minimized.

Copy link

commented Nov 30, 2017

@thegnord thanks for your explanation! I also was struggling with this seemingly basic operation. Remove base, do stuff, add base back makes perfect sense but I completely blanked on that.

<3

@Rubertavio

This comment has been minimized.

Copy link

commented Mar 26, 2018

Hello! I already made this code on base of yours but I still can´t find the output called ciphertext. Are your code incomplete?
Thanks!

@Vickoboy

This comment has been minimized.

Copy link

commented May 8, 2018

let someone help me at this:
Connecting.......
Authenticating......
Preparing...............
Uploading.............
Checking.......
:) caesar.c exists.
:( caesar.c compiles.
expected exit code 0, not 1
:| encrypts "a" as "b" using 1 as key
can't check until a frown turns upside down
:| encrypts "barfoo" as "yxocll" using 23 as key
can't check until a frown turns upside down
:| encrypts "BARFOO" as "EDUIRR" using 3 as key
can't check until a frown turns upside down
:| encrypts "BaRFoo" as "FeVJss" using 4 as key
can't check until a frown turns upside down
:| encrypts "barfoo" as "onesbb" using 65 as key
can't check until a frown turns upside down
:| encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
can't check until a frown turns upside down
:| handles lack of argv[1]
can't check until a frown turns upside down

@Bshivanshu

This comment has been minimized.

Copy link

commented May 12, 2018

WHO came up with this ingenious formula??
Please tell.
I am gone MAD thinking how one could come up with this ingenious formula!
I kinda feel disappointed of myself! Should I take CS?

@1Romario

This comment has been minimized.

Copy link

commented Jun 16, 2018

Hello. Your code is not protected from entering the codeword as a word (there must be a number)

@cat-lee

This comment has been minimized.

Copy link

commented Jun 24, 2018

i had the same concept, thankyou very much

@cat-lee

This comment has been minimized.

Copy link

commented Jun 24, 2018

#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>

int main(int argc, string argv[])
{
// Check if correct # of arguments given
if (argc != 2)
{
printf ("Wrong number of arguments. Please try again.\n");

    return 1;
}

// Convert input to int type
int k = atoi(argv[1]);

// Get text to encode
string p = get_string();

// Loop through text
for (int i = 0, n = strlen(p); i < n; i++)
{
    // Keep case of letter
    if (isupper(p[i]))
    {
        // Get modulo number and add to appropriate case
        printf("%c", 65 + (p[i] - 65 + k) % 26);
    }
    else if (islower(p[i]))
    {
        printf("%c", 97 + (p[i] - 97 + k) % 26);
    }
    else
    {
        // return unchanged
        printf("%c", p[i]);
    }
}

printf("\n");

return 0;

}

@simbazone

This comment has been minimized.

Copy link

commented Dec 29, 2018

Thanks so much for the explanation. I don't understand why we have to start at 97 though, based on this ASCII chart: http://www.asciichart.com/
If the goal is to return to the base like @thegnord said, then shouldn't it return to NUL instead of a?
Forgive my ignorance but I am totally lost at this.

@dsimanoliveira

This comment has been minimized.

Copy link

commented Jan 23, 2019

Thank you so much. This helped me to understand some basic stuff I was stuck.
I just have one doubt...you didn't "block" the user to type letters, did you? So if they enter with that the program would run normally... Or am I missing something?

@Gagan1498

This comment has been minimized.

Copy link

commented Feb 9, 2019

@thegnord Can someone tell me what's the use of subtracting 97 at first and then adding again because the work is same, the reminder will be same also.

@nateharper1

This comment has been minimized.

Copy link

commented Feb 20, 2019

Wouldn't an input such as 10x or 1x840v technically still work though? So the code has a bug.

@cmkoopman

This comment has been minimized.

Copy link

commented Mar 17, 2019

Thank you very much for posting this.
It really helped.

@katsuya245126

This comment has been minimized.

Copy link

commented Apr 1, 2019

Wow very elegant solution. I made mine more complicated than it needed to be


#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

bool isDigit(string s);

int main(int argc, string argv[])
{
    //checks if the user used the right syntax and if the user input a digit
    if(argc == 2 && isDigit(argv[1])) {
        //convert user input string into int
        int key = atoi(argv[1]);
        
        //store user input into plaintext and make a new char array (string) with the same length as plaintext
        string plainText = get_string("plaintext: ");
        char cipherText[strlen(plainText)];
        strcpy(cipherText, plainText);
        
        //making cipher text from plain text here
        for(int i = 0; i < strlen(cipherText); i++) {
            //making sure to only change the alphabets, not the special characters
            if((cipherText[i] >= 'a' && cipherText[i] <= 'z') || (cipherText[i] >= 'A' && cipherText[i] <= 'Z')){ 
                if ((cipherText[i] >= 'a' && cipherText[i] <= 'z')) {
                    cipherText[i] = (cipherText[i] + key) % 122;
                    if(cipherText[i] < 97) {
                        cipherText[i] = (cipherText[i] % 26) + 96;
                    }
                } else if ((cipherText[i] >= 'A' && cipherText[i] <= 'Z')) {
                    cipherText[i] = (cipherText[i] + key) % 90;
                    if(cipherText[i] < 65) {
                        cipherText[i] = (cipherText[i] % 26) + 64;
                    }
                }
            }
        }

        printf("ciphertext: %s\n", cipherText); 
        return 0;
    } else {
        printf("Usage: ./caesar key\n");
        return 1;
    }
 
}

//this function checks if the string is a decimal digit
bool isDigit(string s) {
    for(int i = 0; i < strlen(s); i++) {
        //checks if the character is between 0-9 and returns false if it's not
        if(!(s[i] >= '0' && s[i] <= '9')) {
            return false;
        }
    }
    
    return true;
}

@goalmada

This comment has been minimized.

Copy link

commented Apr 3, 2019

Hi everyone,

How do we check for:
$ ./caesar 20x
Usage: ./caesar key
?

@AbeerHaroon

This comment has been minimized.

Copy link

commented Apr 13, 2019

Wow, so smart!
Thank you very much, I've been stuck in this problem for so long

@p69d

This comment has been minimized.

Copy link

commented Apr 18, 2019

@goalmada

#include <ctype.h>

string digits = argv[1];
int i = 0;
while (digits[i] != '\0')
{
    // Validating the Key is number.
    if (!isdigit(digits[i]))
    {
        printf("Usage: ./caesar key\n");
        
        return 1;
    }
    i++;
}
@Naiftt

This comment has been minimized.

Copy link

commented May 30, 2019

I have solved it with basically the same style you made, but your code won't work if you enter a letter because it will read it as 0, and the program won't end. and it will approach it as if the key entered was 0

@yuriechan

This comment has been minimized.

Copy link

commented Jul 23, 2019

While code[i] is a data type of string, why is this being used with other numerical values as follows?
(((code[i] + k) - 97) % 26) + 97)

Is code[i] automatically converted into an integer?

@yuriechan

This comment has been minimized.

Copy link

commented Jul 23, 2019

Also, If we want to get alphabetical index, can we just substitute the base, and finish?

Why do we have to substitute the base AND module by 26 to get the alphabetical index?

For example, when the key is A = 65.
Substitute by the base of the upper case, which is 65 minus 65 equals 0.
Why do we have to do another %26, while this will only give the same number which is 0?

@gjgcorg1958

This comment has been minimized.

Copy link

commented Aug 1, 2019

While code[i] is a data type of string, why is this being used with other numerical values as follows?
(((code[i] + k) - 97) % 26) + 97)

Is code[i] automatically converted into an integer?

According to the lesson, a string is a collection of char. code[i] is a char. When char is used in a arithmetic operations, it is converted to int.

@gjgcorg1958

This comment has been minimized.

Copy link

commented Aug 1, 2019

Also, If we want to get alphabetical index, can we just substitute the base, and finish?

Why do we have to substitute the base AND module by 26 to get the alphabetical index?

For example, when the key is A = 65.
Substitute by the base of the upper case, which is 65 minus 65 equals 0.
Why do we have to do another %26, while this will only give the same number which is 0?

The modulo is part of the formula so you can loop back. the range of a-z is 97-122. if you increment by 1 and enter z the result will be 123 which is "[".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.