vector<string> filtered_items; filtered_items.push_back("test");
for (auto index_name : filtered_items)
strstr(index_name, "te");
This throws the error -no matching function for call to 'strstr(std::basic_string&, char*)' because of the way index_name is used in strstr. How can I fix this?
strstr is a C function and does not know about std::string. Use std::string::find:
std::vector<std::string> filtered_items{ "test" };
for (const auto& index_name : filtered_items) {
auto i = index_name.find("te");
if ( i != std::string::npos) {
std::cout << "found";
}
}
Related
I have a vector of strings with different chars infront of them. for example:
"Hello
(Hello
I want to remove the first occurrence of the char. So if there is a " or ( before the word, I want it gone. My code so far is this.
void wash(std::vector<std::string> & data)
{
std::string b_chars = "\"'("; //Before_chars
std::string a_chars = "!?;,:.\"')"; //after_chars
data.erase(std::remove_if(data.begin(), data.end(), [&b_chars](const char& c) {
return data.find_first_of(b_chars) != std::string::npos;
}), data.end());
}
Your condition is wrong - you should determine whether c is one of the offending characters, which is b_chars.find(c) != std::string::npos.
To iterate over the entire vector, you could go:
std::for_each(data.begin(),
data.end(),
[&b_chars](std::string& str)
{
str.erase(std::remove_if(str.begin(),
str.end(),
[&b_chars](const char& c)
{return b_chars.find(c) != std::string::npos;}),
data.end());
});
}
But it makes sense to have a separate string-washing function and not limit yourself to vectors of strings (I didn't read your code properly because this is a more useful building block to start with):
void wash_string(std::string & s)
{
static const std::string b_chars = "\"'("; //Before_chars
static const std::string a_chars = "!?;,:.\"')"; //after_chars
s.erase(std::remove_if(s.begin(),
s.end(),
[&b_chars](const char& c)
{return b_chars.find(c) != std::string::npos;}),
s.end());
}
void wash(std::vector<std::string> & data)
{
std::for_each(data.begin(), data.end(), wash_string);
}
There are various problems with your code:
in you code you dont remove chars from a string but from a vector of string. You should iterate your vector and do the removal on each string.
As in molbdnilo answer, you need to change the condition of your lambda to find character inside the string of offending chars
void wash(std::vector<std::string> & data)
{
std::string b_chars = "\"'("; //Before_chars
std::string a_chars = "!?;,:.\"')"; //after_chars
for (auto& str : data) {
str.erase(std::remove_if(str.begin(), str.end(),
[&](const char &c) { return b_chars.find_first_of(c) != std::string::npos; }),
str.end());
}
}
If you want to remove these the chars only before the first occurrence of a 'normal char':
for (vector<std::string>::iterator vt_it = data.begin(); vt_it<data.end(); ++vt_it)
{
std::string::iterator str_it = (*vt_it).begin();
while (str_it != (*vt_it).end())
{
if ((b_chars).find((*str_it)) == std::string::npos)
break;
str_it++;
}
(*vt_it).erase ((*vt_it).begin(), str_it);
}
But if you want to remove all those chars:
for (vector<std::string>::iterator vt_it = data.begin(); vt_it<data.end(); ++vt_it)
{
(*vt_it).erase(
std::remove_if(
(*vt_it).begin(),
(*vt_it).end(),
[&b_chars](const char& c) {return b_chars.find(c) != std::string::npos;}),
(*vt_it).end()
);
}
OBS. You didn't reffer anything about what you want to do with a_chars. I tested all the codes in these post, included mine.
I am trying to write a function that will split a string based on a given character and return a vector of the resulting strings but I am getting a compilation error at the line of my for loop. Any ideas why? I should be able to assign astring[0] to a char pointer correct?
/*
splits string by a given character and returns a vector of each segment
if string = "ab cd ef" and split_char = " " it will return a vector with
"ab" in first location "cd" in second location and "ef" in third location
*/
vector<string> split_string(string string_to_split, const char split_char)
{
//deletes leading split characters
int num_leading_split_char = 0;
for (char * c = string_to_split[0]; c* == split_char; c++)
{
num_leading_split_char++;
}
string_to_split.erase(0, num_leading_split_char);
//makes the split string vector
vector<string> split_string;
string temp_string = "";
for (char * c = string_to_split[0]; c*; c++)
{
if (*c == split_char)
{
split_string.push_back(temp_string); //add string to vector
temp_string = ""; //reset temp string
}
else
{
temp_string += *c; //adds char to temp string
}
}
return split_string;
}
error message:
pgm5.cpp: In function ‘std::vector >
split_string(std::__cxx11::string, char)’:
pgm5.cpp:257:34: error: invalid conversion from
‘__gnu_cxx::__alloc_traits >::value_type {aka
char}’ to ‘char*’ [-fpermissive] for (char c = string_to_split[0];
c == split_char; c++)
^
pgm5.cpp:257:40: error: expected primary-expression before ‘==’ token
for (char c = string_to_split[0]; c == split_char; c++)
^~
pgm5.cpp:269:34: error: invalid conversion from
‘__gnu_cxx::__alloc_traits >::value_type {aka
char}’ to ‘char*’ [-fpermissive] for (char c = string_to_split[0];
c; c++)
^
pgm5.cpp:269:39: error: expected primary-expression before ‘;’ token
for (char c = string_to_split[0]; c; c++)
^
Compilation failed.
I should be able to assign std::string str[0] to a char* pointer correct?
No. str[0] is a char literal not a char* pointer. Your complier is giving you this exact error. Since you're already using std::string why not just simply leverage some of the nice operations it provides for you like substr and find so you don't need to reinvent the wheel or maybe it's a flat tire in your case (kidding). Also, it's a good idea to pass non-POD types that you're not modifying as const references to avoid unnecessary copies i.e. const std::string &. I know in your code there is an erase operation, but in this example there is no need to modify the string being passed in.
std::vector<std::string> split_string(const std::string &string_to_split, char delim)
{
std::vector<std::string> results;
size_t match = 0;
size_t found = 0;
// match each part of string_to_split on delim and tokenize into results
// if delim char is never found then return empty vector
while ((found = string_to_split.find(delim, match)) != std::string::npos)
{
results.push_back(string_to_split.substr(match, found - match));
match = found + 1; // start again at next character
}
// after the loop, if any match was found store the last token
if (match != 0)
{
results.push_back(string_to_split.substr(match));
}
return results;
}
If you are tokenizing on spaces you can use it like this.
std::string test("This is a test.");
std::vector<std::string> tokenized = split_string(test, ' ');
for (const auto& s : tokenized)
{
std::cout << "s=" << s << std::endl;
}
Which will yield the following results.
s=This
s=is
s=a
s=test.
Try this you can try it at http://cpp.sh/8r6ze:
#include <sstream> // std::istringstream
#include <string>
#include <vector>
#include <iostream>
std::vector<std::string> split_string(const std::string string_to_split,
const char delimiter)
{
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(string_to_split);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(token);
}
return tokens;
}
int main ()
{
const std::string theString{"thy with this"};
for (const auto &s:split_string(theString,' ')){
std::cout << s <<std::endl;
}
return 0;
}
I have created a vector<string> names; which stores peoples first names. I want to take each name and create two variables first_letter and last_letter which contains the first and last characters of each name. However I am not quite sure how to get this done since I am just starting with c++. Can anyone explain to me how this can be done, and possibly provide an example?
In C++11 it got easier:
for (std::string& name : names) {
char first_letter = name.front();
char last_letter = name.back();
// do stuff
}
Before that, you'd have to access them directly using operator[]:
for (size_t i = 0; i < names.size(); ++i) {
std::string& name = names[i];
char first_letter = name[0];
char last_letter = name[name.size() - 1];
// do stuff
}
Assuming name is your string and you're OK with using C++11, name.front() and name.back() will work, otherwise dereference the iterator: *name.begin() and *name.rbegin(). Though you'd check whether the name is empty or not:
if (!name.empty()) {
// Safe to proceed now
}
You can iterate over names like (range loop - since C++11)
for (auto& name : names) {
// Do things with individual name
}
or (for older C++)
for (vector<string>::iterator it = names.begin(); it != names.end(); it++) {
// Do things with individual name (*it)
}
It's advised to use constant iterators where possible, if you're not planning to modify strings, replace auto with const auto and ::iterator with ::const_iterator.
Use the string functions front() and back().
Make sure that the string is not empty before using these functions:
Assuming that i is an index into your vector:
if ( !names[i].empty() )
{
char fChar = names[i].front();
char bChar = names[i].back();
}
Create a function to get the two letters from a single string:
std::pair<char, char>
first_and_last(const std::string& s)
{
if (s.length() == 0)
throw std::runtime_error("Empty string!")
return {s.front(), s.back()};
}
(for C++03 return std::make_pair(s[0], s[s.length()-1]) or another of the ways to do it shown by the other answers.)
Then apply that function to each name in turn, saving the results in a new vector:
std::vector<std::pair<char, char>> letters;
letters.reserve(names.size());
std::transform(names.begin(), names.end(), std::back_inserter(letters), first_and_last);
Or use the C++11 range-based for loop:
std::vector<std::pair<char, char>> letters;
letters.reserve(names.size());
for (const auto& name : names)
letters.push_back( first_and_last(name) );
Something like this? There's no error checking, but it's a start.
vector<string> names = ...;
for (vector<string>::iterator i = names.begin(); i != names.end(); ++i)
{
string first_letter = i->substr(0, 1);
string last_letter = i->substr(i->size() - 1, 1);
}
First off, of course, you start with a loop to iterate through the vector.
Then you get those characters with substr, it would look something like this
vector <string>::iterator it;
for(it = names.begin(); it != names.end(); it++)
{
string first = (*it).substr(0, 1);
string second = (*it).substr((*it).length()-1, 1);
..
do whatever you want to
..
}
Consider the following approach
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <utility>
#include <iterator>
int main()
{
std::vector<std::string> v { "Hello", "NinjaZ", "How", "do", "you", "do" };
for ( const auto &s : v ) std::cout << s << ' ';
std::cout << std::endl;
std::vector<std::pair<char, char>> v2;
v2.reserve( v.size() );
std::transform( v.begin(), v.end(),
std::back_inserter( v2 ),
[]( const std::string &s )
{
return std::make_pair( s.front(), s.back() );
} );
for ( const auto &p : v2 )
{
std::cout << p.first << ' ' << p.second << std::endl;
}
return 0;
}
The output is
Hello NinjaZ How do you do
H o
N Z
H w
d o
y u
d o
Instead of the algorithm std::transform you could use an ordinary range based for loop. For example
for ( const auto &s : v ) v2.push_back( { s.front(), s.back() } );
There are many ways to skin this cat.
Here's another short readable example using C++11. What this brings to the table is the use of std::vector::emplace_back which allows for in-place construction of elements, as opposed to move- or copyconstructing. Also shorter syntax which is nice.
Say you have a container that stores the pairs of letters.
std::vector<std::pair<char, char>> letters;
Then use this:
for (auto&& name : names)
letters.emplace_back(name.front(), name.back());
If you want to throw on empty name strings, simply add a statement before the std::vector::emplace_back statement:
if (name.empty()) throw std::runtime_error("Empty string!");
Is it possible to somehow pass multiple strings to the string::find function?
For example, to find a string I might use this:
str.find("a string");
What I'd like to do is something like this:
str.find("a string" || "another string" || "yet another string")
And have the function return the position of the first occurrence of any of the three strings.
Thanks for any suggestions.
Not with std::string::find, but you could use std::find_if from <algorithm>:
std::string str("a string");
std::array<std::string, 3> a{"a string", "another string", "yet another string"};
auto it = std::find_if(begin(a), end(a),
[&](const std::string& s)
{return str.find(s) != std::string::npos; });
if (it != end(a))
{
std::cout << "found";
}
That is not possible but what you can do is:
auto is_there(std::string haystack, std::vector<std::string> needles) -> std::string::size_type {
for(auto needle : needles ){
auto pos = haystack.find(needle);
if(pos != std::string::npos){
return pos;
}
}
return std::string::npos;
}
Either call find multiple times or construct a regular expression from the strings that you want to find. C++11 has support for regular expressions in <regex>.
You can check if it is present with the following code
bool is = string("AB|CD|EF").find("AB") != string::npos ;
So here's the line of my code to which I'm referring:
x.insert("a", "hello");
I'm trying to insert the string "hello" after each "a" in a string. Is it possible to do this with the insert function?
is it not possible to do this with the insert function?
That's right, you can't do this with a single call to insert() since std::string does not have an insert() function with these semantics.
Following this comment, here is how you can do this in a (non-infinite) loop:
void insert_after_each(std::string& s, const std::string& target, const std::string& to_insert)
{
for (std::string::size_type i = s.find(target);
i != std::string::npos;
i = s.find(target, i + target.size() + to_insert.size()))
{
s.insert(i + target.size(), to_insert);
}
}
This inserts the text after (what I call) the target string and skips past the target text ("a") and the inserted text ("hello") in each iteration.
Sample usage:
std::string s = "A cat sat on a mat";
insert_after_each(s, "a", "hello");
assert(s == "A cahellot sahellot on ahello mahellot");
What you want to do is find the position of a by using std::string::find then call std::string::insert to insert string to the right position. For example:
C++11
auto pos = x.find("a");
x.insert(pos, "app");
C++03:
std::string b(x);
int n = 0;
for(std::string::iterator iter = x.begin(); iter!=x.end(); ++iter)
{
if ((*iter) == 'a')
{
int pos = rep.size()* n + distance(x.begin(), iter);
cout << distance(x.begin(), iter) << " " << rep.size() << endl;
b.insert(pos,"app");
n++;
}
}
Now string b is what you are after.