#include<iostream>
#include<string>
#include<array>
#include<locale>
using namespace std;
class endeMachine
{
public:
int findIndex(char letter)
{
int index = 0;
while (letter != alphabet[index])
{
index++;
}//end while letter test
return index;
}//findIndex
string subEncrypt(string clear)
{
string subString = clear;
for (int i = 0; i < clear.length(); i++)
{
subString[i] = substitution[findIndex(clear[i])];
}//end for
return subString;
}//subEncrypt
string transEncrypt(string clear)
{
string subString = clear;
for (int i = 0; i < clear.length(); i++)
{
subString[i] = alphabet[findIndex(clear[i]) + offset];
}//end for
return subString;
}//transEncrypt
private://---------------------------------------------------------
array<char, 26> alphabet = { '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' };
array<char, 26> substitution = { 'm','l','k','j','i','h','g','f','e','d','c','b','a','z','y','x','w','v','u','t','s','r','q','p','o','n' };
int offset = 3;
};//end endoMachine
int main()
{
endeMachine text;
string clear_text = { "Hello" };
cout << text.subEncrypt(clear_text) << endl;
cout << text.transEncrypt(clear_text) << endl;
cin >> clear_text;
}//end main
So in this program I am trying to eventually get to the point where it can:
Encrypt a string entered by the end user
Choose between a substitution or transposition method for the encryption
Decrypt the string previously encrypted post-entry
Choose between either one of the encryption methods to do so
Decrypt the encrypted string without knowing the method of encryption, therefore generating all possible results
My problem is:
When the input string contains an uppercase letter, the whole program shut downs. However if I do something in line 12 like:
while (tolower(letter) != alphabet[index])
the encryption methods both work, but return the strictly lowercase version of the originally input word, making the input of "Hello" result in:
fibby
knoor
upon output, rather than:
Fibby
Knoor
This means that in some way, I need to check for the capitalization of each letter in the word individually, so when it comes time for output, the corresponding letter of the ciphered string can be capitalized before it is output to the screen.
PLEASE HELP!!!
Your findIndex function will fail if an UPPER case char is passed.
The reason is it uses the constant arrays below:
array<char, 26> alphabet = { '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' };
array<char, 26> substitution = { 'm','l','k','j','i','h','g','f','e','d','c','b','a','z','y','x','w','v','u','t','s','r','q','p','o','n' };
So it will not find a match for an UPPER case letter, and your while loop means your index value returned will be out of bounds....That is 1 more than your array size.
You can:
Check if char is upper case
convert to lower case
process via your functions as normal
if it WAS uppercase, then set your encrypted char to uppercase
...And repeat in reverse for decryption I guess.
There are ugly and elegant ways to check if a char is uppercase...
Ugly:
Convert to lower case and compare with original..Are they the same? If not, Original is upper case. This is probably more portable across similar languages. tolower function here.
Elegant:
The char values (for normal English) uppercase characters are between and including 65 and 90. You could check for the char value in this range
Related
Basically I need to check if the characters found in second string can make the first string. The program works, however I have this problem that it doesn't take the character order in mind.
For example if I input:
UMC UniverseCeeMake ==> Yes
but it should input No because UMC != UCM, how can I make it check the character order aswell? can someone assist?
#include <iostream>
#include <string>
using namespace std;
const int MAX = 256;
bool canMakeStr2(string str1, string str2)
{
int count[MAX] = {0};
for (int i = 0; i < str1.length(); i++)
count[str1[i]]++;
for (int i = 0; i < str2.length(); i++)
{
if (count[str2[i]] == 0)
return false;
count[str2[i]]--;
}
return true;
}
int main()
{
int n;
string str1;
string str2;
cin>>n;
for(int i =0;i<n;i++){
cin >> str1 >> str2;
if(str1.length()<=10000 && str2.length()<=10000)
if (canMakeStr2(str2, str1))
cout << "Yes";
else
cout << "No";
}
return 0;
}
As Fabian has alread stated. You approach with counting letters will not work. You will never cover the sequence.
You need to select a different approach. The most easy one is to use the std::strings existing find function.
So, you will go over all characters in the given character set in the correct sequence with a simple range based for loop. Then you can use the find function to check, if the character is existing in the other string.
To ensure the sequence, you must not search always from the beginning, but from the last poasition (+1) where a character was found. This will keep the ensure the sequence.
Example:
UMC UniverseCeeMake
Search for the 'U' starting from the beginning
'U' Found at position 0. Increment start position to 1
Search for 'M' staring from position 1
'M' found at position 11 (already behind the 'C'). Increment start position to 12
Search for a 'C' starting at position 12
Cannot be found --> Result will be "No"
This can be implemented very easyly:
#include <iostream>
#include <string>
bool canMakeStr(std::string toBeChecked, std::string characterSet) {
// Result of function. We assume that it will work
bool result{ true };
// position, where we find a charcted in the string to be checked
size_t position{};
// Go through all characters from the given character set
for (const char c : characterSet) {
// Look, where this character has been found
position = toBeChecked.find(c, position);
// If we could not find the character in the string to be checked
if (position == std::string::npos) {
// Then the result is false
result = false;
break;
}
else {
// Character was found. Now, we implement the solution to check for the sequence
// We will not start to search again at the beginning, but after the just found character
// This will ensure that we keep the sequence
++position;
}
}
return result;
}
int main()
{
// Read the number of test cases
unsigned int numberOfTestCases; std::cin >> numberOfTestCases;
// Work on all test cases
while (numberOfTestCases--) {
// Read the 2 strings
std::string characterSet, toBeChecked; std::cin >> characterSet >> toBeChecked;
// And check for the result
if (canMakeStr(toBeChecked, characterSet))
std::cout << "Yes\n";
else
std::cout << "No\n";
}
return 0;
}
I've been given a programming task that involves taking away certain letters in a string. I was trying out different ways to do this when I found the public member function string find. To put it short I was testing out the function via this program :
#include <iostream>
#include <string>
using namespace std;
int main()
{
string Word = "Applejuice";
cout<<Word.find("e")<<endl;
return 0;
}
So when I put in a letter such as "e" I get the number 4 which confuses me because I thought the function will count all the letters in that specific word such as apple juice. Also, when I use a letter that is not used in that word I get numbers like 18446744073709551615 for example when I put in X for e in the code above.
Could someone explain why this is happening, please?
string.find() will return the position of the first character of the first match.
If no matches were found, the function returns string::npos.
Therefore the number (18446744073709551615) you are getting is the string::npos
If you want to search for an only a single character in the string you can use the following code
#include <iostream>
#include <string>
using namespace std;
// Function that return count of the given
// character in the string
int count(string s, char c)
{
// Count variable
int res = 0;
for (int i=0;i<s.length();i++)
// checking character in string
if (s[i] == c)
res++;
return res;
}
// Driver code
int main()
{
string str= "Applejuice";
char c = 'e';
cout << count(str, c) << endl;
return 0;
}
If you want to avoid some random large values as output i.e. string::npos you can just add check for it like following:
if(Word.find("e") != string::npos)
{
...
}
Method find from class string return the position of the first character of the first match. Return type of find is size_t and since size_t is unsigned integral so if no match were found return string::nopos so you should compare the outputof find with string::nopos.
if(Word.find("e") != string::nopos)
{
...
}
I am making a cypher program for my C++ project. My approach is to declare and initialise two strings, the first one being alphabet and the other being key, as shown below.
string alphabet {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
string key {"ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba"};
I want to pair all these 52 letters to one another in the cypher. That is, small a is replaced with capital Z, small b with Y and so on.
The user will be prompted to enter a string, which will be stored in input. My idea is to loop through the input string and match it with alphabet, and hence find out its pair in key string. This is easy, since the pairs will have the same index. However, how do I swap them? I can use a loop in swap(input[i], key[i]) but that will just make every string as ZYXW... I am not able to write the logic here... Below is my code till now in entirety.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string alphabet {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
string key {"ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba"};
int choice {};
cout<<"Welcome to the Cypher Program";
cout<<"\nYou can encode and decode your messages to make sure they remain secret!";
cout<<"\nWould you like to encode(Press 1) or decode(Press 2) a message?"<<endl;
cin>>choice;
//Encoding
if (choice == 1)
{
string input {};
cout<<"Enter a string to encrypt"<<endl;
getline(cin.ignore(), input); //testString
cout<<"Your encrypted message is: "<<endl;
for(int i {0}; i<input.length(); i++){
swap(input[i], key[i]);
}
cout<<input; //ZYXWVUTSRQ
}
return 0;
}
What I really want to do is:
Get input from user
For each letter in input, find out its index in alphabet
Using this index, swap the character with key.
Display the new string back to user.
I'm going to discuss two simple approaches here.
First is to just do what you've suggested in the first place, find the index of the letter from alhphabet and use that index to find encoded letter from key.
You can use a simple trick here by observing the pattern in your encoding. Whenever it's an uppercase letter, you can just subtract 'A' from its ASCII value to get offset and subtract that offset from ASCII value of z to get encoded letter. You can notice similar pattern when it's a lowercase letter.
Below is the sample code for both approaches.
#include <iostream>
#include <string>
using namespace std;
int main(){
string alphabet {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
string key {"ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba"};
string input1 {"testString"}, input2 {"testString"};
cout<<"Your encrypted message is (Approach 1) : "<<endl;
// Approach 1
for(int i {0}; i<input1.length(); i++){
int idx;
for(idx=0;idx<alphabet.length();idx++) // find index from alphabet
if(input1[i]==alphabet[idx])
break;
input1[i] = key[idx]; // replace with key
}
cout<<input1<<endl; //GVHGhGIRMT
// Approach 2
cout<<"Your encrypted message is (Approach 2) : "<<endl;
for(int i {0}; i<input2.length(); i++){
if(input2[i] >= 'A' && input2[i]<='Z')
input2[i] = 'z' - (input2[i]-'A');
else if(input2[i] >= 'a' && input2[i]<='z')
input2[i] = 'A' + ('z'-input2[i]);
}
cout<<input2; //GVHGhGIRMT
return 0;
}
Output
Your encrypted message is (Approach 1) :
GVHGhGIRMT
Your encrypted message is (Approach 2) :
GVHGhGIRMT
Step 1: Find index of character in original string
if (input[i] >= 97)
{
index = input[i]-97;
}
else
{
index = (input[i]-65) + 26;
}
now use
input[i] = key[index];
Hm, your original code was not that bad. You just need to replace the swap with an assignment. That leaves you originla data untouched.
input[i] = key[alphabet.find(input[i])];
Youd should add const before "alphabet" and "key" then the compiler would have prevented your code.
By the way, with this symmetric key, encoding and decoding is the same.
Not sure why you are writing getline(cin.ignore(), input); instead of getline(cin, input);. That should be corrected.
The calculation approach has the advantage that the key string will not be in exe file.
You could calculate the result also with XOR 255 (result += input[i] ^255, which flips all bits, or with 256-input[i];
Below a "more modern" C++ solution using the std::transform-algorithm
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
// The encoding alphabet and key
constexpr std::string_view alphabet{ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ _" };
constexpr std::string_view key{ "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba_ " };
// String to encrypt
std::string message{"Hello world"};
// Here we will store the result
std::string result;
std::transform(message.begin(), message.end(), std::back_inserter(result), [&key, &alphabet](const char c)
{ size_t pos{ alphabet.find(c) }; return (pos != std::string::npos) ? key[pos] : '_'; });
// Show result
std::cout << "\nEncrypted: " << result << "\n";
message = result;
result.clear();
std::transform(message.begin(), message.end(), std::back_inserter(result), [&key, &alphabet](const char c)
{ size_t pos{ alphabet.find(c) }; return (pos != std::string::npos) ? key[pos] : '_'; });
// Show result
std::cout << "\nDecrypted: " << result << "\n";
return 0;
}
I'm attempting to take an input from the command line and then convert it to lower case. To do this, I've written:
istream& operator>>(istream& is, Card& c)
{
static map<string,Card::Rank> mr = createmr();
static map<string,Card::Suit> ms = createms();
string srank, c1, ssuit;
if (is >> srank >> c1 >> ssuit)
{
if (c1 == "of")
{
string nsrank;
string nssuit;
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
It fails on the second iteration of that for loop (more precisely, it fails on nsrank[i] = tolower(srank[i]);). The error that is displayed is "string substring out of range" but I don't understand how this could be the case as there are definitely still characters left in the string.
To give an example:
If I enter "Ace of Spades" then it will iterate through the first time (when i=0) and transfer the 'a' fine. However, it then goes back through with i equaling 1 (which should refer to the 'c') but instead it tells me the substring is out of range (even though the assignment to the char temp works fine). During debugging, "nsrank" claims a size of 15 so I don't see how that could be out of range either....
The problem is that nsrank is an empty string, so accessing with operator[]....
If pos is not greater than the string length, the function never
throws exceptions (no-throw guarantee). Otherwise, it causes undefined
behavior.
This one worked for me: http://ideone.com/3LcYqv
#include <iostream>
using namespace std;
int main() {
string srank="Ace of Spades";
string nsrank;
nsrank.resize(srank.length());
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
cout << nsrank << endl;
return 0;
}
The key is the resize to make nsrank the same size as srank.
Edit: Added compact solution
From many places, among them from this answer
#include <algorithm>
#include <string>
string srank="Ace of Spades";
string nsrank=srank;
std::transform(nsrank.begin(), nsrank.end(),nsrank.begin(), ::toupper);
resize nsrank to match size of srank before entering the loop
I am working on a "dictionary" for my class. I have an int array called NumOfWordsInFile[] where NumOfWordsInFile[0] corresponds to how many words are in A.txt and NumOfWordsInFile[25] corresponds to Z.txt
As it is now I have a huge switch for the 26 different conditions of letters. I have a function called AddWord(string word). AddWord gets the first letter of the word passed to it and inserts it into the appropriate .txt file. Now here is the problem. Everytime a word is added to A.txt I must increment NumOfWordsInFile[0] by 1. The only way I can think of to do this is with these huge switches. I also have a deleteWord function which conversely decrements NumOfWordsInFile[] if the word is deleted. Now I dont want to have two 26 case swithes but the problem is I dont know how else to do it. Now I could just do the same thing for the delete function but I really dont want to have hundreds of more lines of code to go through. Is there a better way to do this?
Sample of the switch in the AddWord function:
case 'w':
if (numOfWordsInFile[22] < maxWordsPerFile) {
fout.open(fileName.data(), ios::app);
fout << word << " " << endl;
numOfWordsInFile[22]++;
if (totalWordsInDict < maxWordsInDict) {
totalWordsInDict++;
}
return(Dictionary::success);
} else {
return(Dictionary::failure);
}
case 'x':
if (numOfWordsInFile[23] < maxWordsPerFile) {
fout.open(fileName.data(),ios::app);
fout << word << " " << endl;
numOfWordsInFile[23]++;
if (totalWordsInDict < maxWordsInDict) {
totalWordsInDict++;
}
return(Dictionary::success);
} else {
return(Dictionary::failure);
}
Delete function.
bool Dictionary::DeleteAWord(string word)
{
ofstream fout;
ifstream fin;
string x;
string fileName="#.txt";
int count=0;
vector <string> words;
bool deleted=false;
fileName[0]=toupper(word[0]);
fin.open(fileName.data()); //makes the file depending on the first letter of the argument "word"
while (fin >> x)
{
words.push_back(x);
count++;//number of elements in vector
}
if (SearchForWord(x))
{
for ( ;count > 0; count--)
{
if (words[count-1] == word)
{
// cout << "Found word " << word << " during search, now deleting" << endl;
words.erase(words.begin()+(count-1));
deleted = true;
/*
This clearly doesn't work and is what I need help with, I know why it
doesn't work but I don't know how to make it better than having another
huge switch.
*/
numOfWordsInFile[toupper(word[0])]--;
/*
*/
totalWordsInDict--;
fin.close();
}
}
if (deleted)
{
fout.open(fileName.data());
for (int i = 0; i < words.size(); i++)
fout << words[i] << endl;
return(Dictionary::success);
}
return(Dictionary::failure);
}
return(Dictionary::failure);
}
Just taking a very quick look, it seems like you're using the position of the letter in the alphabet to do stuff.
You could replace all your switch statements with one statement that looks like:
int letter = (int)(ActualLetter - 'a');
if(numOfWordsInFile[letter]<maxWordsPerFile){
fout.open(fileName.data(),ios::app);
fout<<word<<" "<<endl;
numOfWordsInFile[letter]++;
if(totalWordsInDict<maxWordsInDict){
totalWordsInDict++;
}
return(Dictionary::success);
}else{
return(Dictionary::failure);
}
ActualLetter is something like, 'a', for example.
On a related note, in the future if you actually have large switch statements, consider encapsulating the code in functions:
switch (letter)
{
case 'a':
LetterA();
break;
case 'b':
LetterB();
break;
...
}
Or even better, you can use polymorphism to have C++ dispatch to the method you want based on the specific derived class:
class BaseLetter
{
...
public:
virtual void DoStuff() = 0;
};
class LetterA : public BaseLetter
{
public:
void DoStuff();
};
class LetterB : public BaseLetter
{
public:
void DoStuff();
};
void Foo(BaseLetter *letter)
{
// Use dynamic dispatch to figure out what to do
letter->DoStuff();
}
Just note, dynamic dispatch does have a (slight) performance hit, and the above is a very bad place to actually use it. The solution I, RedX, and others have posted is much better suited to your specific example.
struct FileInfo {
int NumWords;
std::string Filename;
};
std::map<char, FileInfo> TheFiles;
FileInfo & FI = TheFiles[letter];
// Work with FI.NumWords and FI.Filename
Alternatively:
std::vector<FileInfo> TheFiles;
FileInfo & FI = TheFiles[std::tolower(Letter) - 'a'];
In most practical character encodings that you're likely to encounter whilst using C or C++, 'a' to 'z' are contiguous, so you can get the array index to use simply by doing (c - 'a'), where c is the char you're looking at.
Chars are basically numbers. 'a' is 97, 'b' is 98 and so on.
The easiest way is to simply replace every numOfWordsInFile[n] with numOfWordsInFile[current_char - 'a'] and the whole code repeated for each case may reside in a function, like this:
int AddWord(char current_char) {
if(numOfWordsInFile[current_char - 'a']<maxWordsPerFile){
fout.open(fileName.data(),ios::app);
fout<<word<<" "<<endl;
numOfWordsInFile[current_char - 'a']++;
if(totalWordsInDict<maxWordsInDict){
totalWordsInDict++;
}
return(Dictionary::success);
}else{
return(Dictionary::failure);
}
}
For more general solutions read about hash maps and function pointers (when, for instance, for each char you might want to assign a different function.
if(numOfWordsInFile[letter - 'A']<maxWordsPerFile){
fout.open(fileName.data(),ios::app);
fout<<word<<" "<<endl;
numOfWordsInFile[letter - 'A']++;
if(totalWordsInDict<maxWordsInDict){
totalWordsInDict++;
}
return(Dictionary::success);
}else{
return(Dictionary::failure);
}
This will only work if you only have english letter in your use-case.
Single characters in C++ are really just numbers corresponding to their ASCII values. You can subtract letters from each other to get numerical values. So if word[0] contains the letter A, then word[0] - 'A' will be 0.
So you can index your numOfWordsInFile array directly, and you won't need a switch at all: numOfWordsInFiled[word[0] - 'A'].
Note that 'A' and 'a' have different numeric values, so you'll have to do some extra work if you're mixing upper and lower case.
If your file is A.txt, let your array index be 'A' - 'A' (= 0), if the file is B.txt, let the array index be 'B' - 'A' (= 1), etc.
It depends on how portable you want to be, or how
internationalized. If you can afford to ignore the possibility
that the first letter might be an accented character, and assume
that you're never going to have run on a mainframe, or anywhere
else that uses EBCDIC, then you can convert the first letter to
a specific case, and subtract 'a' or 'A' (depending on the case)
from it to obtain the index. The C++ standard doesn't guarantee
that the letters are contiguous however, and they aren't in
EBCDIC, nor in any of the encodings which support accented
characters. At the very least, you'll have to test that the
first character is a letter, of course.
Handling the internationalization issue is difficult, since
there is no one generally used encoding, and some of the
encodings are multibyte. For the single byte encodings, it's
fairly straight foreward to use a mapping table; a table with
256 entries, indexed by the first letter (cast to unsigned
char), which returns the index into your table. For multibyte
encodings, like UTF-8, the issue is more complicated: you can
translate the initial character in a UTF-8 sequence to an int,
but you can end up with values around a million or more, and you
don't want a table with a million entries (most of which are
completely irrelevant. One simple solution might be to add
a 27th entry for "other". (This would also catch "words" like
"2nd".)
A very portable way to do this would be:
int mappingTable[256];
std::fill_n(mappingTable, 256, 26);
static char const upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ;
static char const lower[] = "abcdefghijklmnopqrstuvwxyz;
for (int i = 0; i < 26; ++ i) {
mappingTable[upper[i]] = i;
mappingTable[lower[i]] = i;
}
Just don't forget to cast the initial character to unsigned char
before indexing.