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;
}
Related
I am trying to create a program where I could read string data from a file and store it into an array, but I want to skip any integers or non-letters and not read them into the array. Any ideas on how to do that?
This is my code:
#include <iostream>
#include <stream>
#include <iomanip>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <string>
using namespace std;
void loadData();
int main()
{
loadData();
return 0;
}
void loadData()
{
const int SIZE = 100;
string fileName;
std::string wordArray[SIZE];
cout << "Please enter the name of the text file you want to process followed by '.txt': " << endl;
cin >> fileName;
ifstream dataFile;
dataFile.open(fileName, ios::in);
if (dataFile.fail())
{
cerr << fileName << " could not be opened." << endl; //error message if file opening fails
exit(-1);
}
while (!dataFile.eof())
{
for (int i = 0; i < SIZE; i++)
{
dataFile >> wordArray[i];
for (std::string& s : wordArray) //this for loop transforms all the words in the text file into lowercase
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return std::tolower(c); });
cout << wordArray[i] << endl;
}
}
}
Use copy_if:
for (std::string& s : wordArray)
std::copy_if(s.begin(), s.end(), s.begin(),
[](char& c) { c = std::tolower(c); return std::isalpha(c); });
Note that this may not be the most efficient code.
This is a scenario where regexes can come in handy.
They do require forward iterators though, so you need to read in the whole file at once before extracting words.
#include <iostream>
#include <iterator>
#include <fstream>
#include <regex>
std::string read_whole_file(const std::string& file_name) {
std::ifstream file(file_name);
return {std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>()};
}
int main()
{
// ...
auto file_data = read_whole_file(filename);
std::regex word_regex("(\\w+)");
auto words_begin =
std::sregex_iterator(file_data.begin(), file_data.end(), word_regex);
auto words_end = std::sregex_iterator();
for (auto i = words_begin; i != words_end; ++i) {
std::cout << "found word" << i->str() << '\n';
}
}
We have a char. We need to replace all ab characters from our char with the letter c.
Example we have :
abracadabra
the output will be :
cracadcra
I tried to use replace() function from C++, but no success.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
string test;
cin>>test;
for(int i=0;i<(strlen(test)-1);i++)
{
if((test[i]=='a')&&(test[i+1]=='b')){
test.replace( test[i], 'c' );
test.replace( test[i+1] , ' ' );
}
}
cout << test << endl;
return 0;
}enter code here
You can use C++11 regex:
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string str = "abracadabra";
std::regex r("ab");
std::cout << std::regex_replace(str, r, "c") << "\n"; // cracadcra
}
Problem:
That is not the syntax of std::string::replace.
Solution:
As is mentioned here the syntax is std::string::replace(size_t pos, size_t len, const string& str). Do test.replace(i, 2, "c" ) instead of test.replace(test[i],'c').
Or use regular expressions as dtell pointed.
Adittional information:
using namespace std; is considered a bad practice (More info here).
You should use std::string::size instead of strlen when you're working with std::string.
To work with std::string you should use #include <string> instead of #include <cstring>.
Full code:
#include <iostream>
int main()
{
std::string test;
std::cin >> test;
for(unsigned int i = 0; i < test.size() - 1; i++)
{
if((test[i]=='a') && (test[i+1]=='b'))
{
test.replace(i, 2, "c" );
}
}
std::cout << test << std::endl;
return 0;
}
The simplest thing you can do by using the standard library is first to find ab and then replace it. The example code I wrote is finding string ab unless there is None in the string and replacing it with c.
#include <iostream>
#include <string>
int main()
{
std::string s = "abracadabra";
int pos = -1;
while ((pos = s.find("ab")) != -1)//finding the position of ab
s.replace(pos, sizeof("ab") - 1, "c");//replace ab with c
std::cout << s << std::endl;
return 0;
}
//OUTPUT
cracadcra
I read many functions online but they just solve that problem with spaces in strings.so how can I get out all the numerical values from a letter and number
sequence.
May be this could help you:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string oldStr = "123we45rt75";
string newStr = "";
for(int i = 0; i < oldStr.length(); i++)
{
char val = oldStr[i];
if( (val <= 90 && val >= 65) || (val <= 122 && val >= 97) )
newStr += val;
}
cout <<"Old String: " << oldStr <<"\nNew String: " << newStr << endl;
return 0;
}
You can achieve this using the erase/remove idiom:
#include <algorithm>
#include <iostream>
#include <string>
int main()
{
std::string input = "123we45rt75";
input.erase(
std::remove_if(input.begin(), input.end(), [](const char c) { return (0 == std::isdigit(c)); }),
input.end());
std::cout << input << std::endl;
return 0;
}
std::remove_if() combined with std::isdigit() will let you find all the non-numeric characters. input.erase() will then remove the found characters from the string.
I used "using namespace std;" in my entire study in C++,so basically I don't understand something like std::out,please help me out.Let's say I have a code shown below,i want the two string to be the same when I compare them.
int main(void)
{
using namespace std;
char a[10] = "123 ";
char b[10] = "123";
if(strcmp(a,b)==0)
{cout << "same";}
return 0;
}
If you use std::string instead of char you could use the truncate functions from boost.
Use std::string to do it
std::string a("123 ");
std::string b("123");
a.erase(std::remove_if(a.begin(), a.end(), ::isspace), a.end());
if (a == b)
std::cout << "Same";
The difference made by using will be
using namespace std;
string a("123 ");
string b("123");
a.erase(remove_if(a.begin(), a.end(), ::isspace), a.end());
if (a == b)
cout << "Same";
It is generally advised not to use the using namespace std. Don't forget to include <string> and <algorithm>.
EDIT If you still want to do it the C way, use the function from this post
https://stackoverflow.com/a/1726321/2425366
void RemoveSpaces(char * source) {
char * i = source, * j = source;
while (*j != 0) {
*i = *j++;
if (*i != ' ') i++;
}
*i = 0;
}
use regex \\s+ to match all space characters and use regex_replace to remove it
#include <iostream>
#include <regex>
#include <string>
int main()
{
std::string text = "Quick brown fox";
std::regex spaces("\\s+");
// construct a string holding the results
std::string result = std::regex_replace(text, spaces, "");
std::cout << '\n' << text << '\n';
std::cout << '\n' << result << '\n';
}
reference: http://en.cppreference.com/w/cpp/regex/regex_replace
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;
}