Caesar Cipher C++ (using char pointer and shift as arguments) - c++
I'm looking to make a method like so (which encrypts a message using Caesar Cipher, entered by the user and displays it):
void encrypt(char *message, int shift);
My code:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <string.h>
char num(char c)
{
const char upper_alph[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
const char lower_alph[26] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
if(isupper(c)) {
for(int i = 0; i < 26; i++)
if(upper_alph[i] == c)
return i;
} else {
for(int i = 0; i < 26; i++)
if(lower_alph[i] == c)
return i;
}
return 0;
}
void encrypt(char *message, int shift)
{
int i = 0;
const char upper_alph[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
const char lower_alph[26] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
while(message[i] != NULL)
{
if(isalpha(message[i]))
{
if(isupper(message[i])) {
printf("%c", upper_alph[(num(message[i])+shift)%26]);
} else {
printf("%c", lower_alph[(num(message[i])+shift)%26]);
}
} else {
printf("%c", message[i]);
}
i++;
}
}
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
int main()
{
//reverse();
//printf("\n\n");
int rc;
char mes[1024];
int sh = 0;
rc = getLine ("Enter message to be encrypted: ", mes, sizeof(mes));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", mes);
return 1;
}
encrypt(mes, 1);
fflush(stdin);
getchar();
return 0;
}
Thank you to anyone who helps or tries to help.
:)
EDIT: Made many corrections. Still not working :/
EDIT2: Made a lot more corrections. Getting an access violation # "while(*message != '\0')"
EDIT3: Updated the code above to the working code. Thank you everyone for your help!
One problem is you never wrap-around. Consider if you are passed something like 'Z' or 'z' with any positive shift, then you will just increment outside of the array.
You need to do something like:
upper_alph[(num(message[i])+shift)%26]
and
lower_alph[(num(message[i])+shift)%26]
You also need to allocate memory for mes:
char mes[1024];
I believe your scanf is also incorrect (c is a character, s is a string):
scanf("%s", mes);
Using %s will however only read until it gets white-space, a better option may be to read the entire line with getline().
You'll get an "index out of bounds" error on these lines:
if(isupper(message[i])) {
printf("%c", upper_alph[num(message[i])+shift]);
} else {
printf("%c", lower_alph[num(message[i])+shift]);
}
You need to calculate the index in advance and make sure it is between 0 and 25:
int shiftedIndex = (num(message[i]) + shift) % 26;
You are aware of the fact that your code only works with English as input language?
It doesn't work because you didn't allocate memory for mes:
char mes[512]; // Enough space!
Use std::string is easier:
string mes;
int sh = 0;
cout << "Enter message to be encrypted: " << endl;
getline(cin, mes);
cout << "Enter a shift amount (1-25): " << endl;
cin >> sh;
encrypt(mes, sh);
And change encrypt function to:
void encrypt(const string &message, int shift)
And keep your characters in range:
upper_alph[(num(message[i])+shift)%26]
lower_alph[(num(message[i])+shift)%26]
There is a fundamental problem here that the OP isn't understanding. And that is, to the computer, letters are just numbers. It us us humans that assign meaning to those numbers, and we can't even decide on which numbers mean what (see comments on question re ASCII, EBDIC and Unicode).
Here is a table showing how the ASCII standard maps the numbers to letters.
Notice that the character 'a' is 97, 'b' is 98, 'c' is 99 and so on. The uppercase characters start at 65 and go up from there. Note also that the letter 'a' and 'A' are on the same row! This means the bit patterns of the lower 5 bits for an upper case letter and a lower case letter are the same. Finally, as the computer only ever sees characters as numbers, it can do numeric operations on them:-
'd' - 'a' == 3
100 - 97
The second thing to note is that mathematically the Caeser cipher is just an addition with a modulo:-
encoded character = (plain text character + shift) mod 26
So now the code can written much more efficiently:-
void Encode (char *message, int shift)
{
while (*message)
{
char c = *message;
if (isalpha (c)) // check c is a letter
{
// get the letter index: this maps 'A' to 0, 'B' to 1, etc
// it also maps 'a' to 32 (97 - 65), 'b' to 33, etc
c -= 'A';
// this is 32 for lower case characters and 0 for upper case
char case_of_c = c & 32;
// map 'a' to 'A', 'b' to 'B'
c &= 31;
// the caeser shift!
c = (c + shift) % 26;
// restore the case of the letter
c |= case_of_c;
// remap the character back into the ASCII value
c += 'A';
// save the result of the shift
*message = c;
}
++message;
}
}
Related
How to pass an element of an array to an other?
I am trying to take a new plate for a car and change the old one. I need the three letters to be capitals. The thing is that I cannot pass the "capital" element of newPlate array to the new_Plate array. The program compiles but the answer is soometimes %^&#%#$ and some other times nothing. I know I have a problem in the pointers. void Car::set(char *newBrand, char *newPlate) { char new_Brand[80]; char new_Plate[8]; if(strlen(newPlate)==8) { for(int i=0;i<3;i++) { if(65<=(int)newPlate[i]<=90) { new_Plate[i]=newPlate[i]; // probably the problem } if(97<=(int)newPlate[i]<=122) { new_Plate[i]=(newPlate[i]+32); // probably the problem } cout<<new_Plate; } } }
Your new_Plate string does not include a zero terminator. Furthermore, 65<=(int)newPlate[i]<=90) is not valid in C++. You should write something like 'A'<=newPlate[i] && newPlate[i]<='Z')
The problem with the expression 65<=(int)newPlate[i]<=90 (aside from being unreadable, use more white spaces to make it more readable) is that it means ((65 <= (int) newPlate[i])) <= 90 which is not what it mathematically appears to mean. This expression will ALWAYS be true because 65 <= (int) newPlate[i] will evaluate to either 1 or 0 and it is of course always < 90. Also, to make the code even more readable use 'A' instead of 65 and the equivalent for any other character. Most programmers know that 'A' is 65 in ascii but you make them stop one or two seconds to realize that you really mean 'A'! Also, you must terminate c-strings with a '\0' so you need one extra character at the end or, cout << new_Plate will invoke undefined behavior. To print c strings the library will output characters from the input buffer until it finds the '\0', since it's not present in your buffer there is no predictable way to print it. Check this, do you understand the changes? #include <iostream> #include <cstring> using namespace std; class Car { public: Car(); void set(const char *const newBrand, const char *const newPlate); }; Car::Car() { } void Car::set(const char *const newBrand, const char *const newPlate) { char new_Brand[80]; char new_Plate[9]; size_t length; size_t i; (void) new_Brand; length = strlen(newPlate); for (i = 0 ; ((i < length) && (i < sizeof(new_Plate) - 1)) ; ++i) { if (('A' <= newPlate[i]) && (newPlate[i] <= 'Z')) { new_Plate[i] = newPlate[i]; } else if (('a' <= newPlate[i]) && (newPlate[i] <= 'z')) { new_Plate[i] = (newPlate[i] - 32); } } new_Plate[i] = '\0'; cout << '*' << new_Plate << '*' << endl; } int main(void) { Car car; car.set("audi", "example text"); return 0; }
C/C++ reading line at a time
I was trying to solve a programming problem of some site and this one had the following statement: Read a string and parse it as a number, char 'l' can be considered as number 1 and chars 'o' and 'O' can be considered as number 0, commas and spaces will be accepted in the input but ignored, if any other character is found then output error... So... since there can be spaces in the lines, I used gets (the documentation says it removes the new line and puts a terminator)... My sequence of IF test if it is a number, then if its an acceptable letter, then checks if it is not a comma or a space... And I found out that it was almost always entering in the last IF even though there wasn't any character that should lead it there so I changed the printf inside it to print the printf("%d error", st[k]); And it outputs 13: carriage return... I tried this compiler here #include <cstdio> #include <cstring> #include <cstdlib> int main() { char st[100]; char buf[100]; int k, i; long long int num; ops: while(gets(st)) { for(k = i = 0; st[k]; k++) if(st[k] >= '0' && st[k] <= '9') buf[i++] = st[k]; else if(st[k] == 'o' || st[k] == 'O') buf[i++] = '0'; else if(st[k] == 'l') buf[i++] = '1'; else if(st[k] != ',' && st[k] != ' ') { printf("error\n"); goto ops; } // remaining code comes here... } The input sample had the following lilnes: lo6 234,657 hi ,,,,,5,,5, 4 2200000000 00 Should I use other function to read instead? Any suggestions on how to avoid this damn Carriage Return? The statemente for the problem can be seen here if you want more detail Thanks EDIT: I'm asking that because there seem to be a difference between the compiler I'm using and the compiler the website was using, once I submitted a code that wasn't generating the correct output on mine but I thought the code was correct... and it passed. Then after it, I tried the code on a linux virtual machine and also correct but my gcc on windows failed... some characters were completely away from where they should be The thing is:
The preferred method of line input is getline. If you do not know the width of the input beforehand, getline will allocate space for you if you set the buffer pointer to NULL. Since you indicate you have some experience in C, the following will show you how to step through an input string and parse it in the manner you want. While there are many ways to handle parsing strings, it is hard to beat assigning a pointer to the beginning of the string and then advancing down the string until you reach the null-terminating character (unsigned 0, or char '\0'). If you have any questions after looking it over, just ask: #include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { char *buffer = NULL; size_t n = 0; ssize_t len; char *ptr = NULL; char *output = NULL; int idx = 0; printf ("\nEnter your string below: ([ctrl + d] on blank line to end)\n"); while ((len = getline (&buffer, &n, stdin)) != -1) { ptr = buffer; idx = 0; output = malloc (sizeof (char) * len); while (*ptr != 0) { if (*ptr >= 48 && *ptr <= 57) output [idx++] = *ptr; if (*ptr == 'I') output [idx++] = '1'; if (*ptr == 'o' || *ptr == 'O') output [idx++] = '0'; ptr++; } output [idx] = 0; printf ("\n valid output: %s\n\n", output); free (output); } return 0; } output: Enter your string below: ([ctrl + d] on blank line to end) This sting has 12345 aeiou AEIOU,,,,,commas, and 99, to end. valid output: 123450100990
function for string statistics
The task is following. Implement function char* stringstat(const char* s) which would count the number of characters, digits, space characters, and nonprintable characters and would output the result string in form of: characters: w digits: x spaces: y non-print: z, where w,x,y and z are the corresponding quantities. So far i have gathered this raw material. #include <stdio.h> #include <string.h> using namespace std; int main(void) { char str[]="something.-21"; int length = strlen(str); printf("The ASCII value of %c is %d ",character,storeAscii); if (storeAscii>=65 && storeAscii<=90) { printf("\nYou have entered a capital letter"); } else if (storeAscii>=97 && storeAscii<=122) { printf("\nYou have entered a small letter"); } else if (storeAscii>=47 && storeAscii<=57) { printf("\nYou have entered a digit "); } else if (storeAscii>=0 && storeAscii>=47 || storeAscii>=54 && storeAscii<=64 || storeAscii>=91 && storeAscii<=96 || storeAscii>=123 && storeAscii<=127) { printf("\nYou have entered a special character"); } return 0; } I know that I must have "for" cycle which checks every symbol in string and pending on the symbol adds count++ and then I can output the quantities. I really dont know how to make the cycle check for strings. Thank you very much timrau The final product corresponding with the task is this: #include<stdio.h> #include<string.h> #include <cctype> int main() { char str[]="This sentence is messed up. It has 32413523."; int w = strlen(str); int x=0,y=0,z=0; for(char *ptr = str; *ptr!='\0';++ptr) { if(isdigit(*ptr)){x++;} else if(isblank(*ptr)){y++;} else if(isprint(*ptr)){z++;} } printf("In sentence \"%s\" there is:\n",str); printf("Characters: %d\n",w); printf("Digits: %d\n",x); printf("Space characters: %d\n",y); printf("Non-printable characters: %d\n",z); return 0; }
#include <cctype> for (char *ptr = str; *ptr != '\0'; ++ptr) { if (isupper(*ptr)) { /* upper case */ } else if (islower(*ptr)) { /* lower case */ } else if (isdigit(*ptr)) { /* decimal digit */ } else { /* special */ } }
Write a string to file not equal to string that read from it
Phase 1 example 1: I have string text = "01100001" then I want write to file "a" example 2: I have string text = "0110000101100010" So I want write to file "ab" NOTE:I solved phase 1 and result of writing is true. Phase 2 for example 1: I want read the file and put it to temp. So temp = "a" and i convert it to "01100001" for example 2: I want read the file and put it to temp. So temp = "ab" and i convert it to "0110000101100010" Question in my code i have below input string text ="00000110101011100010001011111110011011110101100101110101101111010111111110101011" "00111011000011100011100000100010111110111110111001100001110001110000101001111010" "00000101"; I did "phase 1" and I opened the file in a hex editor the writing is true. But after doing "phase 2" temp != text. Why? My code #include <iostream> #include <sstream> #include <vector> #include <fstream> #include <string> #include <stdlib.h> using namespace std; class bitChar{ public: unsigned char* c; int shift_count; string BITS; bitChar() { shift_count = 0; c = (unsigned char*)calloc(1, sizeof(char)); } string readByBits(ifstream& inf) { string s =""; while (inf) { string strInput; getline(inf, strInput ); for (int i =0 ; i < strInput.size() ; i++) { s += getBits(strInput[i]); } } return s; } void setBITS(string X) { BITS = X; } int insertBits(ofstream& outf) { int total = 0 ; while(BITS.length()) { if(BITS[0] == '1') *c |= 1; *c <<= 1; ++shift_count; ++total; BITS.erase(0, 1); if(shift_count == 7 ) { if(BITS.size()>0) { if(BITS[0] == '1') *c |= 1; ++total; BITS.erase(0, 1); } writeBits(outf); shift_count = 0; free(c); c = (unsigned char*)calloc(1, sizeof(char)); } } if(shift_count > 0) { *c <<= (7 - shift_count); writeBits(outf); free(c); c = (unsigned char*)calloc(1, sizeof(char)); } outf.close(); return total; } string getBits(unsigned char X) { stringstream itoa; for(unsigned s = 7; s > 0 ; s--) { itoa << ((X >> s) & 1); } itoa << (X&1) ; return itoa.str(); } void writeBits(ofstream& outf) { outf << *c; } ~bitChar() { if(c) free(c); } }; int main() { ofstream outf("ssSample.dat",ios::binary); string text ="00000110101011100010001011111110011011110101100101110101101111010111111110101011" "00111011000011100011100000100010111110111110111001100001110001110000101001111010" "00000101"; cout<< text<<endl; //write to file bitChar bchar; bchar.setBITS(text); bchar.insertBits(outf); outf.close(); ifstream inf("ssSample.dat" ,ios::binary); //READ FROM FILE string temp=bchar.readByBits(inf); cout << endl; cout << temp << endl; return 0; }
You have a LF Line Feed character. This is the character that is getting omitted. 0000 1010 This may be unrelated, but Windows requires a CR and LF for a new line. This code may act differently in Windows vs. Unix. Read one byte at a time. string readByBits(ifstream& inf) { string s =""; char buffer[1]; while (inf.read (buffer, 1)) { // string strInput; //getline(inf, strInput ); //for (int i =0 ; i < strInput.size() ; i++) //{ s += getBits(*buffer); //} } return s; } Program output: 000001101010111000100010111111100110111101011001011101011011110101111111101010110011101100001110001110000010001011111011111011100110000111000111000010100111101000000101 000001101010111000100010111111100110111101011001011101011011110101111111101010110011101100001110001110000010001011111011111011100110000111000111000010100111101000000101
One problem with your approach is that your text must be a multiple of 8 bits to work. Otherwise, even if everything is correct, that last character will be read from the file and converted to 8 binary digits in the string adding trailing zeros.
Two problems I quickly identified (but I assume there are more) Your input is not a multiple of 8-bits By using getLine you're reading until you meet a delimiting character and thus spoiling your result since you're not dealing with a text-based file
a simple getch() and strcmp problem
I have this simple problem that gets an input from the user using a function then checks if the input is 'equal' to the "password". However, strcmp would never return my desired value, and the culprit is somewhere in my loop that uses getch() to take each character separately and add them to the character array. I found this out by having printf display the character array. If I type in pass word, the function would display it as pass word ". I have no idea on why the closing double quote and a whitespace was included in the array right after the word I typed in. Any idea? Here's the code. Thanks. #include <stdio.h> #include <iostream> #include <conio.h> #include <string.h> int validateUser(); int main() { for(int x = 0;x<2;x++) { if(validateUser()) { system("cls"); printf("\n\n\t\t** Welcome **"); break; } else { system("cls"); printf("\n\n\t\tIntruder Alert!"); system("cls"); } } system("PAUSE>nul"); return 0; } int validateUser() { char password[9]; char validate[] = "pass word"; int ctr = 0, c; printf("Enter password : "); do { c = getch(); if(c == 32) { printf(" "); password[ctr] = c; } if(c != 13 && c != 8 && c != 32 ) { printf("*"); password[ctr] = c; } c++; }while(c != 13); return (!strcmp(password, validate)); }
Your char array password does not have a terminating null char. You need to ensure that you don't stuff more than 8 char into password Also c++ should be ctr++ . do { // stuff char into password. ctr++; }while(c != 13 && ctr <8); password[ctr] = 0;
You're incrementing c in your loop. You should be incrementing ctr. Also, all the stuff everyone else has said (null terminator, only 8 characters, etc).
getch() is a function defined in a non-standard header <conio.h>. Relying on non-standard features is not recommended when you want your code to be portable. :)
do { // stuff char into password. ++ctr; } while(c != 13 && ctr < 9); password[ctr] = '\0';