I have a std::string s=n8Name4Surname. How can I obtain in 2 strings the Name and the Surname? THX
One way to do this is using Boost.Tokenizer. See this example:
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
int main()
{
using namespace std;
using namespace boost;
string text="n8Name4Surname.";
char_separator<char> sep("0123456789");
tokenizer<char_separator<char> > tokens(text, sep);
string name, surname;
int count = 0;
BOOST_FOREACH(const string& s, tokens)
{
if(count == 1)
{
name = s;
}
if(count == 2)
{
surname = s;
}
++count;
}
}
EDIT
If you put the results in a vector, its even less code:
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <algorithm>
#include <iterator>
#include <vector>
int main()
{
using namespace std;
using namespace boost;
string text="n8Name4Surname.";
char_separator<char> sep("0123456789");
tokenizer<char_separator<char> > tokens(text, sep);
vector<string> names;
tokenizer<char_separator<char> >::iterator iter = tokens.begin();
++iter;
if(iter != tokens.end())
{
copy(iter, tokens.end(), back_inserter(names));
}
}
You can detect numerical characters in the string using function isdigit(mystring.at(position), then extract substring between those positions.
See:
http://www.cplusplus.com/reference/clibrary/cctype/isdigit/
Use Boost tokenizer with the digits 0-9 as delimiters. Then, throw away the string containing "n". It's overkill, I realize...
Simple STL approach:
#include <string>
#include <vector>
#include <iostream>
int main()
{
std::string s= "n8Name4Surname";
std::vector<std::string> parts;
const char digits[] = "0123456789";
std::string::size_type from=0, to=std::string::npos;
do
{
from = s.find_first_of(digits, from);
if (std::string::npos != from)
from = s.find_first_not_of(digits, from);
if (std::string::npos != from)
{
to = s.find_first_of(digits, from);
if (std::string::npos == to)
parts.push_back(s.substr(from));
else
parts.push_back(s.substr(from, to-from));
from = to; // could be npos
}
} while (std::string::npos != from);
for (int i=0; i<parts.size(); i++)
std::cout << i << ":\t" << parts[i] << std::endl;
return 0;
}
Mandatory Boost Spirit sample:
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
int main()
{
std::string s= "n8Name4Surname";
std::string::const_iterator b(s.begin()), e(s.end());
std::string ignore, name, surname;
using namespace boost::spirit::qi;
rule<std::string::const_iterator, space_type, char()>
digit = char_("0123456789"),
other = (char_ - digit);
if (phrase_parse(b, e, *other >> +digit >> +other >> +digit >> +other, space, ignore, ignore, name, ignore, surname))
{
std::cout << "name = " << name << std::endl;
std::cout << "surname = " << surname << std::endl;
}
return 0;
}
Related
#include <iostream>
#include <optional>
#include <string>
#include <boost/tokenizer.hpp>
int main() {
std::string a("http://website/some-path/,file1,file2");
char *ptr = (char *)a.c_str();
boost::char_separator<char> delim(",");
std::vector<std::string> pths{};
boost::tokenizer<boost::char_separator<char>> tokens(
std::string(ptr), delim);
std::optional<std::string> pref = std::nullopt;
for (const auto& tok : tokens) {
if (!pref) {
pref = tok;
std::cerr << "prfix is set: " << tok << std::endl;
continue;
}
pths.push_back(*pref + tok);
}
for(auto &t : pths) {
std::cout << t << std::endl;
}
}
My output:
prfix is set: �site/some-path/
�site/some-path/file1
�site/some-path/file2
The question is, what is wrong with the above? If I work with std::regex, it is fine.
EDIT: the scenario with *ptr is the one I actually had: the original string was passed to a function as char *, hence the above. This is to answer the comment by #273K.
A lot can be simplified, at once removing the problems:
Live On Coliru
#include <boost/tokenizer.hpp>
#include <iostream>
#include <optional>
#include <string>
auto generate(std::string const& a) {
boost::tokenizer tokens(a, boost::char_separator<char>{","});
std::optional<std::string> prefix;
std::vector<std::string> result;
for (const auto& tok : tokens) {
if (!prefix)
prefix = tok;
else
result.push_back(*prefix + tok);
}
return result;
}
int main() {
for (auto& t : generate("http://website/some-path/,file1,file2"))
std::cout << t << std::endl;
}
Prints
http://website/some-path/file1
http://website/some-path/file2
This is the example:5,6,13,4,14,22, .
I want to fill the array with 5 6 13 4 12 22
After compilation it returns : 5 6 , 3 4 , 4 , 2 .
When I introduce 2,3,5,1,6,4 the array will be the correct one.
int nr=0;
for(int j=0;j<sizeOfString;j++){
if ((string[j] == ',')){
output << j <<" j ";//comma positions
}else{
stringArrayg[nr++]= putchar(string[j-2]);
}
}
Here is an example that uses vectors, std::replace, and std::istringstream:
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
std::vector<int> convertToIntArray(std::string input)
{
std::replace(input.begin(), input.end(), ',', ' ');
std::istringstream stringReader{ input };
std::vector<int> result;
int number;
while (stringReader >> number)
{
result.push_back(number);
}
return result;
}
int main()
{
std::string testString = "5,6,13,4,14,22";
std::vector<int> newArray = convertToIntArray(testString);
for (int i = 0; i < newArray.size(); ++i)
{
std::cout << newArray[i] << " ";
}
}
Why not using Boost::Tokenizer as like that:
include <iostream>
#include <vector>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main()
{
string str = "5,6,13,4,14,22"; /// string to parse
vector<int> nums; /// vector containing the numbers extracted from the string
char_separator<char> sep(","); /// char separator for the tokenizer
/// extracting all the tokens separated by ","
tokenizer<char_separator<char> > tokens(str, sep);
/// looping over the tokes and storing them in the vector by
/// casting each token to an int
std::transform( tokens.begin(), tokens.end(), std::back_inserter(nums), &boost::lexical_cast<int,std::string> );
/// printing the vector content
for(const auto &v: nums) {
cout << v << " ";
}
cout << endl;
return 0;
}
this might be a stupid question (I hope not) but it caught my mind and I'm trying to figure it out. What is the most efficient way to parse a string using c++ features?
I appreciate everyone's comments as I, am I'm sure everyone else is too, to become a better programmer!
Here is how I would do it right now with my current knowledge:
#include <iostream>
#include <string>
using std::cout;
using std::string;
using std::endl;
void parseLine(string &line)
{
constexpr char DELIMITER_ONE = '|';
constexpr char DELIMITER_TWO = '[';
for (int i = 0; i < line.length(); i++)
{
if (line[i] == DELIMITER_ONE || line[i] == DELIMITER_TWO)
{
line.erase(i, 1);
}
}
cout << line << endl;
}
int main()
{
std::string testString = "H|el[l|o|";
parseLine(testString);
system("pause");
return 0;
}
line.erase(
std::remove_if(line.begin(), line.end(),
[](char c) { return c == DELIMITER_ONE || c == DELIMITER_TWO; }
),
line.end()
);
See also: erase-remove idiom
One more way is to use the boost regex library. Check the below code:
#include <iostream>
#include <string>
#include <boost/regex.hpp>
int main(){
std::string testString = "H|el[l|o|";
boost::regex rx("\\||\\[");
std::string replace = "";
std::string out = boost::regex_replace(testString, rx, replace);
std::cout << out << std::endl;
}
C++14 now includes regular expressions standard:
#include <iostream>
#include <string>
#include <regex>
std::string parseLine(const std::string& line);
int main() {
std::string testString = "H|el[l|o|";
std::string result = parseLine(testString);
std::cout << result << std::endl;
system("pause");
return 0;
}
std::string parseLine(const std::string& line) {
std::string input_string;
std::string result;
std::smatch sm;
std::regex r("([a-zA-Z]+)");
for(input_string = line; std::regex_search(input_string, sm, r); input_string = sm.suffix()) {
result.append(sm[0].str());
}
return result;
}
In the code i am working on now I have a vector load itself from a txt file now I was trying to see if their was a way to replace certain words in the vector without needing a position or anything
so for example if the txt contained a list of animals and i wanted to change bird to book how would i do that without need the position of the letters
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
vector <string> test;
int main()
{
string file;
fstream fout( "Vector.txt" );
while ( !fout.eof())
{
getline(fout,file);
test.push_back(file);
}
fout.close();
for( int i = 0; i < test.size(); i++)
{
cout << test[i] << endl;
}
system("pause");
}
txt contains:
dog
cat
bird
hippo
wolf
Use std::transform().
std::string bird2book(const string &str)
{
if (str == "bird")
return "book";
return str;
}
std::transform(test.begin(), test.end(), test.begin(), bird2book);
you can use std::replace
std::replace (test.begin(), test.end(), "bird", "book");
Try this:
typedef std::istream_iterator<string> isitr;
ifstream fin("Vector.txt");
vector <string> test{ isitr{fin}, isitr{} }; // vector contains strings
map<string,string> dict{ // replacements dictionary
{"bird", "book"}, {"cat", "kitten"}
};
for(auto& x: test) // x must be a reference
{
auto itr = dict.find(x);
if(itr != dict.end()) // if a match was found
x = itr->second; // replace x with the found replacement
// (this is why x must be a reference)
}
for(const auto& x: test)
cout << test << " ";
Use STL!! It's our power. Everything you need:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>
#include <fstream>
#include <map>
int main()
{
std::vector<std::string> words;
const std::map<std::string, std::string> words_to_replace{
{ "bird", "book" }, { "cat", "table" }
};
auto end = words_to_replace.cend();
std::transform(
std::istream_iterator<std::string>{ std::ifstream{ "file.txt" } },
std::istream_iterator<std::string>(),
std::back_inserter(words),
[&](const std::string& word) {
auto word_pos = words_to_replace.find(word);
return (word_pos != end) ? word_pos->second : word;
});
std::copy(words.cbegin(), words.cend(),
std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << std::endl;
}
So having a string like remixsettings_bits=1; wysiwyg=1,2,3,abc; remixclosed_tabs=0; remixgroup_closed_tabs=786432; remixlang=0; remixchk=5; remixsid=35d4f9907281708019490d07728c27ca5c10e5de7a869c322222225e3219e; audio_vol=100
I wonder how to parse tham into map name <-> value using boost::spirit and than be capable to write it back using boost::spirit?
Update:
So what I have done:
#include <iostream>
#include <sstream>
#include <string>
#include <map>
//...
std::map<std::string, std::string> user_control::parse_cookie( std::string cookie_data )
{
std::map<std::string, std::string> parsed_cookie;
std::string token, token2;
std::istringstream iss(cookie_data);
while ( getline(iss, token, ' ') )
{
std::string name, val;
std::istringstream iss2(token);
int num = 0 ;
while ( getline(iss2, token2, '=') )
{
if ( num == 0)
{
name = token2;
num++;
}
else
{
val = token2;
std::string::iterator it = val.end() - 1;
if (*it == ';')
val.erase(it);
}
}
std::cout << "name: " << name << " value: " << val << std::endl;
parsed_cookie.insert(std::pair<std::string, std::string>(name, val));
}
return parsed_cookie;
}
but I really wonder how to port my code into boost::spirit code.
This should do the trick, parsing pairs and printing the results using Karma, although we should probably both go read Hartmut's article!
#include <boost/spirit/include/qi.hpp> // Parsing
#include <boost/spirit/include/karma.hpp> // Generation
#include <boost/fusion/adapted/std_pair.hpp> // Make std::pair a fusion vector
int main( int argc, char**argv)
{
using namespace boost::spirit;
std::string str = "keyA=value1; keyB=value2;keyC=value3;";
std::map<std::string,std::string> contents;
std::string::iterator first = str.begin();
std::string::iterator last = str.end();
const bool result = qi::phrase_parse(first,last,
*( *(qi::char_-"=") >> qi::lit("=") >> *(qi::char_-";") >> -qi::lit(";") ),
ascii::space, contents);
assert(result && first==last);
std::cout << karma::format(*(karma::string << '=' <<
karma::string << karma::eol), contents);
}
Have you seen this parser article and this generator article? AFAICT, they explain exactly what you're trying to do.