how to find a substring or string literal - c++

I am trying to write a code that will search userInput for the word "darn" and if it is found, print out "Censored". if it is not found, it will just print out the userInput. It works in some cases, but not others. If the userInput is "That darn cat!", it will print out "Censored". However, if the userInput is "Dang, that was scary!", it also prints out "Censored". I am trying to use find() to search for the string literal "darn " (the space is because it should be able to determine between the word "darn" and words like "darning". I am not worrying about punctuation after "darn"). However, it seems as though find() is not doing what I would like. Is there another way I could search for a string literal? I tried using substr() but I couldn't figure out what the index and the len should be.
#include <iostream>
#include <string>
using namespace std;
int main() {
string userInput;
userInput = "That darn cat.";
if (userInput.find("darn ") > 0){
cout << "Censored" << endl;
}
else {
cout << userInput << endl;
} //userText.substr(0, 7)
return 0;
}

The problem here is your condition. std::string::find returns a object of std::string::size_type which is an unsigned integer type. That means it can never be less than 0 which means
if (userInput.find("darn ") > 0)
will always be true unless userInput starts with "darn ". Because of this if find doesn't find anything then it returns std::string::npos. What you need to do is compare against that like
if (userInput.find("darn ") != std::string::npos)
Do note that userInput.find("darn ") will not work in all cases. If userInput is just "darn" or "Darn" then it won't match. The space needs to be handled as a separate element. For example:
std::string::size_type position = userInput.find("darn");
if (position != std::string::npos) {
// now you can check which character is at userInput[position + 4]
}

std::search and std::string::replace were made for this:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string userInput;
userInput = "That darn cat is a varmint.";
static const string bad_words [] = {
"darn",
"varmint"
};
for(auto&& bad : bad_words)
{
const auto size = distance(bad.begin(), bad.end());
auto i = userInput.begin();
while ((i = std::search(i, userInput.end(), bad.begin(), bad.end())) != userInput.end())
{
// modify this part to allow more or fewer leading letters from the offending words
constexpr std::size_t leading_letters = 1;
// never allow the whole word to appear - hide at least the last letter
auto leading = std::min(leading_letters, std::size_t(size - 1));
auto replacement = std::string(i, i + leading) + std::string(size - leading, '*');
userInput.replace(i, i + size, replacement.begin(), replacement.end());
i += size;
}
}
cout << userInput << endl;
return 0;
}
expected output:
That d*** cat is a v******.

Related

Why is my code printing the wrong ciphertext?

I am trying to make a program that turns a string into encryption by going ten letters ahead of each letter. https://gyazo.com/86f9d708c2f02cf2d70dbc1cd9fa9a06 I am doing part 2. When I input "helloworld" something like 0x45 something comes up. Please help! This is due soon!
I am tried messing around with the for loops but it didn't help.
#include <iostream>
using namespace std;
int main()
{
//Input Message
cout << "Enter a message" << endl;
string message;
getline(cin, message);
//Convert Message to Numbers
int numMess[message.length()];
for (int i = 0; i<message.length(); i++) {
numMess[i] = (int)message[i];
}
cout << numMess << endl;
//Encrypt Number Message by adding ten to each one
int encryptNumMess[message.length()];
for (int a = 0; a < message.length(); a++){
encryptNumMess[a] = numMess[a] + 10;
if (encryptNumMess[a] > 122) {
encryptNumMess[a] = 97;
}
}
cout << encryptNumMess << endl;
//Convert Encrypted Number Message to letters
string encryption[message.length()];
for (int b = 0; b<message.length(); b++) {
encryption[b] = (char)encryptNumMess[b];
}
cout << encryption << endl;
return 0;
}
I expect when I type "helloworld" the final product will be "rovvygybvn"
If you are willing to scrap the hand-coded loops, you can use the STL algorithms such as std::transform to accomplish this:
But first, there are a few things you should do:
Don't use magic numbers such as 122, 97, etc. Instead use the actual character constants, i.e a, b, etc. However if we assume ASCII, where the alphabetic character codes are contiguous, your particular program could simply use a constant string to denote the alphabet, and then use simple indexing to pick out the character.
const char *alphabet = "abcdefghijklmnopqrstuvwxyz";
Then to get the letter a, a simple subtraction is all that's required to get the index:
char ch = 'b';
int index = ch - 'a'; // same as 'b' - 'a' == 98 - 97 == 1
std::cout << alphabet[index]; // will print 'b'
Given this, the next thing is to figure out what character is reached if you add 10 to the value, and if greater than 26, wrap around to the beginning of the alphabet. This can be done using modulus (remainder after division)
char ch = 'x';
int index = (ch - 'a' + 10) % 26; // Same as ('x' - 'a' + 10) % 26 == (120 - 97 + 10) % 26 == 33 % 26 == 7
std::cout << alphabet[index]; // will print 'h'
The next thing is to figure out the opposite, where given an encrypted character, you have to find the unencrypted character by subtracting 10. Here this wraps the opposite way, so a little more work needs to be done (not shown, but code sample reflects what is done).
Putting this all together, and using std::transform and lambdas, we get the following small program:
#include <iostream>
#include <algorithm>
#include <string>
#include <iterator>
#include <cmath>
int main()
{
//Input Message
const char *alphabet="abcdefghijklmnopqrstuvwxyz";
std::string message = "helloworld";
std::string result;
// set the encrypted string using the formula above and std::transform
std::transform(message.begin(), message.end(), std::back_inserter(result),
[&](char ch) { return alphabet[(ch - 'a' + 10) % 26]; });
std::cout << "Encrypted: " << result << '\n';
// convert back to unencrypted using the above formula and std::transform
std::string result2;
std::transform(result.begin(), result.end(), std::back_inserter(result2),
[&](char ch)
{ int index = ch - 'a' - 10; index = index < 0?26 - (abs(index) % 26):index % 26; return alphabet[index];});
std::cout << "Unencrypted: " << result2;
}
Output:
Encrypted: rovvygybvn
Unencrypted: helloworld
This code works for encrypt, if you want to decrypt you should chande newAlphabet and oldAlphabet
I comment in the code that which newAlphabet and oldAlphabet are for encrypt and which are for decrypt
#include <windows.h>
#include <stdio.h>
#include <string>
#include <iostream>
using namespace std;
int main()
{
// For Encrypt
string newAlphabet = "abcdefghijklmnopqrstuvwxyz";
string oldAlphabet = "klmnopqrstuvwxyzabcdefghij";
// For Decrypt
//string newAlphabet = "klmnopqrstuvwxyzabcdefghij";
//string oldAlphabet = "abcdefghijklmnopqrstuvwxyz";
string input = "";
string output = "";
getline(cin, input);
int inputLen = input.size();
if (oldAlphabet.size() != newAlphabet.size())
return false;
for (int i = 0; i < inputLen; ++i)
{
int oldCharIndex = oldAlphabet.find(tolower(input[i]));
if (oldCharIndex >= 0)
output += isupper(input[i]) ? toupper(newAlphabet[oldCharIndex]) : newAlphabet[oldCharIndex];
else
output += input[i];
}
cout << output << endl;
return 0;
}
As others have already mentioned int numMess[message.length()]; is not valid c++.
If it works for you, you're using compiler extension which you really shouldn't rely on. The correct way would be:
std::vector <int> numMess(message.length());
Look up the std::vector reference for more info.
Next, int encryptNumMess[100]; creates a C array style array. encryptNumMess is the base pointer to the array. when you try std::cout << encryptNumMess it'll output the pointer value, NOT the array. You'll need a for loop for doing that, like so :
for(int i = 0; i < 100; ++i)
std::cout << encryptNumMess[i] << " ";
std::cout << endl;
The above also works when you convert this to a vector like we did with numMess whereas in that case, std::cout << encryptNumMess wouldn't even compile.
Thirdly, string encryption[100] creates an array of 100 strings! Not a string of size 100. To do that:
std::string foo(message.length(), '\0');
We have to specify what character to fill the string with. Thus us '\0'.
And now, for the string, to output it, you may use std::cout << foo.
Lastly, since arithmetic is allowed on char, the entire program may be shortened to just this
#include <iostream>
int main()
{
// Input Message
std::cout << "Enter a message" << std::endl;
std::string message, encryption;
getline(std::cin, message);
// Resize encryption string to the desired length
encryption.resize(message.length());
// Do the encryption
for(size_t i = 0; i < message.length(); ++i) {
encryption[i] = message[i] + 10;
if (encryption[i] > 122) {
encryption[i] = 97;
}
}
// Output the string
std::cout << encryption << std::endl;
return 0;
}
Of course, your encryption algorithm is still not correct as per instructions, but I'll leave that for you to figure out. I believe #PaulMcKenzie has already told you most of how to fix it, and also to not use magic numbers.

Find an hidden permutation of a string C++

I have two strings, and wanted to check if the second is a permutation of the first (and viceversa of course).
So I found out on cplusplus reference that the is_permutation function of the library algorithm could help me. Indeed, I have the following code:
int main () {
string s1 = "bear";
string s2 = "reab";
if ( is_permutation (s1.begin(), s1.end(), s2.begin()) )
cout << "Found permutation.\n";
else cout << "No permutations found.\n";
return 0;
}
And this works. But now, for example, let's say I still have the string "bear", and a second random string that inside has a permutation of bear, so something like this:
s1 = "bear";
s2 = "AsdVYTcKIyqbNQreabJUoBn";
As you can see there's still the permutation "reab". How can I actually check if there's an hidden permutation? And eventually, save it on a "s3" different string?
Hope you can help me.
You can use a combination of std::string::substr and is_permutation to achieve this.
// Example program
#include <iostream>
#include <string>
#include <algorithm>
using std::string;
using std::cout;
int main () {
string s1 = "bear";
string s2 = "AsdVYTcKIyqbNQJUoBnreab";
size_t i;
for( i = 0; i <= s2.size() - s1.size(); i++)
{
string s3 = s2.substr(i, s1.size());
if ( is_permutation (s1.begin(), s1.end(), s3.begin()))
{
cout << "Found permutation.\n";
break;
}
else
{
continue;
}
}
if(i > s2.size() - s1.size())
cout << "No permutations found.\n";
return 0;
}
See live demo here.
as kingW3 already pointed out in the comments on how one might do it.
string s1 = "bear";
string s2 = "AsdVYTcKIyqbNQreabJUoBn";
string key = "";
for (int i = 0; i < s2.length()+1 - s1.length(); i++)
{
key = s2.substr(i, s1.length());
if (is_permutation(s1.begin(), s1.end(), key.begin()))
cout << "Found permutation.\n";
else cout << "No permutations found.\n";
}
return 0;
Edit: Please note that the for loop condition has to be writtenm with either +1 or -1 in order to get the last character from your second string.
i < s2.length()+1 - s1.length()
or
i < s2.length() - (s1.length()-1)
hope it helps.

Using find_first_of with a string instead of a set of predefined characters in c++

I want to take in a code, for example ABC and check whether the characters in the code appear in that exact order in a string, for example with the code ABC, and the string HAPPYBIRTHDAYCACEY, which meets the criteria. The string TRAGICBIRTHDAYCACEY with the code ABC however does not pass, because there's a "c" before the "b" after the "a". I want to use the find_first_of function to search through my string, but i want to check for any of the characters in "code", without knowing what characters are in "code" beforehand. Here is my program so far:
#include <iostream>
#include <string>
using namespace std;
int main() {
string code, str, temp;
int k = 0;
int pos = 0;
cin >> code >> str;
while (k < code.size()) {
pos = str.find_first_of(code,pos);
temp[k] = str[pos];
++k;
++pos;
}
cout << temp << endl; // debug. This is just outputs a newline when i
//run the program
if (temp == code) {
cout << "PASS" << endl;
}
else {
cout << "FAIL" << endl;
}
return 0;
}
I think your best bet is to find just the first character, once found, find the next in the remainder of the string, repeat until end of string or all characters found (and return false or true, respectively).
I don't think there's anything builtin for this. If the characters would need to appear directly after each other, you could use std::string::find() which searches for a substring, but that is not what you want.

std::out_of_range when using string find and replace inside while loop

So I have a task to convert all occurrences of some word in one string to another string. But there is problem with condition of while loop which makes this error
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::replace
This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. Process returned 3 (0x3) execution time : 2.751 s
My code is:
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str2("three");
string str("one three two four three three");
while ( str.find(str2) != NULL ){
str.replace(str.find(str2),str2.length(),"five");
cout << str << endl; // i put it inside loop to see output
}
cout << str << endl;
return 0;
}
Any suggestions?
You are checking if str.find(str2) had an occurrence comparing it to NULL, but this is wrong, because NULL is a macro that isn't meant for that and often expands into 0, which can be a valid index. You should compare it to std::string::npos. After doing this change, your code will work.
Edit : std::string::npos corresponds to 18446744073709551615 when testing on coliru. So that clearly isn't a valid index in your string.
This condition
while ( str.find(str2) != NULL ){
does not make sense because a call of find can return std::string::npos that is not equal to zero. In this case the code has undefined behavior.
You can apply the following approach
std::string str2("three");
std::string str("one three two four three three");
const char *five = "five";
size_t n = std::strlen(five);
for (std::string::size_type pos = 0;
( pos = str.find(str2, pos) ) != std::string::npos; pos += n)
{
str.replace(pos, str2.length(), five);
}
it's caused because str.find(str2) returns -1 if str2 is not existed in the str. You can use a variable pos to save the found position, so that you will not need to re-invoke find function. The solution is supposed as following:
#include <iostream>
#include <string>
using namespace std;
int main () {
string str2("three");
string str("one three two four three three");
int pos = str.find(str2);
while (pos > 0) {
str.replace(pos, str2.length(), "five");
pos = str.find(str2);
cout << str << endl; // i put it inside loop to see output
}
cout << str << endl;
return 0;
}

How to make String::Find(is) omit this

If I have a list, which contains the 4 nodes ("this"; "test example"; "is something of"; "a small") and I want to find every string that has "is" (only 1 positive with this list). This topic has been posted a large number of times, which I have used to help get me this far. However, I can't see anywhere how I omit "this" from a positive result. I could probably use string::c_str, then find it myself, after I've reduced my much larger list. Or is there a way I could use string::find_first_of? It would seem there's a better way. Thanks.
EDIT: I know that I can omit a particular string, but I'm looking for bigger picture b/c my list is quite large (ex: poem).
for(it = phrases.begin(); it != phrases.end(); ++it)
{
found = it->find(look);
if(found != string::npos)
cout << i++ << ". " << *it << endl;
else
{
i++;
insert++;
}
}
Just to clarify: what are you struggling with?
What you want to do is check if what you have found is the start of a word (or the phrase) and is also the end of a word (or the phrase)
ie. check if:
found is equal to phrases.begin OR the element preceding found is a space
AND two elements after found is a space OR phrases.end
EDIT: You can access the character that was found by using found (replace X with the length of the string you're finding (look.length)
found = it->find(look);
if(found!=string::npos)
{
if((found==0 || it->at(found-1)==' ')
&& (found==it->length-X || it->at(found+X)==' '))
{
// Actually found it
}
} else {
// Do whatever
}
We can use boost regex for searching regular expressions. Below is an example code. Using regular expression complex seacrh patterns can be created.
#include <boost/regex.hpp>
#include <string>
#include <iostream>
#include <boost/tokenizer.hpp>
using namespace boost;
using namespace std;
int main()
{
std::string list[4] = {"this","hi how r u ","is this fun is","no"};
regex ex("^is");
for(int x =0;x<4;++x)
{
string::const_iterator start, end;
boost::char_separator<char> sep(" ");
boost::tokenizer<boost::char_separator<char> > token(list[x],sep);
cout << "Search string: " << list[x] <<"\n"<< endl;
int x = 0;
for(boost::tokenizer<boost::char_separator<char> >::iterator itr = token.begin();
itr!=token.end();++itr)
{
start = (*itr).begin();
end = (*itr).end();
boost::match_results<std::string::const_iterator> what;
boost::match_flag_type flags = boost::match_default;
if(boost::regex_search(start, end, what, ex, flags))
{
++x;
cout << "Found--> " << what.str() << endl;
}
}
cout<<"found pattern "<<x <<" times."<<endl<<endl;
}
return 0;
}
Output:
Search string: this
found pattern 0 times.
Search string: hi how r u
found pattern 0 times.
Search string: is this fun is
Found--> is Found--> is found pattern 2 times.
Search string: no
found pattern 0 times.
I didn't realize you only wanted to match "is". You can do this by using an std::istringstream to tokenize it for you:
std::string term("is");
for(std::list<std::string>::const_iterator it = phrases.begin();
it != phrases.end(); ++it)
{
std::istringstream ss(*it);
std::string token;
while(ss >> token)
{
if(token == term)
std::cout << "Found " << token << "\n";
}
}