C++: No viable overloaded '=' data.end() -1 = '\0' - c++11

I'm trying to create a program that filters through speech text, removes any unwanted characters (",", "?", etc., etc.") and then produces a new speech where the words are jumbled based on what words follow or precede them. So for example, if you had the Gettysburg Address:
Four score and seven years ago our fathers brought forth, on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.
my program would take that text, put it into a set of strings. i.e. ["Four","score","and","seven",...."continent,"..."Liberty,"..."equal."] Then it would remove any unwanted characters from each string using c++ .erase and c++ .remove, like "," or "." and capitals. After, you'd have a filtered string like ["four","score","and","seven",...."continent"..."liberty"..."equal."]
After that then the words would be rearranged into a new coherent, funnier speech, like:
"Seven years ago our fathers conceived on men...", etc.
That was just so you know the scope of this project. My trouble at the moment has to do with either using my iterator properly or null terminators.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <set>
#include <iterator> //iterates through sets
#include <algorithm>
using namespace std;
int main() {
set <string> speechSet;
set <string> ::iterator itr; //forgot what :: means. Declares iterator as set
int sum = 0;
int x;
string data;
ofstream out;
string setString;
ifstream speechFile; //declare output file stream object. Unknown type name
speechFile.open("./MySpeech");
if (!speechFile) {
cerr << "Unable to open file " << endl;
exit(1);
}
char unwantedCharacters[] = ".";
while (!speechFile.eof()) {
speechFile >> data; //speechFile input into data
for (unsigned int i = 0; i < strlen(unwantedCharacters); ++i) {
data.erase((remove(data.begin(), data.end(),
unwantedCharacters[i]), data.end())); //remove doesn't delete.
data.end() - 1 = '\0'; //Reorganizes
cout << data << endl;
}
speechSet.insert(string(data));
}
//Go through each string (word) one at a time and remove "",?, etc.
/*for(itr = speechSet.begin(); itr != speechSet.end(); ++itr){
if(*itr == ".")//if value pointed to by *itr is equal to '.'
itr = speechSet.erase(itr);//erase the value in the set and leave blank
cout << " " << *itr;//print out the blank
else{
cout << " " << *itr;
}
}*/
speechFile.close();
return (0);
}
I keep getting an error that says error: no viable overloaded '='. At first I thought it might be due to .end() not being a command for a C++ string, but I checked the documentation and it shouldn't be an issue of mismatched data typed. Then I thought it might have to set the iterator itr equal to the end of the data.
iterator itr = data.end() - 1;
and then dereference that pointer and set it equal to the null terminator
itr* = '\0';
That removed the overload error, but I still had another error use of class template 'iterator' requires template arguments. Let me know if any more clarification is needed.

In the for loop, use auto for iterator so you don't have to specify its type like:
for(auto itr = speechSet.begin(); itr != speechSet.end(); ++itr){

Related

auto reference to address in C++

I'm studying some C++ features, trying to play around with some experiments. However, I stuck in a place where it compiled error:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "some string";
auto &c = str.begin(); // compile error
*c = toupper(*c);
cout << *c << ", str: " << str << endl;
}
I'm not sure why it was not acceptable. My thought was that c had type char * (a pointer to a char), so that's why I had written as above. But why it failed in compiling?
Error C2440 Cannot transform 'std::_String_iteratorstd::_String_val<std::_Simple_types<_Elem>>' to'std::_String_iterator<std::_String_val<std::_Simple_types<_Elem
PS: Another method which I had tried first was OK.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "some string";
auto &c = *str.begin(); // success
c = toupper(c);
cout << c << ", str: " << str << endl;
}
begin() returns an iterator by value, not a reference. You are not allowed to form a non-const lvalue reference.
Making it const would prolong the life of the returned iterator and the program would then compile:
const auto &c = str.begin();
On the other hand, iterators are supposed to be cheap to copy and iterators from contiguous containers are often implemented as pure pointers. The idiomatic approach is:
auto c = str.begin();
In your second example, the idiomatic approach to form a reference to the first element would be:
auto& c = str.front();

How would I write a program that reads from a standard input and outputs only 6 characters to a line?

For example if the input was:
My name is Alex and
I also love coding
The correct output should be:
1:My nam
1:e is A
1:lex an
1:d
2:I also
2: love
2:coding
So far I have this
int main () {
string i;
i.substr(0,6);
while (getline(cin, i)) {
cout << i << endl;
}
}
Using ranges, what you ask is almost as easy as
auto result = view | split('\n') | transform(chunk(6));
where view represents somehow the input, | split('\n') splits that input in several lines, and | transform(chunk(6)) transforms each line by splitting it in chunks of 6 chars. The result is therefore a "range of ranges of chunks", on which you can loop with a double nested for.
Here's a full example:
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/chunk.hpp>
#include <range/v3/view/istream.hpp>
#include <range/v3/view/split.hpp>
#include <range/v3/view/transform.hpp>
// Comment/uncomment the line below
//#define FROM_FILE
using namespace ranges;
using namespace ranges::views;
int main() {
// prepare a path-to-file or string buffer
#ifdef FROM_FILE
std::string path_to_file{"/path/to/file"};
#else
std::basic_stringbuf<char> strbuf{"My name is Alex and\nI also love coding"};
#endif
// generate an input stream from the file or the string buffer
#ifdef FROM_FILE
std::ifstream is(path_to_file);
#else
std::istream is(&strbuf);
#endif
// prevent the stream from skipping whitespaces
is >> std::noskipws;
// generate a range view on the stream
ranges::istream_view<char> view(is);
// manipulate the view
auto out_lines = view | split('\n') // split at line breaks
| transform(chunk(6)); // split each in chunks of 6
// output
int index{};
for (auto line : out_lines) {
++index;
for (auto chunk_of_6 : line) {
std::cout << index << ':'
<< (chunk_of_6 | to<std::string>)
<< std::endl;
}
}
}
First I suggest that you give your variables meaningful names. i isn't good for a variable you use to read lines from std::cin. I've changed that name to line in my example below.
You are on the right track with i.substr(0,6); but you've placed it outside of the loop where i is empty - and you don't print it.
You are also supposed to prepend each line with the line number but that part is completely missing.
You have also missed that you should print the next 6 characters of the read line on the next line until you've printed everything that you read.
Here's an example how that could be fixed:
#include <iostream>
#include <string>
int main() {
unsigned max_len = 6;
std::string line;
for(unsigned line_number = 1; std::getline(std::cin, line); ++line_number) {
// loop until the read line is empty:
while(!line.empty()) {
// print max `max_len` characters and prepend it with the line number:
std::cout << line_number << ':' << line.substr(0, max_len) << '\n';
// if the line was longer than `max_len` chars, remove the first
// `max_len` chars:
if(line.size() > max_len) {
line = line.substr(max_len);
} else { // otherwise, make it empty
line.clear();
}
}
}
}

using stl to run length encode a string using std::adjacent_find

I am trying to perform run length compression on a string for a special protocol that I am using. Runs are considered efficient when the run size or a particular character in the string is >=3. Can someone help me to achieve this. I have live demo on coliru. I am pretty sure this is possible with the standard library's std::adjacent_find with a combination of std::not_equal_to<> as the binary predicate to search for run boundaries and probably using std::equal_to<> once I find a boundary. Here is what I have so far but I am having trouble with the results:
Given the following input text string containing runs or spaces and other characters (in this case runs of the letter 's':
"---thisssss---is-a---tesst--"
I am trying to convert the above text string into a vector containing elements that are either pure runs of > 2 characters or mixed characters. The results are almost correct but not quite and I cannot spot the error.
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
expected the following
======================
---,thi,sssss,---,is-a,---,tesst--,
actual results
==============
---,thi,sssss,---,is-a,---,te,ss,--,
EDIT: I fixed up the previous code to make this version closer to the final solution. Specifically I added explicit tests for the run size to be > 2 to be included. I seem to be having boundary case problems though - the all spaces case and the case where the end of the strings ends in several spaces:
#include <iterator>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
int main()
{
// I want to convert this string containing adjacent runs of characters
std::string testString("---thisssss---is-a---tesst--");
// to the following
std::vector<std::string> idealResults = {
"---", "thi", "sssss",
"---", "is-a",
"---", "tesst--"
};
std::vector<std::string> tokenizedStrings;
auto adjIter = testString.begin();
auto lastIter = adjIter;
// temporary string used to accumulate characters that
// are not part of a run.
std::unique_ptr<std::string> stringWithoutRun;
while ((adjIter = std::adjacent_find(
adjIter, testString.end(), std::not_equal_to<>())) !=
testString.end()) {
auto next = std::string(lastIter, adjIter + 1);
// append to foo if < run threshold
if (next.length() < 2) {
if (!stringWithoutRun) {
stringWithoutRun = std::make_unique<std::string>();
}
*stringWithoutRun += next;
} else {
// if we have encountered non run characters, save them first
if (stringWithoutRun) {
tokenizedStrings.push_back(*stringWithoutRun);
stringWithoutRun.reset();
}
tokenizedStrings.push_back(next);
}
lastIter = adjIter + 1;
adjIter = adjIter + 1;
}
tokenizedStrings.push_back(std::string(lastIter, adjIter));
std::cout << "expected the following" << std::endl;
std::cout << "======================" << std::endl;
std::copy(idealResults.begin(), idealResults.end(), std::ostream_iterator<std::string>(std::cout, ","));
std::cout << std::endl;
std::cout << "actual results" << std::endl;
std::cout << "==============" << std::endl;
std::copy(tokenizedStrings.begin(), tokenizedStrings.end(), std::ostream_iterator<std::string>(std::cout, ","));
std::cout << std::endl;
}
if (next.length() < 2) {
if (!stringWithoutRun) {
stringWithoutRun = std::make_unique<std::string>();
}
*stringWithoutRun += next;
}
This should be if (next.length() <= 2). You need to add a run of identical characters to the current token if its length is either 1 or 2.
I seem to be having boundary case problems though - the all spaces
case and the case where the end of the strings ends in several spaces
When stringWithoutRun is not empty after the loop finishes, the characters accumulated in it are not added to the array of tokens. You can fix it like this:
// The loop has finished
if (stringWithoutRun)
tokenizedStrings.push_back(*stringWithoutRun);
tokenizedStrings.push_back(std::string(lastIter, adjIter));

how to find characters in a string and then remove them if they match a particular character?

/* I have to find character and remove them, based on the character it has to go inside the if/else if condition. I am facing difficulty in getting inside the else if condition */
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <string>
using namespace std;
int main() {
int fut = 0, spd =0;
std::string symbol = "PGSh/d TWOGK h/d"; //it will contain either 'h/d' or '/'
std::string str = "h/d";
std::string str1 = "/";
if(symbol.find(str)) //if it finds "h/d" then it belongs to future
{
++fut; //even one count is enough
boost::erase_all(symbol, "h/d");
std::cout<<"Future Instrument "<<std::endl;
}
else if(symbol.find(str1)) //if it finds "/" then it belongs to spread
{
++spd; //even one count is enough
boost::erase_all(symbol, "//");
std::cout<<"Spread Instrument "<<std::endl;
}
boost::erase_all(symbol, " ");
boost::to_upper(symbol);
std::cout<<symbol<<std::endl;
return 0;
}

C++ sequential container initialization using iterator

I'm trying to create sub containers of a container through container<\T>(InputIt First, InputIt Last). For example, I have a string s1="AreYouOK".
The expected outputs are
A
Ar
Are
AreY
AreYo
AreYou
AreYouO
Here is my code:
#include <vector>
#include <string>
#include <iostream>
using std::vector;
using std::string;
using std::cout;
using std::cin;
using std::endl;
int main()
{
string s1 = "AreYouOK";
vector<string> v;
for (string::const_iterator iter = s1.begin();
iter != s1.end()-1; ++iter)
{
string s(s1.begin(),iter); // no matching container
s += *iter;
v.push_back(s);
}
for (vector<string>::const_iterator iter = v.begin();
iter != v.end(); ++iter)
{
cout << *iter <<endl;
}
return 0;
}
I expect the commented line
string s(s1.begin(),iter);
to create a substring s of string s1 in range [s1.begin(), iter), since iter is an iterator of s1. However, I was told that there is no matching constructor for initialization.
error: no matching constructor for initialization of 'string'
(aka 'basic_string<char, char_traits<char>, allocator<char> >')
string s(s1.begin(),iter);
^ ~~~~~~~~~~~~~~~
While
string s(s1.begin(),s1.begin+3);
did manage to create a substring.
Why
string s(s1.begin(),iter);
did not work?
Many thanks!
If you look here, for example, you can see that a full error message contains
prog.cpp:19:33: error: no matching function for call to 'std::basic_string<char>::basic_string(std::basic_string<char>::iterator, std::basic_string<char>::const_iterator&)'
which says that it thinks your calling a constructor that takes an iterator and (reference to) const_iterator. There is no such constructor. Since s1 is a non-const object, s1.begin() returns a regular iterator.
There are many ways around this. One of them is to change your loop to
string::const_iterator b = s1.begin();
for (string::const_iterator iter = b;
iter != s1.end()-1; ++iter)
{
string s(b,iter);
...
Here you indeed use two const iterators (see here your expected output).
Edit
Two excellent (and superior) alternatives are:
Use cbegin if you're C++11 enabled (#rici)
Use accumulate, once you get to that algorithm (#PaulMcKenzie)

Resources