I'm a new programmer trying to teach myself by doing the psets for CS50. I wrote the following bit of code, which works without a problem.
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
string vencipher(string text, string key)
{
for (int i=0, j=0, n =strlen(text); i < n; i++,j++)
{
int m = strlen(key);
if (text[i] >= 65 && text[i] <= 90 && key[j % m] >= 65 && key[j % m] <= 90)
{
text[i] = 65 + ((text[i] - 65) + (key[j % m] - 65)) % 26;
}
else if (text[i] >= 65 && text[i] <= 90 && key[j % m] >= 97 && key[j % m] <= 123)
{
text[i] = 65 + ((text[i] - 65) + (key[j % m] - 97)) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && key[j % m] >= 65 && key[j % m] <= 90)
{
text[i] = 97 + ((text[i] - 97) + (key[j % m] - 65)) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && key[j % m] >= 97 && key[j % m] <= 123)
{
text[i] = 97 + ((text[i] - 97) + (key[j % m] - 97)) % 26;
}
else
{
text[i] = text[i];
j = j - 1;
}
}
return text;
}
int keyvalidator(string text)
{
int alphalen = 0;
for (int i=0, n=strlen(text); i < n; i++)
{
if ((text[i] >= 97 && text[i] <= 123) || (text[i] >= 65 && text[i] <= 90))
{
alphalen = alphalen + 1;
}
}
if (alphalen == strlen(text))
{
return 1;
}
else
{
return 0;
}
}
int main(int argc, string argv[])
{
if (argc != 2 || keyvalidator(argv[1]) != 1)
{
printf("That is not a valid secret key!\n");
return 1;
}
if (argc == 2)
{
string secretKey = argv[1];
string plainText = GetString();
printf("%s\n", vencipher(plainText, secretKey));
}
return 0;
}
I wanted to try and split up vencipher into some different methods to try and improve the code's readability. This is what I did
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
string keycaseID(string key)
{
for (int i=0, n=strlen(key); i < n; i++)
{
if (key[i] >= 65 && key[i] <= 90)
{
key[i] = 1;
}
else
{
key[i] = 0;
}
}
return key;
}
string setkeycase(string key)
{
for (int i=0, n=strlen(key); i < n; i++)
{
if (keycaseID(key)[i] == 1)
{
key[i] = key [i] - 65;
}
else if (keycaseID(key)[i] == 0)
{
key[i] = key [i] - 97;
}
}
return key;
}
string vencipher(string text, string key)
{
for (int i=0, j=0, n =strlen(text); i < n; i++,j++)
{
int m = strlen(key);
if (text[i] >= 65 && text[i] <= 90 && keycaseID(key)[j % m] == 1)
{
text[i] = 65 + ((text[i] - 65) + setkeycase(key)[j % m]) % 26;
}
else if (text[i] >= 65 && text[i] <= 90 && keycaseID(key)[j % m] == 0)
{
text[i] = 65 + ((text[i] - 65) + setkeycase(key)[j % m]) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && keycaseID(key)[j % m] == 1)
{
text[i] = 97 + ((text[i] - 97) + setkeycase(key)[j % m]) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && keycaseID(key)[j % m] == 0)
{
text[i] = 97 + ((text[i] - 97) + setkeycase(key)[j % m]) % 26;
}
else
{
text[i] = text[i];
j = j - 1;
}
}
return text;
}
int keyvalidator(string text)
{
int alphalen = 0;
for (int i=0, n=strlen(text); i < n; i++)
{
if ((text[i] >= 97 && text[i] <= 123) || (text[i] >= 65 && text[i] <= 90))
{
alphalen = alphalen + 1;
}
}
if (alphalen == strlen(text))
{
return 1;
}
else
{
return 0;
}
}
int main(int argc, string argv[])
{
if (argc != 2 || keyvalidator(argv[1]) != 1)
{
printf("That is not a valid secret key!\n");
return 1;
}
if (argc == 2)
{
string secretKey = argv[1];
string plainText = GetString();
printf("%s\n", vencipher(plainText, secretKey));
}
return 0;
}
The newer code compiles but when I run it I get a "floating point exception," which according to my research is the result of modulo divison by 0. I searched my code several times, and I can't find any instance of division by 0. I was wondering if someone could help me find my error and explain to me what's causing the floating point exception here.
I've also seen floating point exceptions on memory corruptions so that's the more likely scenario given there's not a / character anywhere in your code.
I will tell you something that you're doing wrong. By calling keycaseID() on the same string more than once (as you do in setkeycase(), you're guaranteed to end up with a string of all zeros (0 rather than '0').
The first time it will convert all elements to either 1 or 0 depending on their case (and you therefore lose their original value). The second time, because they're either all 0 or 1, they'll be less than 65 and therefore all set to 0.
Assuming your key is all alpha characters (upper or lower case), you can just use something like this to convert it into values 0 through 25:
for (int i = strlen (key) - 1; i >= 0; i--) // needs string.h
if (isupper (key[i])) // needs ctype.h
key[i] -= 'A';
else
key[i] -= 'a';
or, even shorter:
for (int i = strlen (key) - 1; i >= 0; i--) // needs string.h
key[i] = toupper (key[i]) - 'A'; // needs ctype.h
Neither of those is perfectly portable since C doesn't mandate that A-Z are contiguous code points but, as long as you steer clear of weird non-ASCII environments, you should be fine :-)
The problem turned out to be due to a problem with pointers, which we hadn't yet covered in class. What's going on here is string is defined by a pointer to the address in memory of the first char, and so when I tried to create a copy of string by initializing a new variable, I was instead creating a copy of that address and thereby editing both the original input string and the new copy since they share the same memory address.
Because of this the length of m was indeed 0, since keycaseID was modifying the values at the memory address of key, thus wiping key and causing setkeycase to return the empty string.
Related
I'm trying to figure out why my innermost for loop does not break.
I have implemented a Caesar cipher for a "list" of char arrays.
void caesar(char **list, size_t listsize, unsigned int offset) {
for (size_t i = 0; i < listsize; i++) {
for (size_t j = 0; list[i] != NULL && j < sizeof(list[i]); j++) {
for (unsigned int k = 0; k < offset && (list[i][j] >= 65 && list[i][j] <= 90) || (list[i][j] >= 97 && list[i][j] <= 122); k++) {
if(list[i][j] == 122)
list[i][j] = 97;
else if(list[i][j] == 90)
list[i][j] = 65;
else
list[i][j]++;
//works with the break condition
/* if(k == offset - 1)
break;*/
}
}
}
}
So without the last break statement I get an endless loop, even though I have a break condition in the head of my for loop.
Does anybody have an idea?
for (unsigned int k = 0; k < offset && (list[i][j] >= 65 && list[i][j] <= 90) || (list[i][j] >= 97 && list[i][j] <= 122); k++)
Using some placeholder identifiers, the loop condition is written in the form a && b || c, which due to operator precedence is parsed as (a && b) || c and not the a && (b || c) you intend. You can add some parentheses to fix it:
for (unsigned int k = 0; k < offset && ((list[i][j] >= 65 && list[i][j] <= 90) || (list[i][j] >= 97 && list[i][j] <= 122)); k++)
// ^ ^
I am trying to implement Vigenere's Cipher in C but the problem is that when I try to repeat the key used in the array it is in, it breaks after the 4th letter. So if the key is ABC and the plaintext is HELLO, it returns HFNLO instead of HFNLP. When I look at my code it logically makes sense but it seems to just not work. Can anybody see the problem?
Here is the code:
int main(int argc, string argv[])
{
if(argc != 2)
{
printf("usage: ./vigenere k\n");
return 1;
}
//asks for plain text
printf("plaintext: ");
string text = get_string();
string k = argv[1];
printf("ciphertext: ");
//checks to see if length of key is shorter than length of plaintext and duplicates it.
int count = 0;
while(strlen(k) <= strlen(text))
{
k[strlen(k + count)] = k[count];
count++;
}
//changes key to be within 0 - 25 and encrypts plaintext
for(int i = 0; i < strlen(text); i++)
{
if(k[i] >= 'A' && k[i] <= 'Z')
{
k[i] = k[i] - 65;
}
else if (k[i] >= 'a' && k[i] <= 'z')
{
k[i] = k[i] - 97;
}
//if statement for plaintext capital letters
if(text[i] >= 'A' && text[i] <= 'Z')
{
text[i] = text[i] - 64;
text[i] = ((text[i] + k[i]) % 26) + 64;
}
//if statement for plaintext lowercase letters
else if(text[i] >= 'a' && text[i] <= 'z')
{
text[i] = text[i] - 96;
text[i] = ((text[i] + k[i]) % 26) + 96;
}
//prints final cipher
printf("%c", text[i]);
}
printf("\n");
return 0;
}
You should use the modulo operator to compute the offset into the key.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <cs50.h>
int main(int argc, string argv[]) {
if (argc != 2) {
printf("usage: ./vigenere k\n");
return 1;
}
string k = argv[1];
size_t klen = strlen(k);
if (klen == 0) {
fprintf(stderr, "vigenere: key must not be empty\n");
return 1;
}
printf("plaintext: ");
string text = get_string();
printf("ciphertext: ");
for (size_t i = 0; text[i] != '\0'; i++) {
int d = (unsigned char)k[i % klen];
if (d >= 'A' && d <= 'Z') {
d -= 'A';
} else
if (d >= 'a' && d <= 'z') {
d -= 'a';
} else {
d = 0;
}
int c = (unsigned char)text[i];
if (c >= 'A' && c <= 'Z') {
c = 'A' + (c - 'A' + d) % 26;
} else
if (c >= 'a' && c <= 'z') {
c = 'a' + (c - 'a' + d) % 26;
}
putchar(c);
}
putchar('\n');
return 0;
}
I have coded a Caesar cipher that seem to work in most tests but fails on a few cases. More on the test details are https://www.hackerrank.com/challenges/caesar-cipher-1
Basic info: The cipher only encrypts letters, symbols etc stay unencrypted.
Fails on this case:
90
!m-rB`-oN!.W`cLAcVbN/CqSoolII!SImji.!w/`Xu`uZa1TWPRq`uRBtok`xPT`lL-zPTc.BSRIhu..-!.!tcl!-U
62
Where 90 is n (characters in string), second line is string in array s, and 62 is k (amount of letter rotations)
Any insight into the flaw of my code will be highly appreciated
Code:
int main(){
int n;
scanf("%d",&n);
char* s = (char *)malloc(10240 * sizeof(char));
scanf("%s",s);
int k;
scanf("%d",&k);
if (k>26) {
k%=26;
}
int rotation;
for(int i = 0; i<n; i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
if((s[i] + k) > 'z' ) {
rotation = (s[i] - 26) + k;
s[i] = rotation;
} else {
s[i] = s[i]+k;
}
} else if (s[i] >= 'A' && s[i] <= 'Z') {
if((s[i] + k) >= 'Z' ) {
rotation = (s[i] - 26) + k;
s[i] = rotation;
} else {
s[i] = s[i]+k;
}
}
}
for(int i=0; i<n; i++) {
printf("%c", s[i]);
}
return 0;
}
Ok guys, so I've figured it out.
Old Code:
if((s[i] + k) >= 'Z' )
New Code:
if((s[i] + k) > 'Z' )
It messed up when given a P(ascii 80), it should have stopped at Z(ascii 90) but instead did this calculation:
s[i] - 26 + k = 64
80 - 26 + 10 = 64 (ascii for #) and thus '#' was returned instead of Z
My program works only for uppercase letters and I can't figure the problem out. Everything seems to be fine, but it actually isn't. This is a task from CS50 course (week2), by the way.
Here's my code:
#include <stdio.h>
#include "cs50.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{ /* Main should take only one parameter (except program execution, of course) */
if (argc != 2)
return 1;
string text = GetString(); // text to encrypt
int i, l = strlen(text);
int k = atoi(argv[1]); // shift value (key)
/* Shift value should be less or equal to 26 */
if (k > 26)
k = k % 26;
for (i = 0; i < l; i++)
{ /* Making sure the character to encrypt is a letter (from English alphabet) */
if ((islower(text[i])) || (isupper(text[i])))
{
if ((islower(text[i])) && ((text[i] + k) > 'z'))
text[i] = ('a' + text[i] + k - 'z' - 1);
if ((isupper(text[i])) && ((text[i] + k) > 'Z'))
text[i] = ('A' + text[i] + k - 'Z' - 1);
else
text[i] = text[i] + k;
}
printf("%c", text[i]);
}
printf("\n");
return 0;
}
Result
caesar.exe 13
HELLO WORLD hello world
URYYB JBEYQ uryyk sknyq
This whole block;
if ((islower(text[i])) || (isupper(text[i])))
{
if ((islower(text[i])) && ((text[i] + k) > 'z'))
text[i] = ('a' + text[i] + k - 'z' - 1);
if ((isupper(text[i])) && ((text[i] + k) > 'Z'))
text[i] = ('A' + text[i] + k - 'Z' - 1);
else
text[i] = text[i] + k;
}
Can be reduced to:
if (islower(text[i]) || isupper(text[i]))
{
int base = islower(text[i]) ? 'a' : 'A';
int ord = text[i] - base; // normalize text[i] to be between [0-25]
ord = (ord + k) % 26; // rotate
text[i] = base + ord; // convert back to alphabet value
}
#include <stdio.h>
int main()
{
char text[1000], alpha;
int n;
printf("Please type in text:\n");
scanf("%[^\n]s", text);
printf("\nRotation number: "); // rotates letters to the right.
scanf("%d",&n);
printf("\n");
n = n % 26; // to wrap around alphabet.
int i = 0;
while (text[i] != '\0')
{
if((text[i] >= 'a' && text[i] <= 'z'))
{
alpha = text[i];
text[i] += n;
This is the part which I do not understand why it isn't working:
if(text[i] > 'z')
{
text[i] = 'a' + (n - (26 % (alpha - 'a')));
}
It works until the letter 'd'. 'f' just gives '\200'.
Any ideas as to why my code does not work?
}
i++;
}
printf("Encrypted text:\n%s", text);
return 0;
}
This part which you do not understand why is not working:
if(text[i] > 'z')
{
text[i] = 'a' + (n - (26 % (alpha - 'a')));
}
would be simply solved with
if(text[i] > 'z')
{
text[i] -= 26;
}
UPDATE you are working with char whick is probably signed, so adding the cipher, say, 20 to z will produce a number that is > 128, ie negative.
I suggest this amendment
int alpha; // changed from char
//...
alpha = text[i] + n;
if (alpha > 'z')
alpha -= 26;
text[i] = alpha;
I think what you want is
text[i] = (text[i] - 'a' + n) % 26 + 'a';
which does this
text[i] - 'a' // converts text[i] to a number between 0 and 25
+ n // add the cipher value
% 26 // wrap as necessary so the value is between 0 and 25
+ 'a' // convert back to a letter between 'a' and 'z'
So the loop should look like this
for ( int i = 0; text[i] != '\0'; i++ )
{
if ( text[i] >= 'a' && text[i] <= 'z' )
text[i] = (text[i] - 'a' + n) % 26 + 'a';
}