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.
Related
I'm going through CS50 (2021x version) of Caesar problem, and ran into problem. My program is printing outside of ASCII range (thanks to curiouskiwi over at discord for this hint). The error message says ":( encrypts "barfoo" as "yxocll" using 23 as key, output not valid ASCII text". Another one I'm having trouble is "world, say hello!", for same reason (Not valid ASCII text). Other ones are encrypting fine.
I've stepped through the debugger and found that 'letter' variable is sometimes becoming a negative integer like -119'/211', but can't figure out why that may be so. I expected to see positive value associated with an alphabet in ASCII. When this happens the letters will stop printing on the console.
If I type ./caesar 23 | cat -A and then give barfoo as plaintext, the cyphertext will come out as yxM-^IcM-^FM-^F$.
int main(int argc, string argv[])
{
// only 1 arugment, and positive argument only
if (argc == 2 && argv[1] > 0)
{
// check if each char of argument is digit
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (isdigit(argv[1][i]))
{
// do nothing
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// change the key to how much letters should move over
int input = atoi(argv[1]);
int key = input % 26;
char letter;
// get the input
string text = get_string("plaintext: ");
printf("ciphertext: ");
for (int i = 0, n = strlen(text); i < n; i++)
{
if (isalpha(text[i])) // if it is an alphabet
{
if (islower(text[i])) // if it is lowercase
{
letter = text[i] + key; // add key to text[i]
if (letter > 122)
{
// loop around the alphabet
letter -= 26;
}
printf("%c", letter);
}
else // if it is uppercase
{
letter = text[i] + key; // add key to text[i]
if (letter > 90)
{
// loop around the alphabet
letter -= 26;
}
printf("%c", letter);
}
}
else // if it is not an alphabet
{
printf("%c", text[i]);
}
}
printf("\n");
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
You should use int letter instead of char letter.
This is because
letter = text[i] + key;
will overflow signed char for, say, 'z' + 23.
I'm trying to write code that will take each digit from a plaintext string input and, if it is a letter, output a different letter, as defined by a substitution key (26-letter key).
In other words, if the alphabet was "abcd" and provided key was "hjkl", an input of "bad" would output "jhl".
// Regular alphabet is to be used as comparison base for key indexes //
string alphabet = "abcdefghijklmnopqrstuvwxyz";
// Prompt user for input and assign it to plaintext variable //
string plaintext = get_string("plaintext: ");
Non-letters should be printed as-is.
My idea was to loop the input digit through every index in the alphabet looking for the corresponding letter and, if found, print the same index character from the string. (confusing, I think)
This loop, however, returns a segfault when I run it, but not when debugging:
// Loop will iterate through every ith digit in plaintext and operate the cipher //
for (int i = 0; plaintext[i] != '\0'; i++) {
// Storing plaintext digit in n and converting char to string //
char n[2] = "";
n[0] = plaintext[i];
n[1] = '\0';
// If digit is alphabetic, operate cipher case-sensitive; if not, print as-is //
if (isalpha(n) != 0) {
for (int k = 0; alphabet[k] != '\0'; k++) {
char j[2] = "";
j[0] = alphabet[k];
j[1] = '\0';
if (n[0] == j[0] || n[0] == toupper(j[0])) {
if (islower(n) != 0) {
printf("%c", key[k]);
break;
} else {
printf("%c", key[k] + 32);
break;
}
}
}
} else {
printf("%c", (char) n);
}
}
What's going wrong? I've looked for help online but most sources are not very beginner-friendly.
Your code seems to be working except one error: The program crashes at
isalpha(n)
Cause you declared
char n[2]
the parameter there is a pointer of type char*. But islower only accepts an int parameter, so just write it as
isalpha(n[0])
Same for islower.
I'm solving a coding problem currently. I must encipher a code and change words into other encoded words. I know I can check and print each character. However, I want to try to save the result of the checking of each character to an array, and then later I want to just print it.
Below is the full code... I deeply appreciate it if anyone can help me answer my question, or if it is even possible.
//one command line argument with the type int
int main(int argc, string argv[])
{
//setting condition that: K = +; if more or less than one, immediate error message, return 1
//if not decimal return = usage ./caesar. key, return value 1 to main
if (argc != 2)
{
printf("Error 404 : \n");
return 1;
}
//main program
else if ( argc == 2 && isdigit(*argv[1]))
{
int k = atoi(argv[1]);
string pltext = get_string("plaintext: "); //getting input for the plain text
char cptext[]
for (int i = 0, n = strlen(pltext) ; i < n; i++) //turning pltext to integer
{
if (pltext[i] >= 'a' && pltext[i] <= 'z')
{
cptext[i] = ((pltext[i] - 'a' + k)%26)+'a'; //shifting the integer with k (lowercase)
}
else if (pltext[i] >= 'A' && pltext[i] <= 'Z')
{
cptext[i] = ((pltext[i] - 'A' + k)%26)+'A'; //shifting the integer with k (uppercase)
}
else
{
cptext[i] = pltext[i]; //other symbol stays
}
}
//print out result
string cptext = ("test");
printf("ciphertext: %s\n", cptext[]);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
You should allocate an array according to the number of elements to be stored.
char cptext[]
should be
char cptext[strlen(pltext) + 1]; // +1 for terminating null-character
(variable-length array introduced in C99)
and
string cptext = ("test");
printf("ciphertext: %s\n", cptext[]);
should be
cptext[strlen(pltext)] = '\0'; // terminate the string
printf("ciphertext: %s\n", cptext); // remove extra []
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 :)
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// two arguments
if (argc != 2)
{
printf("Give two arguments\n");
return 1;
}
printf("plaintext: ");
string plaintext = get_string();
printf("ciphertext: ");
string key = argv[1];
for (int i = 0, t = 0, n = strlen(plaintext); i < n; i++, t++)
{
// if it's no letter, then:
if (!isalpha(plaintext[i]) && plaintext[i] != ' ')
{
printf("False");
return 1;
}
int number = 0;
if (isalpha(plaintext[i]))
{
number += 1;
}
if (strlen(key) > number)
{
number = 0;
}
if (isupper(plaintext[i]))
{
printf("%c", (((plaintext[i] - 65) + key[number]) % 26) + 65);
}
//if it is lowercase
else if (islower(plaintext[i]))
{
printf("%c", (((plaintext[i] - 97) + key[number]) % 26) + 97);
}
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
}
So there's something missing with my code. When I do ./vigenere baz and then type as plaintext: Hello, world!, I get ciphertext: ByffiFalse. I should be getting iekmo, vprke! Also, when I type ./vigenere hello, and then type bye as the plaintext, I get ciphertext bye too while it should be icp. Can someone figure out what's missing or wrong with my code?
The biggest two problems with your code are the calculating the correct key differential value (you're not), and key advancement. I'll talk about them in reverse order.
Key advancement should start with the first key character, then advance one by one with each plain text being processed. When the key position reaches end-of-string, it is restarted. The most basic pseudo code for that would be
char *keyp = argv[1];
for (loop through plainttext)
{
if (*keyp == 0) // reached the terminator ?
keyp = argv[1]; // then reset to beginning.
//... process the current plain text character, using *keyp
//... as the next key character to use.
// advance key to next position (possibly conditionally)
++keyp;
}
But your code doesn't do that. Rather, it advances the key immediately, meaning you're starting with the second character onward.
int number = 0;
if (isalpha(plaintext[i]))
{
number += 1; // HERE. first pass will use key[1]. it should be key[0]
}
if (strlen(key) > number) // this is backward
{
number = 0;
}
Secondly, and probably more important, the whole point if a Vigenere cipher is effectively using a square shading table. See this link for a picture of that. The point of the algorithm you're coding is to act like that table exists using math. The offsets are the important part.When you do this calculation:
(((plaintext[i] - 65) + key[number]) % 26) + 65
which in reality should look like this:
(((plaintext[i] - 'A') + key[number]) % 26) + 'A'
consider what that key character addition is doing. Take your example:
key: baz
plaintext: Hello, World!
The first ciphertext character by your calculation will be:
((('H' - 'A') + 'a') % 26) + 'A'
Note: the 'a' is there because your first-pass is broken by one, remember?
That crunches down as follows
(((7) + 97) % 26) + 'A'
((105) % 26) + 'A'
(1 % 26) + 'A'
1 + 'A'
'B'
And that's exactly what you're getting. But its wrong. Its wrong because this is wrong:
(((plaintext[i] - 'A') + key[number]) % 26) + 'A'
^^^^^^^^^^^
That's the raw ascii value of the input character. What it should be is a calculated value between 1..26. In short, you're not adjusting your key input correctly.
Assumptive Solution
The following assumes the key will always be lower-case. It also fixes your first-skip logic, and decouples using cs50.h (which, frankly, I think does more harm than good). Finally it uses a `char* to track which key character is being used next. I leave the task of supporting mixed case input keys to you:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
// two arguments
if (argc != 2)
{
printf("Give two arguments\n");
return 1;
}
printf("plaintext: ");
char pt[256] = { 0 };
if (fgets(pt, sizeof pt, stdin))
{
// get the plaintext length
size_t ptlen = strlen(pt);
// remove trailing newline if present, and adjust ptlen
if (ptlen > 0 && pt[ptlen - 1] == '\n')
pt[--ptlen] = 0;
// the key we're using. intially at the start
char *key = argv[1];
for (size_t i = 0; i < ptlen; ++i)
{
// reset key if prior iteration landed on terminator
if (!*key)
key = argv[1];
if (isalpha((unsigned char)pt[i]))
{
if (isupper((unsigned char)pt[i]))
{
printf("%c", (((pt[i] - 'A') + (*key-'a')) % 26) + 'A');
++key;
}
//if it is lowercase
else if (islower((unsigned char)pt[i]))
{
printf("%c", (((pt[i] - 'a') + (*key-'a')) % 26) + 'a');
++key;
}
else
{
fputc(pt[i], stdout);
}
}
else
{
fputc(pt[i], stdout);
}
}
fputc('\n', stdout);
}
else
{
perror("Failed to read string");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Output from ./progname baz
plaintext: Hello, World!
Iekmo, Vprke!
All non-alpha characters (not spaces only) should be skipped without encoding. Do not print "False" and return on, for example ',' symbol in "Hello, world!" string. Also, you can encode string in-place. Thus, main loop may looks like
printf("plaintext: ");
string s = GetString();
if (s == NULL)
return 1;
for (int i = 0, len = strlen(s); i < len; ++i) {
if (isalpha(s[i])) {
/* encode s[i] in-place,
* all non-alpha characters left as is
*/
}
}
printf("ciphertext: %s\n", s);
Key characters should also be "shifted". For example, for uppercase letters
s[i] = ((s[i] - 'A') + (key[n] - 'A') % 26) + 'A';
if (++n >= keylen)
n = 0;
I suggest to normalize key before main loop, so that you will be able to use (key[n] - 'A') both for lower and upper characters from input string:
string key = argv[1];
strupper(k);
int keylen = strlen(key);
int n = 0;
Although I don't want provide full code because this is your courses, I think it would be better if you do it by yourself. But… some pieces:
strupper function:
void strupper(string s)
{
for (int i = 0, n = strlen(s); i < n; ++i)
s[i] = toupper(s[i]);
}
Compact main loop:
for (int i = 0, n = strlen(s); i < n; ++i) {
if (isalpha(s[i])) {
char ref = isupper(s[i]) ? 'A' : 'a';
int shift = k[j] - 'A';
s[i] = ref + (s[i] - ref + shift) % 26;
if (++j >= klen) j = 0;
}
}
p.s. You use the same key character for all input characters because of int number = 0; defined and zeroed inside for loop.