Shift deciphering tool (cesar cipher) - c++

I have built a Program to decipher shift ciphers (Cesar Cipher). It seems to take the Input and makes a output file but it is empty. I am thinking that something may be wrong with the Deciphering function and have tried changing multiple things to no avail. I use Bloodshed C++ for compiling and windows for a OS.
Thanks
kd7vdb
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
using namespace std;
const int ARRAYSIZE = 128;
void characterCount(char ch, int list[]);
void calcShift( int& shift, int list[]);
void writeOutput(ifstream &in, ofstream &out, int shift);
int main()
{
int asciiCode = 0,
shift = 0;
string filename;
char ch;
ifstream infile;
ofstream outfile;
//input file
cout << "Input file name: ";
getline(cin, filename);
infile.open(filename.c_str());
if (!infile.is_open()) {
cout << "Unable to open file or it doesn't exist." << endl;
return 1;
}
//output file
cout << "Output file name: ";
getline(cin, filename);
outfile.open(filename.c_str());
int list[ARRAYSIZE] = {0};
while (infile.peek() != EOF)
{
infile.get(ch);
characterCount(ch, list);
}
infile.clear();
infile.seekg(0);
calcShift (shift, list); //Calculate the shift based on the <strong class="highlight">most</strong> characters counted
writeOutput(infile, outfile, shift); //Decypher and write to the other document
return 0;
}
void characterCount(char ch, int list[])
{
if (ch >= 'A' && ch <= 'z') //If the character is in the alphabet...
{
int asciiCode = 0;
asciiCode = static_cast<int>(ch); //Change it to the ASCII number
list[asciiCode]++; //And note it on the array
}
}
void calcShift( int& shift, int list[])
{
int maxIndex = 0, //Asuming that list[0] is the largest
largest = 0;
for (int i = 1; i < ARRAYSIZE; i++)
{
if (list[maxIndex] < list[i])
maxIndex = i; //If this is true, change the largest index
}
largest = list[maxIndex]; //When the maxIndex is found, then that has the largest number.
if (largest >= 65 && largest <= 90) //Calculate shift with <strong class="highlight">E</strong> (for upper-case letters)
shift = largest - 69;
if (largest >= 97 && largest <= 122) //For lower-case letters (<strong class="highlight">e</strong>)
shift = largest - 101;
}
void writeOutput(ifstream &infile, ofstream &outfile, int shift)
{
char ch;
int asciiCode = 0;
while (infile.peek() != EOF) { //Until it is the end of the file...
infile.get(ch); //Get the next character
if (ch >= 'A' && ch <= 'z') //If the character is in the alphabet...
{
asciiCode = static_cast<int>(ch); //Change it to the ASCII number
asciiCode += shift; //Do the shift
ch = static_cast<char>(asciiCode); //Change it to the shifted letter
}
outfile << ch; //Print to the outfile
}
}

You have a couple of logical errors. I think your last two functions should be more like:
void calcShift( int& shift, int list[])
{
int maxIndex = 0; //Asuming that list[0] is the largest
for (int i = 1; i < ARRAYSIZE; i++)
{
if (list[maxIndex] < list[i])
maxIndex = i; //If this is true, change the largest index
}
if (maxIndex >= 'A' && maxIndex <= 'Z') //Calculate shift with <strong class="highlight">E</strong> (for upper-case letters)
shift = 'E' - maxIndex;
if (maxIndex >= 'a' && maxIndex <= 'z') //For lower-case letters (<strong class="highlight">e</strong>)
shift = 'e' - maxIndex;
}
void writeOutput(ifstream &infile, ofstream &outfile, int shift)
{
char ch;
while (infile.peek() != EOF) { //Until it is the end of the file...
infile.get(ch); //Get the next character
if (ch >= 'A' && ch <= 'Z') //If the character is in the alphabet...
{
ch = 'A' + (((ch - 'A') + shift + 26) % 26);
}
if (ch >= 'a' && ch <= 'z') //If the character is in the alphabet...
{
ch = 'a' + (((ch - 'a') + shift + 26) % 26);
}
outfile << ch; //Print to the outfile
}
}
To summarise, you don't need largest in calcShift, you need to subtract maxIndex from 'E' or 'e' to calculate the shift, and your calculation of the substituted char in writeOutput was pretty far off.
I'm not sure why you were getting an empty output file, but this works for me using MSVC.

It seems to me you forget to ofstream::close your output file.
http://www.cplusplus.com/reference/iostream/ofstream/close/

There's a few serious problems I noticed.
1) You're not wrapping around when shifting. So if shift = 20 and you get the character 'y', when you do this:
asciiCode = static_cast<int>(ch); //Change it to the ASCII number
asciiCode += shift; //Do the shift
ch = static_cast<char>(asciiCode);
asciiCode becomes 'y' + 20 == 141, which is off the end of standard 128 bit ascii. You need to take the number mod, I guess, 128. So:
asciiCode = static_cast<int>(ch); //Change it to the ASCII number
asciiCode += shift; //Do the shift
asciiCode %= 128; // Wrap around
ch = static_cast<char>(asciiCode);
2) But this still doesn't address why you're only shifting alphabetic characters, yet shifting those letters to non-letter characters in the result.
And why are you counting them in an array of size 128, instead of 52? Or just 26?
Even more so, why are you generating the shift value in-code? You need to know that value to decode later...
In short, I reccommend you do it this way:
Translate characters to the numbers 0-51, rather than 0-127 (or 0-25)
When applying the shift, add it to the 0-51 value, then take that % 52 to get the new character.
Consider manually inputting the shift value.
This is how a Ceasar Cypher generally works.
3) As to the printing problem, have you tried debugging?
I can't speak about Bloodshed C++, but even just adding a cout statement would be really helpful. For instance:
std::cout << ch; //Print to cout for debugging
outfile << ch; //Print to the outfile
would let you know if the error is in the outfile processing.

Related

Why does the program print 0 instead of the average?

Wrote a function that calculates the average length of words in a sentence.
Why does the program print 0 instead of the average?
Please help me fix my mistake.
If you know how to make an implementation in one function, please write.
#include <iostream>
#include <string>
using namespace std;
int CountWordsAndLetters(char* str, int& words, int& letters)
{
words = 0;
int i = 0;
letters = 0;
while (str[i] == ' ')
i++;
for (; str[i]; i++) {
if (((str[i] >= 'a') && (str[i] <= 'z'))
|| ((str[i] >= 'A') && (str[i] <= 'Z')))
letters++;
if (str[i] == ' ') {
words++;
while (1)
if (str[i] == ' ')
i++;
else {
i--;
break;
}
}
}
words = words + 1;
return (words);
}
float AverageLetters(float words, float letters)
{
float a = (double)(letters / words);
return a;
}
int main()
{
char array[255];
int words = 0;
int letters = 0;
cout << "Enter the string\n\n";
gets_s(array);
int size;
for (size = 0; array[size]; size++)
;
char* str = new char[size];
CountWordsAndLetters(str, words, letters);
cout << "\nAverage number of letters per word: "
<< AverageLetters(words, letters);
return 0;
}
If you know how to make an implementation in one function, please write.
Here, you are allocating an uninitialized array of char:
char* str = new char[size];
You put nothing in it.
You then pass it to CountWordsAndLetters:
// here -------v
CountWordsAndLetters(str, words, letters);
You should consider simply sending array instead:
CountWordsAndLetters(array, words, letters);
Here's a live example of your code working.

Only read numbers out of users input

I'm trying to write a function which only reads four ints out of a users input like this: ewzge242jfdsiii23 So it is supposed to save only 2422.
This is my code and it just gives me some weird output, if I let it cout number.
Can you maybe see my mistakes and explain why I can't do it how I did and what I could do instead? Thanks a lot!
int readnumber ( ) {
char kar, ont_kar, ont_ont_kar;
int number;
while (kar != '\n' ){
cin.get (kar);
if (kar >= '0' && kar <= '9') {
old_kar=kar;
old_kar = old_kar*10 + (kar - '0');
old_old_kar = old_kar ;
} //if
} //while
if (old_kar < 9999) {
number=old_kar;
}//if
else {
number=old_old_kar;
}//else
}//readnumber
This looks too complicated, why do you need so many variables?
Also old_kar and old_old_kar are misstyped. The function does not return, that should be the main problem.
Here's a quick simple example:
unsigned readnumber(int number_of_chars) {
char ch;
unsigned number = 0;
while (number_of_chars > 0) {
std::cin.get(ch);
if ('\n' == ch)
break; // Stop on new line
if (ch < '0' or ch > '9')
continue; // Skip non-digits
--number_of_chars; // One digit processed
number = number * 10 + ch - '0'; // And added to the result
}
return number;
}
And here is a full version without break or continue:
#include <iostream> // std::cin, std::cout
#include <fstream> // std::ifstream
using namespace std;
int readnumber(int number_of_chars) {
char ch;
int number = 0;
while (number_of_chars > 0) {
std::cin.get(ch);
if ('\n' == ch)
return number;
if (ch >= '0' and ch <= '9') {
--number_of_chars;
number = number * 10 + ch - '0';
}
}
return number;
}
int main() {
int n = readnumber(4);
cout << "You entered: " << n << endl;
return 0;
}
NB: Always compile with all warnings on, this will save you a lot of time.

Read a text file and store into two different arrays: a string array and float array

I'm having trouble storing my code into two different arrays. The following text file contains a format like this: name,1,2,3,4,5anothername,6,7,8,9,10...
For example it could be something like this, I'll name it test.txt:
Drake,1,2,3,4,5
Kanye West,6,7,8,9,10
Ka,11,12,13,14,15,16
Young Thug,17,18,19,20
Kendrick Lamar,21,22,23,24,25
Here is my code so far:
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
int main(){
ifstream inputFile;
inputFile.open("test.txt");
if (inputFile.fail()){
cout << "File open error!" << endl;
return -1;
}
string namesarray[25]; //array used to store names
float numbersarray[25]; //array used to store numbers
string line; //string used to read text file
int i = 0; //counter for entire text input
int j = 0; //counter for namesarray
int k = 0; //counter for numbersarray
while(getline(inputFile, line, ',')){
if (line[i] >= 'A' && line[i] <= 'Z'){
namesarray[j] = line;
j++;
}
else{
numbersarray[k] = stof(line);
k++;
}
i++;
}
inputFile.close();
}
My problem is that I can't store all the names into the string array, but the numbers are stored into the float array just fine. My code only stores in the first name and then a random number. For example if I create a loop to check if the names or the numbers are stored in correctly
for (int a = 0; a < 25; a++){
cout << numbersarray[a] << endl;
}
The numbers are stored in fine but checking for the names it doesn't store all the names in there. If I'm checking for the first letter of the line shouldn't it store the name in there if it contains a letter? I don't want to use isalpha() because it still outputs the same problem.
for (int a = 0; a < 25; a++){
cout << namesarray[a] << endl;
}
With getline() the third parameter does not eliminates commas. It specifies the delimeter until which the function fills your string. So according to your input file, it is going to get the names.
I can not find the use of the i++;. You always have to look for line[0].
I suggest using isdigit(line[0]) to check either it is a number or a character
The problem with your code is that you are checking only for upper case letters.
Upper case letters exist only in the first character of the string with the name.
So when i++; goes to i>0, line[i] stops to be an upper case, and becomes a lower case.
Also I want you to know that stof works only in c++11
the getLine function's delimiter is a comma, so when it gets to the last number for each name, it reads the number and the name in one string. To fix it, consider something like:
bool letter = true;
bool letterNum = false;
int index = 0;
if((line[0] >= '9' && line[0] >= '0') || line[0] == '.') {
letter = false;
}
for(int l = 0; l < line.length()) {
if((letter && ((line[l] >= '9' && line[l] >= '0') || line[l] == '.')) || (!letter && !((line[l] >= '9' && line[l] >= '0') || line[l] == '.'))) {
index = l;
l = line.length();
}
}
if(letter && index == 0) {
namesarray[j] = line;
j++;
i++;
}
if(!letter && index == 0) {
numbersarray[k] = atoi(line);
k++;
i++;
}
if(letter && index != 0) {
namesarray[j] = line.substr(0, index);
numbersarray[k] = stof(line.substr(index, line.length() - index));
j++;
k++;
i += 2;
}
if(!letter && index != 0) {
numbersarray[k] = stof(line.substr(0, index));
namesarray[j] = line.substr(index, line.length() - index);
j++;
k++;
i += 2;
}
Instead of:
if (line[i] >= 'A' && line[i] <= 'Z'){
namesarray[j] = line;
j++;
}
else{
numbersarray[k] = stof(line);
k++;
}
i++;
Inside the while loop. You could also use ifstream >> string, because that reads one line at a time, and you could just parse each substring in between the commas. That may look something like:
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
int main(){
ifstream inputFile;
inputFile.open("test.txt");
if (inputFile.fail()){
cout << "File open error!" << endl;
return -1;
}
string namesarray[25]; //array used to store names
float numbersarray[25]; //array used to store numbers
string line; //string used to read text file
int i = 0; //counter for entire text input
int j = 0; //counter for namesarray
int k = 0; //counter for numbersarray
while(!inputFile.eof()){
string data;
inputFile >> data;
int lastIndex = 0;
for(int l = 0; l < data.length(); l++) {
if(data[l] == ',' || l == data.length() - 1) {
line = data.substr(lastIndex, l - lastIndex);
lastIndex = l + 1;
if (((line[l] >= '9' && line[l] >= '0') || line[l] == '.')){
numbersarray[k] = stof(line);
k++;
}
else{
namesarray[j] = line;
j++;
}
i++;
}
}
}
inputFile.close();
}
However, this only works when each name and it's numbers are separated by a \n. If they are, this is the better option. But if they aren't, use the first method.
This is better handled using std::getline() to read an entire line, and then std::istringstream to parse each line, eg:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
ifstream inputFile;
inputFile.open("test.txt");
if (!inputFile)
{
cout << "File open error!" << endl;
return -1;
}
string namesarray[25]; //array used to store names
float numbersarray[25*5]; //array used to store numbers
string s; //string used to read text file
int i = 0; //counter for entire text input
int j = 0; //counter for namesarray
int k = 0; //counter for numbersarray
while (getline(inputFile, s))
{
istringstream iss(s);
getline(iss, namesarray[j], ',');
++j;
while (getline(iss, s, ','))
{
numbersarray[k] = stof(s);
++k;
}
++i;
}
inputFile.close();
return 0;
}
Why in else statement is i++? You must check the i variable first element of array be but not the addition of it.

How can I correctly encrypt the input when there is a space?

#include <iostream>
#include <cstring>
#include <string>
//#include <cctype>
using namespace std;
string k;
int key[1024];
int size = 0;
int count = 0;
char text[1024];
char o;
void encrypt(char input[]);
void decrypt(char input[]);
// get arguments from command line
int main(int argc, char *argv[])
{
if(argc >= 3)
{
k = argv[2];
// removing spaces from key
// storing key into char array
for(int i = 0; i < k.length(); i++)
{
if(k.at(i) == ' ')
{
key[i] = k.at(i+1);
i++;
}
else
{
key[i] = k.at(i);
// cout << key[i] << endl;
}
size++;
}
if(strcmp(argv[1], "-e") == 0)
{
// get text from user
// encrypt
cout << "encryption " << endl;
cin.getline(text, sizeof(text));
encrypt(text);
}
else if(strcmp(argv[1], "-d") == 0)
{
// get text from user
// decrypt
cout << "decryption " << endl;
decrypt(text);
}
}
}
void encrypt(char input[])
{
string word = input;
char wkey[word.length()];
// cout << word.length();
for(int i = 0; i < word.length(); count++, i++)
{
// if input is larger than the key
// chars of key will repeat until the size is the length of the input
if(i > size - 1)
{
if(i == size)
{
count = 0;
}
wkey[i] = wkey[count];
}
else
{
wkey[i] = key[i];
}
// if input is a space, cout a space
if(input[i] == ' ')
{
cout << " ";
}
// if input is something other than a letter, then just print it out
else if(input[i] < 65 || input[i] > 122 || input[i] > 90 && input[i] < 97)
{
cout << input[i];
}
else
{
// ignoring case of the letters in the key
// give the letters a range of 0-25
if(wkey[i] >= 65 && wkey[i] <= 90)
{
wkey[i]-= 'A';
}
else if(wkey[i] >= 97 && wkey[i] <= 122)
{
wkey[i]-= 'a';
}
// cout << wkey[i] << endl;
// if input is uppercase, put it in the range of 0-25
// make shift by adding to char in key
// mod by 26 to wrap around
// o is the encrypted character that will be printed
if(input[i] >= 65 && input[i] <= 90)
{
o = ((wkey[i] + (input[i] - 'A')) % 26) + 'A';
}
else if(input[i] >= 97 && input[i] <= 122)
{
o = ((wkey[i] + (input[i] - 'a')) % 26) + 'a';
}
}
cout << o;
}
}
The problem is that I am having trouble encrypting plaintext if that text contains a space. If the text is just one single word, then the program works. In the encryption function where I test the input for a space, I just print out a space, and then the next iteration of the for loop occurs. I think that this problem is occurring because once that next iteration occurs, the character in the key that is at the same index as the space in the input is skipped. I've tried doing an if statement to roll back to the skipped letter in the key, but I still get the wrong output. If anyone could give me some advice on how to fix this problem, I would really appreciate it.

Stop accepting input after 20 chars

I am trying to understand how to use cin.get(). I would like to only accept 20 characters of input, but any method I've tried using to detect further input stop
I am accepting input one character at a time and inputting it into an array like so:
void getInput(char arr[], int size) {
char number = '0';
for (int i = 0; i < size + 1; i++) {
char number;
cin.get(number);
if ((number >= '0') && (number <= '9')) {
arr[i] = number;
}
if ((number == ' ') || (number == '\n')) {
break;
}
}
}
I've tried doing something like this, but you can see that my understanding is not excellent:
if (number == '\n') {
done == true;
}
and wrapping the whole thing in a while (!done) for example, but that doesn't work in situations where I input 20 characters or less.
You could initialise a variable int count= 1;. With each iteration you can increment it with count++;and put the check
if (count==20)
break;
I hope this helps.
You could round down the passed size parameter to 20 if it's larger than 20. Then the for-loop will break at most after 20 iterations.
#include <iostream>
using namespace std;
void getInput(char arr[], size_t size) {
const size_t MAX_SIZE = 20;
if (MAX_SIZE < size) {
// 'round down' size if it's too big
size = MAX_SIZE;
}
char number = '0';
for (unsigned int i = 0; i < size; i++) {
cin.get(number);
if ((number >= '0') && (number <= '9')) {
arr[i] = number;
} else if ((number == ' ') || (number == '\n')) {
break;
}
}
}
int main() {
char arr[32] = {};
// even though arr could fit 32 chars
// only MAX_SIZE=20 numbers at most will be accepted
getInput(arr, 32);
cout << arr << endl;
return 0;
}
I'm assuming size is the length of arr (can't yet comment so could not ask this), if so then watch out for doing i < size + 1; that let's i take values from 0 to size inclusive, and then the for-loop eventually reaches arr[size] which is off the end of the array. Use i < size instead.