Encryption with substitution cipher does not generate valid ASCII output - c

I cant figure out why it says "output not valid ASCII text"!
In order for you to understand the context of the problem i post what is described to do.
IT STARTS HERE!
In a substitution cipher, we “encrypt” (i.e., conceal in a reversible way) a message by replacing every letter with another letter. To do so, we use a key: in this case, a mapping of each of the letters of the alphabet to the letter it should correspond to when we encrypt it. To “decrypt” the message, the receiver of the message would need to know the key, so that they can reverse the process: translating the encrypt text (generally called ciphertext) back into the original message (generally called plaintext).
A key, for example, might be the string NQXPOMAFTRHLZGECYJIUWSKDVB. This 26-character key means that A (the first letter of the alphabet) should be converted into N (the first character of the key), B (the second letter of the alphabet) should be converted into Q (the second character of the key), and so forth.
A message like HELLO, then, would be encrypted as FOLLE, replacing each of the letters according to the mapping determined by the key.
Let’s write a program called substitution that enables you to encrypt messages using a substitution cipher. At the time the user executes the program, they should decide, by providing a command-line argument, on what the key should be in the secret message they’ll provide at runtime.
Here are a few examples of how the program might work. For example, if the user inputs a key of YTNSHKVEFXRBAUQZCLWDMIPGJO and a plaintext of HELLO:
$ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext: HELLO
ciphertext: EHBBQ
Here’s how the program might work if the user provides a key of VCHPRZGJNTLSKFBDQWAXEUYMOI and a plaintext of hello, world:
$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext: hello, world
ciphertext: jrssb, ybwsp
Notice that neither the comma nor the space were substituted by the cipher. Only substitute alphabetical characters! Notice, too, that the case of the original message has been preserved. Lowercase letters remain lowercase, and uppercase letters remain uppercase.
Whether the characters in the key itself are uppercase or lowercase doesn’t matter. A key of VCHPRZGJNTLSKFBDQWAXEUYMOI is functionally identical to a key of vchprzgjntlskfbdqwaxeuymoi (as is, for that matter, VcHpRzGjNtLsKfBdQwAxEuYmOi).
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: ./substitution KEY");
return 1;
}
else if (argc == 2)
{
string text = argv[1];
string storing = text;
int counter = 0;
int i = 0;
bool number = true;
bool flag = true;
while (flag == true && i < 26)
{
if ((int)text[i] >= 48 && (int)text[i] <= 57)
{
number = false;
}
if (((int)text[i] >= 65 && (int)text[i] <= 90) || ((int)text[i] >= 97 && (int)text[i] <= 122))
{
counter++;
for ( int j = 0; j < counter - 1; j++)
{
if ((int)storing[j] == (int)text[i] || (int)storing[j] + 32 == (int)text[i])
{
flag = false;
}
}
}
i++;
}
if (number == false)
{
printf("Key must only contain alphabetic characters.");
return 1;
}
if (flag == false)
{
printf("Key must not contain repeated characters.");
return 1;
}
if (counter < 26)
{
printf("Key must contain 26 characters.");
return 1;
}
}
string plaintext = get_string("plaintext:");
string key = argv[1];
int counter;
bool not_letter;
bool capital1;
bool capital;
int crypto[strlen(plaintext)];
for (int i = 0; i < strlen(plaintext); i++)
{
capital1 = false;
capital = false;
not_letter = false;
if ((int)plaintext[i] >=65 && (int)plaintext[i] <= 90)
{
counter = (int)plaintext[i] - 65;
capital = true;
}
else if ((int)plaintext[i] >=97 && (int)plaintext[i] <= 122)
{
counter = (int)plaintext[i] - 97;
capital1 = true;
}
else
{
not_letter = true;
}
if (not_letter == true)
{
crypto[i] = (int)plaintext[i];
}
else if (capital == true)
{
if ((int)key[i] >=65 && (int)key[i] <= 90)
{
crypto[i] = (int)key[counter];
}
else if ((int)key[i] >=97 && (int)key[i] <= 122)
{
crypto[i] = (int)key[counter] - 32;
}
}
else if (capital1 == true)
{
if ((int)key[i] >=65 && (int)key[i] <= 90)
{
crypto[i] = (int)key[counter] + 32;
}
else if ((int)key[i] >=97 && (int)key[i] <= 122)
{
crypto[i] = (int)key[counter];
}
}
}
printf("ciphertext: ");
for (int i = 0; i < strlen(plaintext); i++)
{
printf("%c", (char)crypto[i]);
}
printf("\n");
return 0;
}
When i test the programm with check50 cs50/problems/2020/x/substitution of CS50 it says:
:) substitution.c exists
:) substitution.c compiles
:) encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
! :( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
output not valid ASCII text
! :( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
output not valid ASCII text
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key

Let's assume you've converted your key to uppercase.
Then let's simplify the heck out of your loop:
int keyIndex;
bool lowercase;
for (int i = 0; i < strlen(plaintext); i++) {
if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') {
keyIndex = plaintext[i] - 'A';
lowercase = false;
} else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') {
keyIndex = plaintext[i] - 'a';
lowercase = true;
} else {
// do not encrypt that character
ciphertext[i] = plaintext[i];
// and skip the rest of the loop
continue;
}
ciphertext[i] = key[keyIndex];
// revert back to lowercase, if necessary
if (lowercase) {
ciphertext[i] += 'a' - 'A';
}
}
The problem you've run into is that you didn't split up your big problem into small problems. So if you e.g. create an all uppercase key in advance, then you don't need to take it into account later on.
It's also very important to keep symmetry in your application. So if you assign a value in one part of the if then also do it in the else. And if you're able to stop early (such as when a character needs to be kept), then please do so.
It would of course be equally valid to convert the key to all lowercase and then convert characters back to uppercase where necessary.
Never ever use variable names such as counter or uppercase1. It makes your code very hard to read; variable names should be as clear as possible.
The ciphertext array is just a string / character array in the code above. If you're using C rather than C++ then you'd normally use a char* or char[], I presume.
Of course, this is still without using any separation between functions. It also doesn't use any platform functionality. If you manage to do that then debugging / maintaining your application will become even easier.

Related

CS50 - pset2 - Substitution - "output not valid ASCII text"

My program is producing what is seems like the correct output but i still get the :( message when i run check50. I have already read other awnsers to similar questions but none of them seems actually similar to my problem.
check50 output:
:) substitution.c exists
:) substitution.c compiles
:) encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
->:( encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
output not valid ASCII text
->:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
output not valid ASCII text
:) encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) does not encrypt non-alphabetical characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) handles lack of key
:) handles too many arguments
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
I put the -> before the error messages for easier visualization
It is weird because right before the two errors there is an almost identical input/output that has been checked as correct
Here is my code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
string subs(string plain, string key);
int key_check(string key);
int main(int argc, string argv[]) {
// CHECK IF IT HAS AN INPUT
if (argc < 2) {
printf("Usage: ./substitution key\n");
return 1;
}
// CHECK IF IT HAS MORE THAN 1 INPUT
if (argc > 2) {
printf("Usage: ./substitution key\n");
return 1;
}
// IF KEYCHECK FUNCTION DETECTS AN ERROR, RETURN 1
if (key_check(argv[1]) == 1) {
return 1;
}
// ELSE KEY = USER ARGV INPUT
string key = argv[1];
// GET USER PLAINTEXT INPUT
string plain = get_string("plaintext: ");
string cipher = subs(plain, key);
// PRINT RESULT
printf("ciphertext: %s\n", cipher);
}
int key_check(string key) {
// STRING LENGHT
int leng = strlen(key);
// CHECK IF KEY HAVE 26 CHARACTERS
if (leng < 26) {
printf("Key must contain 26 characters.\n");
return 1;
}
for (int i = 0; i < leng; i++) {
// CHECK IF KEY ONLY HAVE ALPHABET CHARACTERS
if (isalpha(key[i]) == 0) {
printf("Key must contain only alphabet characters\n");
return 1;
}
// CHECK IF KEY HAVE REPEATED CHARACTER
for (int i2 = 0; i2 < 26; i2++) {
if (i != i2) {
if (key[i] == key[i2]) {
printf("Key must have each character exactly one time\n");
return 1;
}
}
}
}
return 0;
}
string subs(string plain, string key) {
// GET PLAINTEXT LENGHT
int leng = strlen(plain);
// CREATES CIPHER STRING
string cipher = plain;
// CREATES AN ARRAY FOR UPPER KEY
int UPPER[26];
for (int i2 = 0; i2 < 26; i2++) {
if (isupper(key[i2]) > 0 ) {
UPPER[i2] = key[i2];
}
else {
UPPER[i2] = key[i2] - 32;
}
}
// CREATES AN ARRAY FOR LOWER KEY
int LOWER[26];
for (int i3 = 0; i3 < 26; i3++) {
if (islower(key[i3] > 0)) {
LOWER[i3] = key[i3];
}
else {
LOWER[i3] = key[i3] + 32;
}
}
for (int i = 0; i < leng; i++) {
if (isupper(plain[i]) > 0) {
cipher[i] = UPPER[plain[i] - 65];
}
else if (islower(plain[i]) > 0) {
cipher[i] = LOWER[plain[i] - 97];
}
else {
cipher[i] = plain[i];
}
}
return cipher;
}
It all leads me to think that it is a check50 problem, but with my lack of experience with coding and problem solving it can be anything.
Thanks in advance.
The line if (islower(key[i3] > 0)) { has parentheses in the wrong place. It should be:
if( islower(key[i3]) > 0 ){
or (more typical in C):
if( islower(key[i3]) ){
To get upper and lower working.
Lowercase needs to point to i2.
for (int i2 = 0; i2 < 26; i2++) {
if (islower(key[i2] > 0)) {
LOWER[i2] = key[i2];
}
else {
LOWER[i2] = key[i2] + 32;

Encryption does not output an ASCII string

In CS50, I'm trying to end my substitution exercise and I've an issue but don t know how to solve.
This is my code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
int get_validkey(string Text);
int get_Alpha_to_code(char charac);
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("usage: ./substitution key\n");
return 1;
}
int validation = get_validkey(argv[1]);
if (validation != 0)
{
if(validation == 1)
{
printf("key must contain 26 alphabetical characters\n");
}
else
{
if (validation == 2)
{
printf("some charaters are not alphabetic\n");
}
else
{
printf("some charaters are repeated\n");
}
}
return 1;
}
else
{
// constants
string code = argv[1];
int charc;
int j;
// ask for message to encrypt
string tocode = get_string("plaintext: ");
// transform to code
int charcount = strlen(tocode);
char codedmessage[charcount];
for (int i = 0; i < strlen(tocode); i++)
{
// check type of character
if ((tocode[i]>='a' && tocode[i]<='z') || (tocode[i]>='A' && tocode[i]<='Z'))
{
j = get_Alpha_to_code(tocode[i]);
if(islower(tocode[i]))
{
codedmessage[i] = tolower(code[j]);
}
else
{
codedmessage[i] = toupper(code[j]);
}
}
else
{
codedmessage[i] = tocode[i];
}
}
codedmessage[strlen(codedmessage)] = '\0';
printf("ciphertext: %s", codedmessage);
printf("\n");
return 0;
}
}
// function assesses if the key input is valid and returns 0 if it is and 1 if it is not
int get_validkey(string Text)
{
int inputlength = strlen(Text);
if (inputlength != 26)
{
return 1;
}
else
{
for (int g = 0; g < 26; g++)
{
// checks if the character is non alphabetical
char chartest = toupper(Text[g]);
if (chartest < 'A' || chartest > 'Z')
{
return 2;
}
// scans all characters before A[g] to see if it has already been used
for (int k = 0; k < g; k++)
{
char beforechar = toupper(Text[k]);
if (chartest == beforechar)
{
return 3;
}
}
}
return 0;
}
}
int get_Alpha_to_code(char charac)
{
// define order for alphabet
const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char Alphachar = '\0';
// look at char position in alphabet
char chartest = toupper(charac);
// find position of charac in chain
int k = 0;
while (chartest != Alphachar)
{
Alphachar = Alphabet[k];
k++;
}
// send back char in code
return k - 1;
}
Check results are:
:) substitution.c exists :) substitution.c compiles :( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key output not valid ASCII text
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
output not valid ASCII text
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
output not valid ASCII text
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
output not valid ASCII text
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
:) encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) handles lack of key :) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
My results seems working because for 'A' I've 'Z', for 'a' I've got 'z', ...
But check system does not recognize my output as ASCII.
your problem is in that line :
codedmessage[strlen(codedmessage)] = '\0';
it can be
codedmessage[i] = '\0';
or
codedmessage[charcount] = '\0';
or more expensive for nothing
codedmessage[strlen(tocode)] = '\0';
because you cannot use strlen on codedmessage before to put the null character ending it (which is what you are trying to do), so you (may be) rewrite 0 exactly at the position you (may be) found it, that can be out of codedmessage and the behavior is undefined. In the next line printf writes the chars until it (may be) found a null char, so writing non initialized characters from it, the behavior is again undefined
Out of that, in :
int charcount = strlen(tocode);
char codedmessage[charcount];
for (int i = 0; i < strlen(tocode); i++)
you know the length is charcount why do you callstrlen(tocode) each turn knowing it is unchanged and what is its value ?

CS50 - pset2 - substitution

I can't see what my code for pset2 substitution is missing. When I use check50 to test the program it returns this result:
:) substitution.c exists
:) substitution.c compiles
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: Z...", not "ciphertext: Z..."
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: z...", not "ciphertext: z..."
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
expected "ciphertext: NJ...", not "ciphertext: NJ..."
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
expected "ciphertext: Ke...", not "ciphertext: Ke..."
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
expected "ciphertext: Cb...", not "ciphertext: Cb..."
:( encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
expected "ciphertext: Cb...", not "ciphertext: Cb..."
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
expected "ciphertext: Cb...", not "ciphertext: Cb..."
:( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
expected "ciphertext: Rq...", not "ciphertext: Rq..."
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
However, when I manually type in a key and plaintext it works exactly as it is supposed to. Furthermore, the 'expected' result of check50 seems to be exactly the same as the output so it is not obvious what is wrong.
my code is as follows:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int get_validkey(string A);
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("useage: ./substitution key\n");
return 1;
}
int validation = get_validkey(argv[1]);
if (validation == 1)
{
printf("key must contain 26 alphabetical characters\n");
return 1;
}
// prompting user for plaintext
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
int length = strlen(plaintext);
for (int c = 0; c <= length ; c++)
{
// printing any non-alphabet characters unchanged
if (plaintext[c] < 'A' || (plaintext[c] > 'Z' && plaintext[c] < 'a') || plaintext[c] > 'z')
{
printf("%c", plaintext[c]);
}
else
{
for (int b = 0; b <= 25; b++)
{
if (plaintext[c] == 65 + b)
{
char upper = argv[1][b];
int up = isupper(upper);
if (up == 0)
{
upper = toupper(upper);
printf("%c", upper);
}
if (up != 0)
{
printf("%c", upper);
}
}
else if (plaintext[c] == 97 + b)
{
char lower = argv[1][b];
int low = islower(lower);
if (low == 0)
{
lower = tolower(lower);
printf("%c", lower);
}
if (low != 0)
{
printf("%c", lower);
}
}
}
}
}
printf("\n");
return 0;
}
// function assesses if the key input is valid and returns 0 if it is and 1 if it is not
int get_validkey(string A)
{
int inputlength = strlen(A);
if (inputlength != 26)
{
return 1;
}
else
{
for (int g = 0; g < 26; g++)
{
// checks if the character is non alphabetical
if (A[g] < 'A' || (A[g] > 'Z' && A[g] < 'a') || A[g] > 'z')
{
return 1;
}
// scans all characters before A[g] to see if it has already been used
for (int k = (g - 1); k >= 0; k--)
{
if (A[k] == A[g])
{
return 1;
}
// also checks if different case of the same character has been used
if (A[k] == A[g] + 32)
{
return 1;
}
if (A[k] == A[g] - 32)
{
return 1;
}
}
// scans all characters after A[g] to check if it has been used already. (Not sure if this is necessary)
for (int l = (g + 1); l < 26; l++)
{
if (A[l] == A[g])
{
return 1;
}
// also checks if a different case of the same letter is used
if (A[l] == A[g] + 32)
{
return 1;
}
if (A[l] == A[g] - 32)
{
return 1;
}
}
}
return 0;
}
}
I am very new to programming and any help would be greatly appreciated.
I've encountered the same problem, but then I realized that when I was iterating the for loop for my plain text which is, for (int c = 0; c <= length ; c++), that the programming language itself internally starts with 0 when counting. Even though we knew it should be equal to the length of the plain text, the language will iterate to that null element within the array of string. I suggest you do this, for (int c = 0; c <= length ; c++). Hope, it helps.

All outputs end up the same in C

I'm trying to iterate over key which has been entered into the command line. During the iteration, I want to create 2 strings, upper and lower, which can be used later. The problem is that key, upper and lower are all ending up with the same outputs. Could someone help me figure out where I'm going wrong?
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./substitution key\n"); // if no key is entered on command line, end program and say key
return 0;
}
string key = argv[1]; // convert command to key for ease
string alpha = "abcdefghijklmnopqrstuvwxyz"; // lowercase alphabet
string cap_alpha ="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //uppercase alphabet
string lower = argv[1]; // gives string correct length to edit later
string upper = argv[1]; // ^ditto
if (strlen(key) != 26) // if key is not exactly 26 char, end program and say it must contain 26
{
printf("Key must contain 26 characters\n");
return 0;
}
for (int i = 0; i < strlen(key); i++) //iterates through key
{
if (key[i] < 'A' || (key[i] > 'Z' && key[i] < 'a') || key[i] > 'z') //if key includes non alphabet characters, ends command
{
printf("Key must only contain alphabetic characters.\n");
return 0;
}
else if (key[i] >= 'A' && key[i] <= 'Z')
{
lower[i] += 32; // if char in key is uppercase, convert to lowercase, add to string "lower"
upper[i] = key[i]; // if char in key is uppercase, keeps as uppercase
}
else
{
upper[i] = key[i] - 32; // if char in key is lowercase, changes to uppercase
lower[i] = key[i];
}
}
printf("key: %s\n", key); // test output
printf("lower: %s\n", lower); //test output
printf("upper: %s\n", upper); // test output
}
Assigning strings doesn't make copies, it just assigns a pointer to the same string. So key, upper, lower, and argv[1] are all the same string. When you make a change to one of them, it affects all of them.
You need to make copies first.
size_t len = strlen(key) + 1; // +1 for the null byte
char upper[len], lower[len];
strcpy(upper, key);
strcpy(lower, key);
BTW, C has functions isalpha(), isupper(), islower(), toupper(), and tolower(). You should use these instead of relying on the specifics of ASCII coding.

Why does the output of the code changes, when printing a random text with printf?

The following code changes the letters of the input by k. k is the first argument. For example, if the input should be changed by 2 letters the command line argument would be "./caesar 2", if three then "./caesar 3". etc.
Changing letters means, for example, change by 2, then input 'a' becomes 'c'. Change by 3 means input "abc" becomes "def", etc.
The input provided by the user is being checked if (a) the number arguments is exact 2, (b) the argument is a number.
The code executed as the code is written below the output is shortened by one letter. For example, "hello" changed by 1 letter becomes "iffm".
If one letter is entered only, it shows the correct output follwed by some undefined letters. For example, 'a' becomes "b��P" or "bm>�" or "b;���".
When either (1) the input check (b) [if the argument is a number] is removed OR (2) a printf line with a random statement (it can even be an empty string) is inserted EXACTLY between the get_string function, when asking the user for input and the for-loop, when changing the letters the output is as intended. Or (3) if the input's last character is a special character, the output is as expected (special character is any non-alphabetical character). for example, "hello1" or "hello!" changed by one letter becomes "ifmmp1" or "ifmmp!".
I am really desperate and I don't know what is happening and more importantly why this is happening.
So my questions are:
(1) Why is output shortened by one letter? Why is the output wrong, when the input is one letter only? (I guess it's the same problem).
(2)
(a) Why does the output change when either the number check is removed or
(b) a random printf line is inserted exactly between the lines mentioned above or
(c) the last character is a non-alphabetical character?
I really appreciate any help and please excuse any weird English as it is not my native language :). Thanks a lot! A desperate code learner :)
This is the code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
bool isNumber(string numberToCheck);
int main(int argc, string argv[])
{
// checking, if arguments are correct
// checking, if input is correct (i.e. 2)
if(argc != 2)
{
printf("Usage: ./cesar key\n");
exit(1);
}
// checking, if input is a number, if the following if statement is removed the output changes
if(!isNumber(argv[1]))
{
printf("Usage: ./caesar key\n");
exit(1);
}
// variables
int k = atoi(argv[1]);
string plaintext;
int plaintextLength;
// getting the plaintext from user input, creating ciphertext string of same length
plaintext = get_string("paintext: ");
// checking the length of the input
plaintextLength = strlen(plaintext);
//the output changes, when the next line is being inserted
printf("");
// creating new empty string with plaintextLength
char ciphertext[plaintextLength];
// iterating through plaintext char by char
for(int i = 0; i < plaintextLength;i++)
{
// in case of capital letter
if(plaintext[i] >= 65 && plaintext[i] <= 90)
{
ciphertext[i] = 65 + (((plaintext[i] - 65) + k) % 26);
}
// else in case of small letter
else if(plaintext[i] >= 97 && plaintext[i] <= 122)
{
ciphertext[i] = 97 + (((plaintext[i] - 97) + k) % 26);
}
// else in case of non alphabetical letter
else
{
ciphertext[i] = plaintext[i];
}
}
printf("ciphertext: %s\n", ciphertext);
}
bool isNumber(string numberToCheck)
{
for(int i = 0;i < strlen(numberToCheck); i++)
{
if(!isdigit(numberToCheck[i]))
{
return false;
}
}
return true;
}
int i;
// iterating through plaintext char by char
for(i = 0; i < plaintextLength;i++)
{
// in case of capital letter
if(plaintext[i] >= 65 && plaintext[i] <= 90)
{
ciphertext[i] = 65 + (((plaintext[i] - 65) + k) % 26);
}
// else in case of small letter
else if(plaintext[i] >= 97 && plaintext[i] <= 122)
{
ciphertext[i] = 97 + (((plaintext[i] - 97) + k) % 26);
}
// else in case of non alphabetical letter
else
{
ciphertext[i] = plaintext[i];
}
}
ciphertext[i] = '\0';
printf("ciphertext: %s\n", ciphertext);
}
In C every string uses \0 to tell the computer that it has reached the end of a string.
Without this NULL terminating operator, the computer might try to read past the string into the memory, that is why you encounter random symbols.
So when you are creating a string make sure to add \0 at the end of it, this is what I did with your code here.
I declared "i" outside the for loop the it doesn't cease to exist at the end of the loop.
When you reach the end of the loop, "i" will be equal to the length of your string.
If my plaintext is "lol" and the key is 5, "i" will be equal to 3 and the ciphertext will be "qtq".
Ciphertext[i] will point just after the last "q" since we count from 0 an this is where you want to put your \0.
Also there is a typo in your getstring.
Hope my explaination is clear, if you have any question juste ask :)

Resources