Manipulating specific position in string according to one another - c++

I have a string which has different city names. The format like this :
string cities = "-Paris-Berlin-Cologne-"
And also there is another string which contains some voting results for these cities.
string vote = "-31.2-42.5-40-"
I need to define a function which takes input from user for ex : Berlin
Then function will find and change the result of vote in "string vote"
I tried with counting "-" separators but I couldn't succeed.
Any help would appreciate.
Thank you.

Split both strings at the separators and put the results in a map
std::vector<std::string> split(std::string const& s, std::string const& sep);
std::vector<std::string> const c = split(cities, "-");
std::vector<std::string> const v = split(votes, "-");
// now put them all in a map
assert(c.size() == v.size());
std::map<std::string, float> city2vote;
for (std::vector<std::string>::size_type i=0; i != c.size(); ++i)
{
city2vote[c[i]] = atof(v[i]);
}
// update vote for Berlin
city2vote["Berlin"] = 42.0;
The split function is strait forward, but Boost also provides an implementation if you can use libraries. I used atof for simplicity. In real life, a stringstream would be a much better choice.

Would using a regex search to find the positions in the string of the hyphens, then changing the values between those positions accordingly be an acceptable solution?
Edit - On second thoughts, you needn't overcomplicate things by using regex, you could just use a for loop to scan through the string

First I think you should map each delimited string from cities to vote. You can do it like this:
std::map<std::string, std:string> city_to_vote;
std::istringstream i1(cities), i2(vote);
for (std::string str1, str2; std::getline(i1, str1, '-') &&
std::getline(i2, str2, '-'); )
{
if (!str1.empty() && !str2.empty())
{
city_to_vote.emplace(std::make_pair(str1, str2));
}
}
Then once you receive string input from the user, check if it is inside the map, access the value that it is mapped to, and use a replace algorithm to augment the vote string.

Related

How to find certain substring in string and then go back to certain character?

I save messages in string and I need to make filter function that finds user specified word in those messages. I've split each message by '\n' so the example of one chat would be:
user1:Hey, man\nuser2:Hey\nuser1:What's up?\nuser2:Nothing, wbu?\n etc.
Now user could ask to search for word up and I've implemented a search like this:
for (auto it = msg.cbegin(); (it = std::find(it, msg.cend(), str)) != msg.cend(); it++)
and I could put that string into stringstream and use getline to \n, but how do I go backwards to previous \n so I can get full message? Also, what about first message, cause it doesn't start with \n?
Since you said you split the strings, I image you have a vector of strings where you want to find up for example. You would do something like this
for (const auto& my_string: vector_of_strings){
if (my_string.find("up") != string::npos) {
// message containing up is my_string
}
}
In case you haven't split the strings in a vector you can use this func inspired by this:
vector<string> split(const string& s, const string& delimiter){
vector<string> ret;
size_t last = 0;
size_t next = 0;
while ((next = s.find(delimiter, last)) != string::npos) {
ret.emplace_back(s.substr (last, next - last));
last = next + 1;
}
ret.emplace_back(s.substr(last));
return ret;
}
If this function doesn't work you can always take a look at How do I iterate over the words of a string?

Suggestions that could improve my string splitting function

I'm new to C++ and I'm trying to write some basic functions to get the hang of some of it, I decided on making a custom function to split up a string into tokens every time a specific delimiter is reached.
I've made it work successfully, but since I'm new, I'd like to hear from more experienced programmers on if there is a better way to go about it. This is my code:
vector<string> split(string const str, string const separator=" ") {
int str_len = str.length();
int sep_len = separator.length();
int current_index {0};
vector<string> strings {};
for(int i {0}; i < str_len; ++i) {
if(str.substr(i, sep_len) == separator) {
strings.push_back(str.substr(current_index, i-current_index));
current_index = i + sep_len;
}
}
strings.push_back(str.substr(current_index, str_len-current_index));
return strings;
}
One thing I will say is, I don't like how I had to put
strings.push_back(str.substr(current_index, str_len-current_index));
this after the entire iteration to get the final part of the string. I just can't think of any different methods.
Use std::string::find() to find separators in the string, which is probably much more efficient than your loop that checks for each possible position if the substring at that position matches the separator. Once you have that, you can make use of the fact that if the separator is not found, find() returns std::string::npos, which is the largest possible value of std::string::size_type, so just pass this to substr() to get everything from the current position to the end of the string. This way you can avoid the second push_back().
vector<string> split(string const &str, string const &separator=" ") {
string::size_type current_index {};
vector<string> strings;
while (true) {
auto separator_index = str.find(separator, current_index);
strings.push_back(str.substr(current_index, separator_index - current_index));
if (separator_index == str.npos)
break;
else
current_index = separator_index + separator.size();
}
return strings;
}
Note: ensure you pass the input parameters by reference to avoid unnecessary copies being made.

Use of strtok to obtain certain strings from an input string

vector<string> CategoryWithoutHashTags;
string tester = "#hello junk #world something #cool";
char *pch;
char *str;
str = new char [tester.size()+1];
strcpy(str, tester.c_str());
pch = strtok(str,"#");
while(pch!=NULL)
{
CategoryWithoutHashTags.push_back(pch);
pch=strtok(NULL,"#");
}
cout<<CategoryWithoutHashTags[0]<<endl;
I want to write a program which involves storing all the hash tags words in a vector of strings. The above program stores "hello junk" in the first index rather than "hello". What changes can i make to the program to make it do so?
If you are set on using strtok, you should at the very least use its re-entrant version strtok_r. Then, you should change the code to split at spaces, not at hash marks. This would give you the tokens. Finally, in the loop you would need to look for the first character to be the hash mark, adding the item to the list if it's there, and disregarding the item when the hash mark is not there.
An even better approach would be using a string stream: put your string into it, read tokens one by one, and discard ones with no hash mark.
Here is how you can do it with very little code using C++11's lambdas:
stringstream iss("#hello junk #world something #cool");
istream_iterator<string> eos;
istream_iterator<string> iit(iss);
vector<string> res;
back_insert_iterator< vector<string> > back_ins(res);
copy_if(iit, eos, back_ins, [](const string s) { return s[0] == '#'; } );
Demo on ideone.

how to split cin input

I am facing problem to split my input in C++, for something similar to the Python split function.
The input is given as 1001-43 1003-45 1008-67 in different lines. I want to know how to take these inputs split by '-' and store them in different variables.
In Python it's:
a, x = input().split('-')
Have a look at boost. The string algorithms library includes most of what you can find in python including a split function which splits a string into an stl container of your choice. For example (lifted from their docs) splitting on dash or asterisk:
std::string str1("hello abc-*-ABC-*-aBc goodbye");
std::vector< std::string > SplitVec; // #2: Search for tokens
split( SplitVec, str1, is_any_of("-*"), token_compress_on );
// SplitVec == { "hello abc","ABC","aBc goodbye" }
int number,digit1,digit2,digit3;
std::cin>>number;
digit1=number%10;
digit2=number%100;
digit3=number%1000;
Check out strtok(), http://www.cplusplus.com/reference/clibrary/cstring/strtok/

Replace string in a vector string

I want to replace a string in a vector string. I mean, I have a vector string, define vector tmpback , with info like this: name_lastname_phonenumber
I want to replace some last names. For example if someone is john_smith_5551234, I want to replace smith to smith100.
this is my code, o part of it:
vector<string> tmpback = names;
for (Int_t i = 0; i < tmpback.size(); i++) {
replace(tmpback[i].begin(),tmpback[i].end(),"smith", "smith"+number);
}
(i defined number previously as Int_t number = 0 and give some values later).
did someone have any idea of what am I doing wrong?
Thanks
std::replace does not replace sequences with other sequences. It replaces single elements with other single elements. Besides that, your method of appending a number to a string does not work.
Try boost::replace_first or boost::replace_all along with either boost::lexical_cast or std::to_string(c++11 only) for converting a number to a string.
using namespace boost;
std::string replace_str = std::string("smith") + lexical_cast<std::string>(number);
replace_first(tmpback[i], "smith", replace_str);
You could also search for the sub-string, and if you find it, insert the number (converted to a string) after it:
std::string::size_type pos = tmpback[i].find("smith");
if (pos != std::string::npos)
{
// adding 5 because that's the length of "smith"
tmpback[i].insert(pos + 5, std::to_string(number));
}
My immediate reaction would be to wonder why you're putting yourself in this situation at all. Instead of jamming three separate items into a string, then manipulating pieces of that string, why not create a struct so you can work with each piece separately?
struct person {
std::string first_name;
std::string last_name;
int record_no;
std::string phone_number;
};
This way, instead of tacking the record number (or whatever exactly your '100' represents) onto the end of the last name, you just give it its own field, and write an appropriate number as needed:
vector<person> tmpback;
for (int i=0; i<tmpback.size(); i++)
tmpback[i].record_no = number;