Error implementing Caesar Cipher Using C++ - c++

I am trying to implement Caesar Cipher using C++. The directions are to use this file which is already encrypted:
5
Asi ymj rtrjwfymjx tzylwfgj.
Aqq rnrxd bjwj ymj gtwtlwtajx
Dni ldwj fsi ldrgqj ns ymj bfgj.
Tbfx gwnqqnl fsi ymj xnymjd ytajx
The number 5 represents the shift that is applied to the text. I have to decode the Caesar ciphered text and reverse the lines as in put line 4 in line 1's position and line 3 in line 2's. The first letter of each line does not need to be decoded (the uppercase letters).
The text should look like this after running the program:
Twas brillig and the sithey toves
Did gyre and gymble in the wabe.
All mimsy were the borogroves
And the momerathes outgrabe.
As of right now, I have this code:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
using namespace std;
char decipher (char c, int shift);
int main(){
//declare variables
char c;
string deciphered = "";
int shift;
vector <string> lines;
//ask for filename and if not found, keep trying
ifstream inFile;
string filename;
cout << "What is the name of the file? ";
cin >> filename;
inFile.open(filename);
while (!inFile){
cout << "File not found. Try again: ";
cin >> filename;
inFile.open(filename);
}
//find shift from file
inFile >> shift;
//get lines from file
inFile >> noskipws;
while (inFile >> c){
char decipheredChar = decipher (c, shift);
deciphered += decipheredChar;
}
cout << deciphered;
}
char decipher (char c, int shift){
string letters = "abcdefghijklmnopqrstuvwxyz";
if (c == 'T'){
return c;
}
else if (c == 'D'){
return c;
}
else if (c == 'A'){
return c;
}
else if (c == ' '){
return c;
}
else {
int currentPosition = letters.find(c);
int shiftedPosition = currentPosition - shift;
if (shiftedPosition < 0){
shiftedPosition = 26 + shiftedPosition;
}
char shifted = letters[shiftedPosition];
return shifted;
}
}
The result I'm getting is this:
uAnd the momerathes outgrabeuuAll mimsy were the borogrovesuDid gyre and gymble in the wabeuuTwas brillig and the sithey tovesu
How do I get rid of the u's and also separate the words by line? I have an idea of reversing the lines using a vector and using a loop counting backwards but I'm not sure how to get to there yet. Please help. Thank you.

To answer your question, the 'u's are the newlines. You read them in and decipher them, so they change and the result is pulled from letters. You should be able to add another case to decipher() to leave newlines alone:
char decipher (char c, int shift){
string letters = "abcdefghijklmnopqrstuvwxyz";
if(c == '\n'){ // do not modify new lines.
return c;
}
else if (c == 'T'){
return c;
}
// ...
}
Probably the cleanest way to reverse the lines is parse them while you read the characters. You can them pop them from the vector in reverse order. A working (but not robust) example would be to add the following to your while loop:
while (inFile >> c){
char decipheredChar = decipher (c, shift);
deciphered += decipheredChar;
if(decipheredChar=='\n'){ //if full line
lines.push_back(deciphered); //push line
deciphered = ""; //start fresh for next line
}
}
lines.push_back(deciphered+'\n'); //push final line (if no newline)
while(!lines.empty()){
cout << lines.back(); //prints last line
lines.pop_back(); //removes last line
}
I say not robust because there are minor things you may still need watch out for. For instance, this reads stores newline from after 5, and if the file ends in a newline I've added an empty one on the end... I'll leave you minor details to clear up.

Related

C++ I don't know how to find a word(eg.banana,sandwich) inside of a string(sentence) THE USER ENTERS the sentence and then write that word out

I've tried this but I'm stuck honestly.
I'm trying to find the first character, and then search for the ending of that substring (for eg. if the word is "sandwich" and it finds 's' that it figures out that its "sandwich") and then write out the word sandwich. And also I'm new to C++.
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s, word;
char a;
cout << "Enter the sentence that you desire: ";
getline(cin, s);
cout << "Enter the letter that you want: ";
cin >> a;
for (int i = 0; i < s.length; i++)
{
if (s[i] == a)
{
if (s[i] == '\0')
{
word = s;
cout << word;
}
}
}
return 0;
}
The request is a bit foggy but given also the code you posted, i think i got a heck of what you intend to do.
The easiest (yet not necessarily the most performing one) is to use a stringstream, more precisely an istringstream.
You basically build it with a string (the one you passed from keyboard) and then you use it as if it was your cin (it acts as a normalized istream).
At that point you can iterate each word of the sentence and check the first letter.
The first character of a string is either myString[0] or myString.front(). That is up to you.
the code should look like this :
#include <iostream> //cin/cout
#include <sstream> //istringstream
using namespace std ;
int main()
{
//first of all let's get our sentence AND the character you want
cout << "insert sentence here: " ;
string sentence ;
getline(cin, sentence) ;
cout << "insert the character here: " ;
char letter ;
cin >> letter ;
//then let's create an istringstream with said sentence
istringstream sentenceStream(sentence) ;
//let's then iterate over each word
string word ;
while(sentenceStream >> word)
{
//and see if the word starts with the letter we passed by keyboard
if(word.front() == letter)
{
cout << "the word \"" << word << "\" starts with '" << letter << "'\n" ;
}
}
return 0 ;
}
Just a couple of hints:
iostream includes string already, there is no need to re-include it.
[Edit] (as pointed out by whozcraig, this does not follow the standard. guards will "negate" the double inclusion anyway, so yes, including string is not a mistake. as specified in the comment, i'm yet to find an implementation of iostream that does not include string)[/Edit]
It is good practice not to call a variable 's', or 'a': use a name
that makes it recognizable.
You can find the end of a word with std::find_if:
#include <algorithm>
#include <string>
template <typename Is>
std::string find_word(Is& stream, char needle) {
auto const nonword = [](char c) {
if ('a' <= c && c <= 'z') return false;
if ('A' <= c && c <= 'Z') return false;
if (c == '-') return false;
return true;
};
for (std::string w; stream >> w;) {
if (w.size() && w[0] == needle) {
auto const last = std::find_if(std::begin(w),std::end(w),nonword);
return std::string(std::begin(w),last);
}
}
return "";
}
This takes any stream as argument, including std::cin, and can be invoked like this:
std::cout << find_word(std::cin,'w') << "\n";
It is important to specifically find the last character in each chunk handed you by the stream because the streams will only cut along whitespace by default. So if you enter a sentence:
Hello world!
You want the end of the word to be 'd', not '!'.

Odd Cout Behavior

I'm sorry for the initial post. This is tested and reproducible.
I'm trying to get cout to work within a fstream while loop while detecting each character its parsing, but it's exhibiting an odd behavior with the Text getting overrided by the first variable that I'm trying to put into cout.
main.cxx
#include <string.h>
#include <fstream>
#include <iostream>
#include <stdio.h>
using std::string;
using std::fstream;
using std::noskipws;
using std::cout;
int main(int argc, const char *argv[]){
char pipe;
string word; // stores the word of characters it's working on at the moment
string filename = "Directory.dat";
int type = 0; // 2 types counter, starts at 0
int newindicator = 0; // for detecting a new * for the data set
fstream fin(filename.c_str(), fstream::in);
while(fin >> noskipws >> pipe){
if(pipe == '*'){ // if the character is an asterisk
type++;
newindicator = 0;
word.clear();
}else if (pipe == '\n'){ // if the character is next line
if(newindicator == 0){ // tells the reader to know that it just finished reading a *, so it doesn't print anything.
newindicator = 1;
}else {
if(type == 1){
cout << "new word as: ";
cout << word << "\n";
}else if (type == 2){
cout << "new word as: ";
cout << word << "\n";
}
word.clear(); // clears the word string as it's reading the next line.
}
}else{
word+=pipe;
}
}
return 0;
}
Directory.dat
*
Chan
Johnathan
Joespeh
*
Betty
Lady Gaga
Output
Chanword as:
new word as: Johnathan
new word as: Joespeh
Bettyord as:
new word as: Lady Gaga
Note that how "Chan" is overriding the characters "new " on the first line, but it's fine after that. This seems to happen on every new type I'm doing, and when its recalling a new set of type. Same with Betty on the next set, which overrides "new w" with "Betty" on that cout.
Any feedback would be much appreciated. Thank you!
I suspect your input file has Windows line endings. These contain the carriage return character that's handled differently on Unix.
https://superuser.com/questions/374028/how-are-n-and-r-handled-differently-on-linux-and-windows
Thank you all for the comments and feedback. Made the changes as suggested:
Corrected
#include <string.h>
#include <fstream>
#include <iostream>
#include <stdio.h>
using std::string;
using std::fstream;
using std::noskipws;
using std::cout;
int main(int argc, const char *argv[]){
char pipe;
string word; // stores the word of characters it's working on at the moment
string filename = "Directory.dat";
int type = 0; // 2 types counter, starts at 0
int newindicator = 0; // for detecting a new * for the data set
fstream fin(filename.c_str(), fstream::in);
while(fin >> noskipws >> pipe){
if(pipe == '*'){ // if the character is an asterisk
type++;
newindicator = 0;
word.clear();
}else if (pipe == '\n'){ // if the character is next line
if(newindicator == 0){ // tells the reader to know that it just finished reading a *, so it doesn't print anything.
newindicator = 1;
}else {
if(type == 1){
cout << "new word as: ";
cout << word << "\n";
}else if (type == 2){
cout << "new word as: ";
cout << word << "\n";
}
word.clear(); // clears the word string as it's reading the next line.
}
}else{
if (pipe != '\r'){
word+=pipe;
}
}
}
return 0;
}
Output
new word as: Chan
new word as: Johnathan
new word as: Joespeh
new word as: Betty
new word as: Lady Gaga

How to parse this data of unknown size

I have a simple text file containing instructions per a line. e.g
A 1 1
B 2 1 A
C 3 1 A
D 4 1 B C
Basic syntax is Letter, Num, Num, Letter(s)
I just don't know what function I should be calling to parse the data, and how to parse it in the given syntax. I feel like there's so many ways to do it.
The following C++ example shows one of possible way to read single characters from file, controlling end of line:
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;
int main(void)
{
ifstream inpFile("test.txt");
string str;
char c;
while (inpFile.good()) {
// read line from file
getline(inpFile, str);
// make string stream for reading small pieces of data
istringstream is(str);
// read data ingnoring spaces
do
{
is >> c; // read a single character
if (!is.eof()) // after successful reading
cout << c << " "; // output this character
} while (is.good()); // control the stream state
cout << "[End of line]" << endl;
}
cout << "[End of file]" << endl;
}
Here istringstream is used to process single line that is got by getline.
After reading a char with is >> c value in c can be checked for content, e.g.:
if (!is.eof()) // after successful reading
{
// analyze the content
if ( isdigit(c) )
cout << (c - '0') << "(number) "; // output as a digit
else
cout << c << "(char) "; // output as a non-number
}
Note: if file can contain not single characters / digits, but numbers and words, type of c should be appropriate (e.g. string)
In C++, read an entire line and make a stream from it, then read from that stream with >>.
Example:
std::ifstream file(filename);
std::string line;
while (file.getline(line))
{
std::istringstream in(line);
char letter;
int number1;
int number2;
std::vector<char> letters;
if (in >> letter >> number1 >> number2)
{
char letter2;
while (in >> letter2)
{
letters.push_back(letter2);
}
}
}
This is C example that read lines, and then goes (using pointer) from the beginning to output readable characters (with code greater than 32):
#include <stdio.h>
#include <ctype.h>
#define MAX_LINE_LEN 80
int main(void)
{
FILE * inpFile = fopen("test.txt", "r");
char buf[MAX_LINE_LEN];
char *p;
while (!feof(inpFile))
{
// read a line from file
if (fgets(buf, MAX_LINE_LEN, inpFile) != NULL)
{
p = buf; // start from the beginning of line
// reading data from string till the end
while (*p != '\n' && *p != '\0')
{
// skip spaces
while (isspace(*p) && *p != '\n') p++;
if (*p > 32)
{
// output character
printf("%c ", *p);
// move to next
p++;
}
}
}
printf("[End of line]\n");
}
printf("[End of file]\n");
return 0;
}
To extract numbers and words from the line you can do something like:
// reading data from string till the end
while (*p != '\n' && *p != '\0')
{
// skip spaces
while (isspace(*p) && *p != '\n') p++;
if (*p > 32)
{
int num;
char word[MAX_LINE_LEN];
// trying to read number
if (sscanf(p, "%i", &num))
{
printf("%i(number) ", num);
}
else // read string
{
sscanf(p, "%s", word);
printf("%s(string) ", word);
}
// move to next space in the simplest way
while (*p > 32) p++;
}
}

Swapping chars in a file, multiple times

I have code that works but only once. I need an input char a to be swapped with an input char b. The first time through the loop, it swaps the two selected chars fine, but on the second and following iterations it does nothing but keep the outFile the same. How can I swap more than two chars until I want to stop?
ifstream inFile("decrypted.txt");
ofstream outFile("swapped.txt");
const char exist = 'n';
char n = '\0';
char a = 0;
char b = 0;
cout<<"\nDo u want to swap letters? press <n> to keep letters or any button to continue:\n"<<endl;
cin>>n;
while (n != exist)
{
cout<<"\nWhat is the letter you want to swap?\n"<<endl;
cin>>a;
cout<<"\nWhat is the letter you want to swap it with?\n"<<endl;
cin>>b;
if (inFile.is_open())
{
while (inFile.good())
{
inFile.get(c);
if( c == b )
{
outFile<< a;
}
else if (c == a)
{
outFile<< b;
}
else
{
outFile<< c;
}
}
}
else
{
cout<<"Please run the decrypt."<<endl;
}
cout<<"\nAnother letter? <n> to stop swapping\n"<<endl;
cin>>n;
}
Consider a different approach.
Collect all the character swaps in a lookup table. By default translate['a'] == 'a', the input character is the same as the output character. To swap a with z just set translate['a'] = 'z' and translate['z'] = 'a'.
Then perform a single pass over the file, copying and translating at the same time.
#include <array>
#include <fstream>
#include <iostream>
#include <numeric>
int main()
{
std::array<char,256> translate;
std::iota(translate.begin(), translate.end(), 0); // identity function
for (;;)
{
char a, b;
std::cout << "\nEnter ~ to end input and translate file\n";
std::cout << "What is the letter you want to swap? ";
std::cin >> a;
if (a == '~') break;
std::cout << "What is the letter you want to swap it with? ";
std::cin >> b;
if (b == '~') break;
std::swap(translate[a], translate[b]); // update translation table
}
std::ifstream infile("decrypted.txt");
std::ofstream outfile("swapped.txt");
if (infile && outfile)
{
std::istreambuf_iterator<char> input(infile), eof;
std::ostreambuf_iterator<char> output(outfile);
// this does the actual file copying and translation
std::transform(input, eof, output, [&](char c){ return translate[c]; });
}
}
You have read the entire file, and as such will not read more bytes or write more bytes. You can use seek to get back to the beginning, or simply close and re-open the files.

Counting the number of words in a file

#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
int hmlines(ifstream &a){
int i=0;
string line;
while (getline(a,line)){
cout << line << endl;
i++;
}
return i;
}
int hmwords(ifstream &a){
int i=0;
char c;
while ((c=a.get()) && (c!=EOF)){
if(c==' '){
i++;
}
}
return i;
}
int main()
{
int l=0;
int w=0;
string filename;
ifstream matos;
start:
cout << "give me the name of the file i wish to count lines, words and chars: ";
cin >> filename;
matos.open(filename.c_str());
if (matos.fail()){
goto start;
}
l = hmlines(matos);
matos.seekg(0, ios::beg);
w = hmwords(matos);
/*c = hmchars(matos);*/
cout << "The # of lines are :" << l << ". The # of words are : " << w ;
matos.close();
}
The file that i am trying to open has the following contents.
Twinkle, twinkle, little bat!
How I wonder what you're at!
Up above the world you fly,
Like a teatray in the sky.
The output i get is:
give me the name of the file i wish to count lines, words and chars: ert.txt
Twinkle, twinkle, little bat!
How I wonder what you're at!
Up above the world you fly,
Like a teatray in the sky.
The # of lines are :4. The # of words are : 0
int hmwords(ifstream &a){
int i;
You've forgotten to initialize i. It can contain absolutely anything at that point.
Also note that operator>> on streams skips whitespace by default. Your word counting loop needs the noskipws modifier.
a >> noskipws >> c;
Another problem is that after you call hmlines, matos is at end of stream. You need to reset it if you want to read the file again. Try something like:
l = hmlines(matos);
matos.clear();
matos.seekg(0, ios::beg);
w = hmwords(matos);
(The clear() is necessary, otherwise seekg has no effect.)
Formatted input eats whitespaces. You can just count tokens directly:
int i = 0;
std::string dummy;
// Count words from the standard input, aka "cat myfile | ./myprog"
while (cin >> dummy) ++i;
// Count files from an input stream "a", aka "./myprog myfile"
while (a >> dummy) ++i;