Issues replacing a char within a c++ string using string.replace() - c++

I am writing a program that uses a substitution cipher.
I am trying to replace each char in a string (that the user entered) with a char from another string (the encryption key string).
But I am having a bunch of issues doing this with the string.replace() function. It does not replace the the char in the userMessage with the correct char. Although this is not an issue when using a string literal as initialization value for letterReplacement. In addition to this, it somehow temporarily increases the size of the userMessage resulting in the loop running for e.g 5 times in a message 3 char long (no matter how letterReplacement was initialized). I would appreciate any information on why this happens.
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::string userMessage;
std::cout << "\nWelcome to my Military Grade* Encryption Software!" << std::endl;
std::cout << "--------------------------------------------------" << std::endl << std::endl;
std::cout << "Please enter a secret message you would like to encrypt:\n-" << std::endl;
getline(std::cin, userMessage);
std::cout << "-" << std::endl << std::endl;
const std::vector <std::string> encryptionKey {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZT.,<>!?+- '",
"ß?pl ,;0=}okm9)]ijN8([uhb7/{zgv6&ctf%xdrys4$e3w|<2Q>1!+#ä-:*GnB"};
for(size_t i {0}; i < userMessage.size(); ++i) // Here we encrypt the Message
{
char currentCharInUM {userMessage.at(i)};
size_t currentKeyPosition {encryptionKey.at(0).find(currentCharInUM)}; //finds the position that currentChar is at in first encryptionKey String
std::cout << "currentKeyPosition: " << currentKeyPosition << " - " << i << std::endl;
if(currentKeyPosition == std::string::npos) // is letter in userMessage is not in encryptionKey just keep it (skip iteration)
continue;
std::string letterReplacement {encryptionKey.at(1).at(currentKeyPosition)};
userMessage.replace(i,1,letterReplacement);
}
std::cout << userMessage;
return 0;
}

The first character of 2nd element of the vector encryptionKey 'ß' is equal to 2 bytes. So that when you enter 'a' it returns '├' and when you enter b it returns 'ş'. You can try to put another character instead of 'ß'.

If your intention was indeed to store non-ASCII characters, it would have been more advantageous to use a lookup table.
The table could be a std::unordered_map<char, wchar_t> to map the English letters to the encrypted characters:
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
// Strings
const char encryptionKeyA[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZT.,<>!?+- '";
const wchar_t encryptionKeyW[] = L"ß?pl ,;0=}okm9)]ijN8([uhb7/{zgv6&ctf%xdrys4$e3w|<2Q>1!+#ä-:*GnB";
// Create the lookup table bassed on the above strings
std::unordered_map<char, wchar_t> encryptionMap;
for (int i = 0; i < sizeof(encryptionKeyA); ++i)
encryptionMap[encryptionKeyA[i]] = encryptionKeyW[i];
// Test
std::string userMessage = "abcTest";
// The output string
std::wstring encryptedString;
// Now go through each character of the userMessage
for(auto ch : userMessage)
{
// If the character exists, append the associated encrypted character
auto iter = encryptionMap.find(ch);
if (iter != encryptionMap.end())
encryptedString += iter->second;
else
encryptedString += L' '; // Just append a space if it didn't exist
}
std::wcout << encryptedString;
}
Output:
ss?p1 N8
Note that the output is what is displayed if the locale is English. If the terminal/output were set to German, I would assume that the output would be:
ß?p1 N8
since the first character is the "double-s".
Also note that I am assuming that both the original and encrypted character strings are of the same size (in terms of the number of characters) when building the map. If they're not the same size, then the loop to build the map should be adjusted accordingly.

Related

How do you add up the value of Hebrew letters with spaces in map in C++?

I'm trying to make a Gematria calculator in C++ and I'm almost there; however, when I put in multiple words, meaning there are spaces, it only gives me the gematria of the first word.
How would I make it so it includes the spaces?
Here's my code:
int main() {
std::unordered_map <char, double > Gematria_Values = {
{' ', 0}
{'א', 1},
{'ב', 2},
// Goes through rest of hebrew alphabet.
};
// Creates string Gematria
std::string Gematria;
std::cout << "What do you want the gematria of ?" << std::endl;
// Stores input as Gematria
std::cin >> Gematria;
int sum = 0;
for (auto ch : Gematria)
sum += Gematria_Values[ch];
std::cout << "The Gematria of " << Gematria << " is " << sum;
return 0;
So, when I enter "אאאבב" (without the quotes), I get:
The Gematria of אאאבב is 7
However, when I enter אאא בב I get:
The gematria of אאא is 3
So, even though I have a space that = 0, it doesn't add up with the words. So how would I get it so it adds up with the spaces, so it could add up more than one word at a time?
A side question is how do I get it so I don't get the error message of :
Character too large for enclosing character literal type
It would also be nice to know how to break this up into a few methods, but again that's just a side point. My main question is how do I add the Gematria with the spaces?
You should access the map elements using map->first and map->second.
so, in your case, I'd do something like:
#include <unordered_map>
#include <iostream>
#include <string>
int main() {
std::unordered_map <char, double > Gematria_Values = {
{' ', 0},
{'a', 1},
{'b' , 2},
// Goes through rest of hebrew alphabet.
};
// Creates string Gematria
std::string Gematria;
std::cout << "What do you want the gematria of ?" << std::endl;
// Stores input as Gematria
getline(std::cin, Gematria);
int sum = 0;
for (auto ch : Gematria)
sum += Gematria_Values.find(ch)->second;
std::cout << "The Gematria of " << Gematria << " is " << sum;
return 0;
}
The >> (input) operation for a std::string type ends when a whitespace is found in the input stream. Thus, your attempted input (אאא בב) is only reading the first three characters.
You can read in the entire line (up to, but not including, the terminating newline) using the std::getline() function with std::cin as the first (stream) argument.
A side question is how do I get it so I don't get the error message of
Character too large for enclosing character literal type.
You can use the wide character-based versions of cout, cin and string, and also make your map use wchar_t in place of char.
Here's a possible solution:
#include <iostream>
#include <unordered_map>
#include <string>
int main()
{
std::unordered_map <wchar_t, int> Gematria_Values = { // Not sure why you used "double" here but "int" for the sum...
{L' ', 0},
{L'‭א', 1}, // Note: There is an invisible character before the Hebrew
{L'‭ב', 2}, // letters in these lines (to force left-to-right override).
// Goes through rest of hebrew alphabet.
};
// Creates string Gematria
std::wstring Gematria;
std::wcout << "What do you want the gematria of ?" << std::endl;
// Stores input as Gematria
// std::wcin >> Gematria;
std::getline(std::wcin, Gematria);
int sum = 0;
for (auto ch : Gematria)
sum += Gematria_Values[ch];
std::wcout << L"The Gematria of " << Gematria << L" is " << sum;
return 0;
}
I can't fully test it on my platform, as my console doesn't properly process Hebrew characters – please feel free to let me know if there are issues in this respect. However, a test after replacing the א and ב with a and b suggests it works well.

reverse array using recursion in c++

The assignment is relatively simple reverse the array in main using the ReverseStringRecursive function. However the limitation is I can only use a single int and a char nothing else that declares a variable (this includes the banning of for loops and so on). Additionally no extra libraries may be used I am limited to iostream and conio.h. The problem I'm having is that the string will be printed forward and then backwards when I just need it to be printed backwards. the reverseMe variable is pointing to a string in main that contains "abcdefghijklmnopqrstuvwxyz". This function is not suppose to print the string just reverse is then main will print the string.
// INCLUES AND NAMESPACES
#include <iostream>
#include<conio.h>
using namespace std;
// CONSTANTS
const int STRING_SIZE = 100;
// PROTOTYPES
int ReverseStringRecursive(char*);
// MAIN
int main() {
// create a string
char someString[STRING_SIZE] = "abcdefghijklmnopqrstuvwxyz";
// display the string before being reversed
cout << "The string contains: " << endl;
cout << someString << endl << endl;
// make the call to the recursive function
cout << "CALL THE REVERSING FUNCTION" << endl << endl;
ReverseStringRecursive(someString);
// display the string after being reversed
cout << "The string contains: " << endl;
cout << someString << endl;
// exit program
_getch();
return 0;
}
int ReverseStringRecursive(char* reverseMe) {
// YOUR IMPLEMENTATION GOES HERE...
int position = 0;
char holder = ' ';
if (reverseMe[0] == '\0') {
return 1;
}
else {
holder = reverseMe[position];
}
ReverseStringRecursive(reverseMe + 1);
while (reverseMe[position] != '\0') {
position++;
}
reverseMe[position] = holder;
return position;
}
Example output that I am getting:
"abcdefghijklmnopqrstuvwxyz zyxwvutsrqponmlkjihgfedcba"
what I'm suppose to get:
"zyxwvutsrqponmlkjihgfedcba"
Tough problem. You have to shorten the inner string on each recursion by placing a '\0' on the last character before calling the recursive function and then performing the swap after the recursive call.
algorithm:
0. save the index of the last character in the string
1. Save the last character of the current string
2. Set the last character of the current string to null (use the saved index)
3. Call the recursive function starting one character in which will recurse the algorithm for the next inner string (we have already shortened the end of the recursed string)
4. Once the recursion has finished, set the last character to the first char of the current string; then
5. set the first character of the current string to the saved character (which was at the end)
This will work for odd-length strings as well.
The following code should work on a windows system. To make it work on Linux, simply comment out the conio.h include line, comment the __getch() line and uncomment the cin.getch() line.
// INCLUES AND NAMESPACES
#include <iostream>
#include <conio.h>
using namespace std;
// CONSTANTS
const int STRING_SIZE = 100;
// PROTOTYPES
int ReverseStringRecursive(char *);
char *orig;
// MAIN
int main()
{
// create a string
char someString[STRING_SIZE] = "abcdefghijklmnopqrstuvwxyz";
orig = someString;
// display the string before being reversed
cout << "The string contains: " << endl;
cout << someString << endl << endl;
// make the call to the recursive function
cout << "CALL THE REVERSING FUNCTION" << endl << endl;
ReverseStringRecursive(someString);
// display the string after being reversed
cout << "The string contains: " << endl;
cout << someString << endl;
// exit program
_getch(); // uncoment conio.h on a windows system
// std::cin.get(); // use this if on a linux system
return 0;
}
int ReverseStringRecursive(char *reverseMe)
{
int last_index = 0;
while (reverseMe[last_index + 1] != '\0')
last_index++;
char save_char = reverseMe[last_index];
if (*reverseMe != '\0') {
reverseMe[last_index] = '\0'; // shorten the inner string by one
// recurse on the shorter string
ReverseStringRecursive(reverseMe + 1);
// save the outer two characters
reverseMe[last_index] = *reverseMe;
*reverseMe = save_char;
}
}
You are overwriting your terminating '\0' and therefore corrupting your string. When your while loop exists, reverseMe[position] is at '\0' and then you overwrite it with the value holder. Your string is no longer null-terminated and you are getting undefined behavior on your next while loop as it accesses outside the bounds of your array.

ASCII to dec stored in string

The string str_hex contains the hex-values for the letters A-J, which corresponds to the decimal values 65-74. I'm trying to cast each hex-value to its decimal value following this example. It works nice for the std::cout case inside the for-loop, but the output-std::string still has the ascii-values. Why does this not work or is there a nicer/more proper way to build my output string?
#include <string>
#include <iostream>
#include <stdint.h>
int main()
{
std::string str_hex("\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b", 10);
std::string str_output = "";
for (int i = 0; i < 10; ++i)
{
uint8_t tmp = str_hex[i];
str_output.append(1, (unsigned)tmp);
std::cout << "cout+for: " << (unsigned)tmp << std::endl;
if(i<9)
str_output.append(1, '-');
}
std::cout << std::endl << "cout+str_append: " << str_output << std::endl;
return 0;
}
Compiling and running the program gives the following output:
cout+for: 65
cout+for: 66
cout+for: 67
...
cout+str_append: A-B-C-D-E-F-G-H-I-J
The desired output is:
cout+str_append: 65-66-67-68-...
The method string::append accepts, among the various overload, a size_t and a char, see reference.
string& append (size_t n, char c);
Therefore, in your code line
str_output.append(1, (unsigned)tmp);
you are implicitly converting the unsigned tmp to a char, i.e., to a single letter. To obtain the output you want, you have to convert tmp to a string containing the number, and then append it to str_output. You can do that by using
str_output+=std::to_string(tmp);
instead of str_output.append(1, (unsigned)tmp);.
You have to change your string append to for the change from a number to its "string":
str_output.append(std::to_string(tmp));
It's not one character that you want to add, but a string representing the number.

C++ reversing a string

I'm trying to reverse a string in my C++ code line below revStr.at(j) = str.at(size);
But it doesn't change any of the elements in revStr.
Is there another way to do it without using any libraries.
#include <iostream>
#include<sstream>
#include <iterator>
using namespace std;
int main() {
ostringstream d;
long long c = 123456789;
d << c;
//cout << c << endl;
string str = d.str();
//cout << str.at(0) << endl;
int size = str.size() - 1;
//cout << size << endl;
ostringstream e;
e << str;
string revStr = e.str();
for (int i = size; size==0; size--) {
//cout << str.at(size);
int j = 0;
revStr.at(j) = str.at(size);
j++;
} // End For
cout << "Original String is :" << str << endl;
cout << "Reversed String is :" << revStr << endl;
}
Use std::reverse:
#include <string>
#include <algorithm>
#include <iostream>
int main()
{
std::string test{"Hello"};
std::cout << "Original string: " << test << std::endl;
std::reverse(test.begin(), test.end());
std::cout << "Reversed string: " << test << std::endl;
return 0;
}
Output:
Original string: Hello
Reversed string: olleH
If you just want to reverse a string, you should use std::reverse, as described by Tyler Lewis. It is the best option.
If you want to learn C++, then writing your own version is good practice.
The line
for (int i = size; size==0; size--)
means “Create a new int called i and set it to size initially. Then, while size is zero, do the following and then decrement size”.
There are three problems with this:
Size is not zero unless you entered a one-character string
Since you never use i, there’s no point in declaring it
Inside the loop you use j which is set to zero each time.
You can fix the first by changing the middle part of the for loop to size >= 0 (but be careful—if you later change it so that size is an unsigned type, because it doesn’t make sense for it to be negative, that code won’t work; it’s generally better to increment going up instead). You can fix the second by using i everywhere in the loop statement, and not changing size. You can fix the third by using i in the loop body, and not declaring a new variable inside the loop.
I noticed you used std::string so I used std function swap and string. Depending on if you consider this as a 'library'. There are several definitions of 'reverse'. You could reverse the word order in a string, or a pure char to char reversal like I wrote. Reversal could also mean changing character case, etc... but this is simply swap first and last. Then swap the 2nd and 2nd to last, then swap the 3rd and 3rd to last, etc...
So some points from your code. You only need to loop half the string length. The swap is from the ith and the ith to last. So the last is numCharacters - 1, thus the ith to last would be Last - i or numCharacters - 1 - i. I believe this is what you intended by using a farLeft(i) and a farRight(j) index.
#include <iostream>
void reverseStringInPlace(std::string &stringToReverse)
{
int numCharacters = stringToReverse.length();
for (int i=0; i<numCharacters/2; i++)
{ std::swap(stringToReverse[i], stringToReverse[numCharacters-i-1]); }
}
int main()
{
std::string stringToReverse = "reversing a string";
std::cout << stringToReverse << std::endl;
reverseStringInPlace(stringToReverse);
std::cout << stringToReverse << std::endl;
return 0;
}
Output:
reversing a string
gnirts a gnisrever
Changes made to the piece of code in question, it works.
for (unsigned int i = size; size >= 0; size--) {
revStr[j] = str[size];
j++;
}

How to count words and numbers from a simple sentence in C++

I'm a beginner at c++(took a couple classes, then no c++ for a while, then starting back up several months later), and I'm trying to count the number of words in a simple sentence and then count the number of numbers in that same sentence. To count the words, I use:
int countWord(char *word)
{
int counter = 0;
char *words = strtok(word, " 0123456789-");
while (words != NULL)
{
counter++;
words = strtok(NULL, " 0123456789-");
}
return counter;
}
The number counter is basically the same, just instead of using integers I use the alphabet.
char *num = strtok(number, " abcdefghijklmnopqrstuvwxyz");
My main is:
int main()
{
char str[] = "what time is 88 it 99today";
cout << "words = " << countWord(str) << " " << "numbers = " <<
countNum(str) << endl;
system("pause");
return 0;
}
When I run this it outputs: words = 3 numbers = 2.
When i rearrange main to:
char str[] = "what time is 88 it 99today";
cout << "words = " << countWord(str) << " ";
cout << "numbers = " << countNum(str) << endl;
output is: words = 5 numbers = 0
Can anyone explain why this is incorrect? Also, if anyone can refer me to a text that covers this, I'd appreciate that. The text I learned from is: "C++ Programming: Program Design Including Data Structures by D.S. Malik. I didn't see any techniques in this book to count "words". Thank you.
The issue is that strtok marks the end of tokens in the original string by a null character. Citing from cppreference:
If such character was found, it is replaced by the null character '\0' and the pointer to the following character is stored in a static location for subsequent invocations.
Notes: This function is destructive: it writes the '\0' characters in the elements of the string str. In particular, a string literal cannot be used as the first argument of strtok.
In your case the line
cout << "words = " << countWord(str) << " " << "numbers = " <<
countNum(str) << endl;
is a composition of operator<<, like
...operator<<(operator<<(cout, "words"), countWord(str))...
so the line countNum(str) is evaluated first. Then countWord(str) is evaluated secondly. This is in contrast to
cout << "words = " << countWord(str) << " ";
cout << "numbers = " << countNum(str) << endl;
where the other way around happens.
One solution is to use a copy of the original string when using strtok, e.g. use strtok(strdup(str)) every time. Better yet, use standard C++ library features, like std::string, std::count_if etc. I'm sure there are plenty of word counting solutions around using pure C++.
Vlad has submitted a nice answer for your C-style code. My answer is demonstrating use of more C++ libraries to help move things along:
#include <iostream>
#include <string>
#include <vector>
#include <regex>
int main() {
// The main string.
std::string str = "what time is 88 it 99today";
// Space is your delimiter
std::string delimiter = " ";
// Create a regex string for matching numbers, including floating point.
std::regex number_chars(std::string("[0123456789.]+"));
// A lambda function to help tokenize strings.
// Returns a vector of substring tokens.
// The internal code is taken from an answer on Stack Overflow.
auto tokenizer = [](std::string s, std::string delimiter) {
size_t pos = 0;
std::string token;
std::vector<std::string> tokens;
while (pos = (s.find(delimiter))) {
token = s.substr(0, pos);
tokens.push_back(token);
s.erase(0, pos + delimiter.length());
if (pos == std::string::npos)
break;
}
return tokens;
};
// Apply the lambda.
auto tokens = tokenizer(str, delimiter);
// Output your tokens.
for (auto it : tokens) {
std::cout << it << "\n";
} std::cout << "\n";
// Output tokens that are numbers.
for (auto it : tokens) {
if (std::regex_match(it, number_chars)) {
std::cout << "String: " << it << " is a number.\n";
}
}
return 0;
}
Since C++ has a regular expression library in C++11, it would be good to leverage it.
Coliru: http://coliru.stacked-crooked.com/a/43cd6711e1243f4a