We continue to do homework in the Computer science CS50 (Harvard) course, and this time we need to write the program code and encrypt a text message.
The essence of the ceasar.c task is that the program asks for the user’s text input, and then displays a line of text below in encrypted form with the user’s key. The key is specified when starting the program on the command line after naming the program itself argv[]. So in the example of running the finished program, the user enters the key = 13 and his text “Hello, world!”, and at the output he receives this text in the form of a cipher:
It is important that the program meets the following conditions:
- if the user enters capital letters, then capital letters also appear in the cipher, if lowercase, then lowercase;
- if the user enters something other than letters, then these characters are printed in the output form;
- the encryption key that the user passes to the program when it is launched via the command line can be a positive number, this number does not necessarily correspond to the number of letters in the alphabet, but can be more, for example, 100 – in this case, the original letter is not replaced by the +100 symbol ASCII, but a letter between a and z or A and Z that is 100 steps ahead of the original;
- formula for calculating the letter shift index in encryption:
Ci=(Pi+key)%26,
where Ci is the encrypted letter, Pi is the original letter, key is the number of the encryption key.
Igroglaz’s solution #1. The solution turned out to be somewhat redundant in terms of additional character encoding; on the other hand, it may come in handy in the next task.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int only_digits(char *argv[]);
int main(int argc, char *argv[])
{
int i, n, j, x;
int key; // cipher key
char text[1000]; // text input
char cypher[1000]; // cypher output
char AZ[26], az[26]; // alphabetical indexes
// in case if user didn't input any console argument
if (!(argc == 2))
{
printf("Usage: ./caesar key");
return 1;
}
// check console argument (char) and convert them to (int)
key = only_digits(argv);
if (key < 0)
{
printf("Usage: ./caesar key");
return 2;
}
printf("plaintext: ");
for (n = 0; ; n++) // we also count number of input for the next cycle
{
scanf("%c", &text[n]); // input
if (text[n] == '\n') // if input ended - put new line
break;
}
// build A-Z and a-z indexes
for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25
AZ[i] = j++;
for (i = 0, j = 97; i < 26; i++)
az[i] = j++;
printf("ciphertext: ");
for (i = 0; i < n; i++)
{
if (text[i] >= 'A' && text[i] <= 'Z') // print capital
{
text[i] -= 65; // to move ASCII to 0
text[i] = (text[i] + key)%26; // key formula to move past 'z'
x = (int)text[i]; // convert char to int
printf("%c", AZ[x]); // print based on index
}
else if (text[i] >= 'a' && text[i] <= 'z') // print small
{
text[i] -= 97;
text[i] = (text[i] + key)%26;
x = (int)text[i];
printf("%c", az[x]);
}
else
printf("%c", text[i]); // print rest characters
if (text[i] == '\n')
break;
}
getchar();
getchar();
return 0;
}
int only_digits(char **argv)
{
unsigned int i;
unsigned int len = strlen(argv[1]);
for (i = 0; i < len; i++)
{
if (argv[1][i] < '0' || argv[1][i] > '9')
return -1;
else
// convert argv 'number' characters to real number
return strtol(argv[1], NULL, 10);
}
// Error: no argv was processed
return -2;
}
This solution works, but it doesn’t go through cs50 tests. So I had to fix it and made solution #2:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int only_digits(char *argv[]);
int main(int argc, char *argv[])
{
int i, n, j, x;
int key; // cipher key
char text[1000]; // text input
char cypher[1000]; // cypher output
char AZ[26], az[26]; // alphabetical indexes
// in case if user didn't input any console argument
if (!(argc == 2))
{
printf("Usage: ./caesar key");
return 1;
}
// check console argument (char) and convert them to (int)
if (only_digits(argv) == 1)
{
printf("Usage: ./caesar key");
return 1;
}
// convert argv 'number' characters to real number
key = strtol(argv[1], NULL, 10);
if (key < 0)
{
printf("Usage: ./caesar key");
return 1;
}
printf("plaintext: ");
for (n = 0; ; n++) // we also count number of input for the next cycle
{
scanf("%c", &text[n]); // input
if (text[n] == '\n') // if input ended - put new line
{
break;
}
}
// build A-Z and a-z indexes
for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25
{
AZ[i] = j++;
}
for (i = 0, j = 97; i < 26; i++)
{
az[i] = j++;
}
printf("ciphertext: ");
for (i = 0; i < n; i++)
{
if (text[i] >= 'A' && text[i] <= 'Z') // print capital
{
text[i] -= 65; // to move ASCII to 0
text[i] = (text[i] + key) % 26; // key formula to move past 'z'
x = (int)text[i]; // convert char to int
printf("%c", AZ[x]); // print based on index
}
else if (text[i] >= 'a' && text[i] <= 'z') // print small
{
text[i] -= 97;
text[i] = (text[i] + key) % 26;
x = (int)text[i];
printf("%c", az[x]);
}
else
{
printf("%c", text[i]); // print rest characters
}
if (text[i + 1] == '\n')
{
printf("\n");
break;
}
}
return 0;
}
int only_digits(char **argv)
{
unsigned int i;
unsigned int len = strlen(argv[1]);
for (i = 0; i < len; i++)
{
if (!isdigit(argv[1][i]))
{
return 1;
}
}
return 0;
}
Solution by Shtukensia:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
char rotate (char c, int n);
int main(int argc, string argv[])
{
// Check if user write key number correctly as string argv[]:
if (argc!=2)
{
printf("Error: command-line arguments\n");
return 1;
}
int k=1;
int i=0;
int key=0;
for (i=0; argv[k][i];i++)
{
int k_check = isdigit(argv[k][i]);
if (k_check==0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Turn our key number string argv[1] to integer key_number:
char* endPtr;
int key_number = strtol(argv[1],&endPtr,10);
// Get use input of plaintxtx:
printf("plaintext:");
string plaintxtx = get_string(" ");
int textlen = strlen(plaintxtx);
// Turn characters from plaintxtx to ciphertext with function
char c; // character from plaintxtx
char c_new; // newmade rotated character
int n = key_number; // key that user made
printf("ciphertext: ");
int j=0;
for (j=0;j<=textlen;j++) // print char by char rotated letters
{
c = plaintxtx[j];
c_new = rotate(c,n);
printf("%c", c_new);
}
printf("\n");
return 0;
}
char rotate (char c, int n)
{
int check_c = isalpha(c); // check if char is letter
if (check_c==0) // if char is not letter, return as it is
return c;
else
{
char c_new=' '; // rotated newmade character
int check_upper = isupper(c); // check if char is uppercase
if (check_upper>0)
{
int c_asci = (c); // convert char LETTER to int ASCII number (A->65)
int num_letter=(c_asci-65); // find numer of upper letter in alphabet (index)
int shifted_index=(num_letter+n)%26; // shift index of letter with key number
c_new=shifted_index+65; // convert ASCII number back to new letter
return c_new;
}
else if (check_upper==0)
{
int c_asci = (c); // convert char LETTER to int ASCII number (a->97)
int num_letter=(c_asci-97); // find numer of lower letter in alphabet (index)
int shifted_index=(num_letter+n)%26; // shift index of letter with key number
c_new=shifted_index+97; // convert ASCII number back to new letter
return c_new;
}
else
return 'N';
}
}
And after checking bugs my programme looks like this:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
char rotate(char c, int n);
int main(int argc, string argv[])
{
// Check if user write key number correctly as string argv[]:
if (argc != 2)
{
printf("Error: command-line arguments\n");
return 1;
}
int k = 1;
int i = 0;
int key = 0;
for (i = 0; argv[k][i]; i++)
{
int k_check = isdigit(argv[k][i]);
if (k_check == 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Turn our key number string argv[1] to integer key_number:
char *endPtr;
int key_number = strtol(argv[1], &endPtr, 10);
// Get use input of plaintxtx:
printf("plaintext:");
string plaintxtx = get_string(" ");
int textlen = strlen(plaintxtx);
// Turn characters from plaintxtx to ciphertext with function
char c; // character from plaintxtx
char c_new; // newmade rotated character
int n = key_number; // key that user made
printf("ciphertext: ");
int j = 0;
for (j = 0; j < textlen; j++) // print char by char rotated letters
{
c = plaintxtx[j];
c_new = rotate(c, n);
printf("%c", c_new);
}
printf("\n");
return 0;
}
char rotate(char c, int n)
{
int check_c = isalpha(c); // check if char is letter
if (check_c == 0) // if char is not letter, return as it is
{
return c;
}
else
{
char c_new = ' '; // rotated newmade character
int check_upper = isupper(c); // check if char is uppercase
if (check_upper > 0)
{
int c_asci = (c); // convert char LETTER to int ASCII number (A->65)
int num_letter = (c_asci - 65); // find numer of upper letter in alphabet (index)
int shifted_index = (num_letter + n) % 26; // shift index of letter with key number
c_new = shifted_index + 65; // convert ASCII number back to new letter
return c_new;
}
else if (check_upper == 0)
{
int c_asci = (c); // convert char LETTER to int ASCII number (a->97)
int num_letter = (c_asci - 97); // find numer of lower letter in alphabet (index)
int shifted_index = (num_letter + n) % 26; // shift index of letter with key number
c_new = shifted_index + 97; // convert ASCII number back to new letter
return c_new;
}
else
{
return 'N';
}
}
}
