C++ Output via copy() - c++

I'm playing with C++ and the STL and I've attempted to copy a deque into a list and print the list via copy() and ostream_iterator. For some reason, the contents of the list I copied to doesn't print unless I access the elements via front(), back(), or at(). Why do the first two printing attempts fail:
#include <iostream>
#include <fstream>
#include <deque>
#include <algorithm>
#include <iterator>
#include <list>
using namespace std;
void alterParticle(string&);
int main(){
string tmp_str;
deque<string> d;
list<string> l;
ifstream in("foo.txt");
if(!in.is_open()){
cout << "Error opening file" << endl;
return 1;
}
while(in){
getline(in,tmp_str);
d.push_back(tmp_str);
}
for_each(d.begin(),d.end(),alterParticle);
copy(d.begin(),d.end(),ostream_iterator<string>(cout,"\n"));
ostream_iterator<string> out(cout,"\n");
copy_if(d.begin(),d.end(),out,
[](const string& s){
if(s.find("fooparticle")!= string::npos)
return true;
return false;
});
copy_if(d.begin(),d.end(),l.begin(),
[](const string& s){
if(s.find("fooparticle")!= string::npos)
return true;
return false;
});
cout << "First try: " << endl;
for(string s : l)
cout << s << endl;
cout << "Second try: " << endl;
copy(l.begin(),l.end(),out);
cout << "Last try: " << l.front() << endl;
return 0;
}
void alterParticle(string& s){
int fpos = s.find("quark");
string rep_str{"quark"};
if(fpos != string::npos){
s.replace(s.find(rep_str),rep_str.length(),"fooparticle");
}
}
Output:
fooparticle 10 11.4
neutrino 7 20.5
electron 5 6.7
proton 8 9.5
fooparticle 10 11.4
First try:
Second try:
Last try: fooparticle 10 11.4
Edit:
Just so it's easier to see why this didn't work for anyone who asks the same question, here are the semantics of copy_if(). It makes it pretty clear that it does not expand the container:
template <class InputIterator, class OutputIterator, class UnaryPredicate>
OutputIterator copy_if (InputIterator first, InputIterator last,
OutputIterator result, UnaryPredicate pred)
{
while (first!=last) {
if (pred(*first)) {
*result = *first;
++result;
}
++first;
}
return result;
}

copy and copy_if do not add new elements to the list, they assume there are existing elements to copy into. Your list is initially empty and thus you are writing to the begin() == end() iterator of the list. This does not increase the list size (which is why the first two attempts print nothing), but if you access the (not actually existing) first list member, you might get the result that was written there.
Needless to say, assigning to the end() iterator is undefined behavior.
You can keep using copy and friends if you use an insert_iterator (you'd generally use back_inserter), similar to the ostream_iterator you are already using.

Related

Sort a c++ list and remove all duplicate strings

#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <string>
using namespace std;
istream& GetLines(istream& is, list<string>& list) {
string str;
if (list.size() > 0) {
list.clear();
}
while (getline(is, str, is.widen('\n'))) {
list.push_back(str);
}
return is;
}
void Print(const list<string>& list) {
for (auto it = list.cbegin(); it != list.cend(); it++) {
cout << *it << endl;
}
}
void SortAndUnique(list<string>& list) {}
int main() {
list<string> list;
ifstream f(
"/home/jacksparrow/Downloads/university/project/module3/Module_3/"
"T2-list/src /main.cpp");
// Read the file into list
if (!f.is_open() || !GetLines(f, list).eof()) {
cout << "Opening error: Error reading main.cpp" << endl;
return EXIT_FAILURE;
}
Print(list);
cout << "---" << endl;
// Sort and unique
SortAndUnique(list);
Print(list);
cout << "---" << endl;
// Print again
Print(list);
return 0;
}
I have got the above code in a file "main.cpp" and, I read this file "main.cpp" into a list "list list;", now what I want to do is to sort that list into alphabetical order and remove the duplicate strings. Also, this upper code is my main.cpp file in which I included (#include "list.cpp" ) file which's code is written below and does 3 functions:
Reads the file data into the list "getline()".
Prints the list Print()
sort that "list" into alphabetical order and, removes the duplicate strings SortAndUnique().
The proper way to solve this is as suggested to read the documentation about the container you are using and in std::list Operations, you'll find this list:
Public member function
Description
merge
merges two sorted lists
splice
moves elements from another list
removeremove_if
removes elements satisfying specific criteria
reverse
reverses the order of the elements
unique
removes consecutive duplicate elements
sort
sorts the elements
The linked member functions are overloaded:
std::list::sort:
void sort();
template< class Compare >
void sort( Compare comp );
std::list::unique:
size_type unique();
template< class BinaryPredicate >
size_type unique( BinaryPredicate p );
In your case you do not need to provide a Compare functor or a BinaryPredicate since the overloads taking no arguments already do what you want.

How to find the words starting with 'b' from a list and the words which are with 4 symbols?

#include<iostream>
#include<list>
#include<string>
#include<iterator>
#include<algorithm>
using namespace std;
void print(const list<string> &l)
{
cout << "The list elements are : ";
copy(l.begin(), l.end(), ostream_iterator<string>(cout, " "));
cout << endl << endl;
}
int main()
{
list<string> l = { "blue","red","black","yellow","green","gold","silver","pink","orange","brown" };
print(l);
list<string>::iterator it;
system("pause");
return 0;
}
I had to create a list of 10 words. I created it and made the print function. But the problem is that for my school project I have to find all the words starting with b and the next one is that I have to find all the words which length is 4 symbols (for example blue). Thank you in advance for your help!
With std::copy you are copying all the string in the list – no criterium is used for discriminating the strings. You may want to consider std::copy_if() with a suitable predicate instead of std::copy(). The predicate should return true for the string that matches your criteria.
For example:
void print(const std::list<std::string>& lst) {
auto pred = [](const std::string& str) {
if (str.empty() || str[0] != 'b' || str.length() != 4)
return false;
return true;
};
std::copy_if(lst.begin(), lst.end(), std::ostream_iterator<std::string>(std::cout, " "), pred);
}
It will copy strings whose first character is b and are four characters in length. Only the strings that fulfill these requirements will be copied into the output iterator and, as a consequence, streamed into std::cout.
Alternatively, you can simply use a range-based for loop:
for (auto const& str: lst)
if (/* ... */) // criterium
std::cout << str << ' ';

Trouble with C++ maps and their content

This is rather extensive, so in advance, if you get through this, even without an answer or solution, thank you.
So, I have this program that is meant to be a basic social network, minus the user-interface, in which a user is represented by a Person object, which is responsible for maintaining a friend list, a block list, a list of messages, and a queue of pending friend requests. These lists are, respectively, of types std::map<std::string, Person>, std::map<std::string, Person>, std::vector<Message>, and std::queue<Message>, where the std::string for the two maps is a concatenation of the user's first and last names and the Message in the final two containers are and additional class I have defined. I have overloaded the << operator for Person such that it prints the user's first and last names, with a space in between. For some reason, when I go to print these names out, the return is empty, and I have no idea why. The following code is essentially a walkthrough what is happening.
The lines I am using to test my code in the main class:
std::string cFirst ("Chris");
std::string cLast ("Cringle");
SocialNetwork sn;
sn.addUser(cFirst,cLast);
The addUser() function in SocialNetwork:
void SocialNetwork::addUser(std::string first, std::string last){
std::string name = (first + last);
Person user (first, last);
_users.insert(std::pair<std::string, Person>(name, user));
}
Where _users is member data on the SocialNetwork of type std::map<std::string, Person>. The constructor for Person is:
Person::Person(std::string first, std::string last){
_first = first;
_last = last;
}
Where _first and _last are member data on Person that represent the user's first and last names. Then, back in the main class, after sn.addUser(cFirst,cLast);:
sn.printUsers();
Which looks like:
void SocialNetwork::printUsers(){
std::map<std::string, Person>::iterator it;
it = _users.begin();
while(it != _users.end()){
cout << it->first << endl;
cout << it->second << endl;
it++;
}
}
With the given code I have, the expected output for cout << it->first << endl; should be ChrisCringle, and it is. The expected output for cout << it->second << endl; should call the overloaded operator and should be Chris Cringle, but it simply prints a blank space. Any indications as to why would be greatly appreciated. Do I need to pass my params by reference? I have tried this already and seem to run into a lot of trouble. If something appears to be missing that may help, feel free to ask! Thanks again! I know I'll probably get a lot of flak for this long question but I do not think I can manage to make this any more of a simple question.
EDIT: The code for the overloaded operator is:
ostream& operator<<(ostream& os, const Person& per){
os << per._first << " " << per._last;
return os;
}
I just used all the code you've shown: http://ideone.com/mFBxTC
#include <string>
#include <iostream>
#include <map>
using namespace std;
struct Person {
Person(std::string first, std::string last);
std::string _first, _last;
};
ostream& operator<<(ostream& os, const Person& per){
os << per._first << " " << per._last;
return os;
}
struct SocialNetwork {
void addUser(std::string first, std::string last);
std::map<std::string, Person> _users;
void printUsers();
};
void SocialNetwork::addUser(std::string first, std::string last){
std::string name = (first + last);
Person user (first, last);
_users.insert(std::pair<std::string, Person>(name, user));
}
Person::Person(std::string first, std::string last){
_first = first;
_last = last;
}
void SocialNetwork::printUsers(){
std::map<std::string, Person>::iterator it;
it = _users.begin();
while(it != _users.end()){
cout << it->first << endl;
cout << it->second << endl;
it++;
}
}
int main() {
std::string cFirst ("Chris");
std::string cLast ("Cringle");
SocialNetwork sn;
sn.addUser(cFirst,cLast);
sn.printUsers();
return 0;
}
And it works fine. So error is elsewhere
Thats why one should post SSCCE when posting debugging questions.

Multi Set as a class template using list functions as basis

The basis of the program is a header file that defines the template class miniMultiSet. The class uses the list structure as the implementation structure for the multiset. The class is implemented in the header file by implementing the defined class methods.
When I do this I am running into issues sending the information from main.cpp to my header file. There are not error messages, it just freezes and I have to close it. This makes me think I have an error in my memory management.
HEADER FILE:
#ifndef MINIMULTISET_H_INCLUDED
#define MINIMULTISET_H_INCLUDED
#include <list>
#include <set>
using namespace std;
template <typename T>
class miniMultiSet
{
public:
typedef typename list<T>::iterator iterator;
typedef typename list<T>::const_iterator const_iterator;
// miniMultiSet iterators are simply list iterators
miniMultiSet();
// default constructor
bool empty() const{return l.empty();}
// is the multiset empty?
int size() const{return l.size();}
// return the number of elements in the multiset
iterator insert(const T& item)
{
l.insert(l.end(), item);
return l.end();
}
// insert item into multi set and return an
// iterator pointing at the new element.
private:
list<T> l;
// multiset implemented using a list
};
#endif // MINIMULTISET_H_INCLUDED
main.cpp //////////////////////////////////////////////
#include <iostream>
#include <list>
#include <set>
#include <algorithm>
#include "miniMultiSet.h"
using namespace std;
int main()
{
miniMultiSet<int> *A;
A=0;
*A->insert(90);
cout << A->size() << endl;
if(A->empty())
cout << "Set is empty." << endl;
else
cout << "Set contains data." << endl;
return 0;
}
I build it and there are no error statements. When I run it I get "Program has stopped working, searching for a solution.". It then ends the program and I get "Process returned -1073741819 (0xC0000005) execution time: 4.421 s. Press any key to continue."
I am not sure how to fix this, any advice would be greatly appreciated.
Your problem is that you cannot ever construct a miniMultiSet because you declared its default constructor but never defined it. It seems like you should simply remove your default constructor declaration, as the compiler-generated one will work fine, and then do miniMultiSet<int> A without a pointer.
The code above altered to make a multiset operate like a list.
HEADER FILE:
#ifndef MINIMULTISET_H_INCLUDED
#define MINIMULTISET_H_INCLUDED
#include <list>
#include <set>
#include <algorithm>
#include <vector>
using namespace std;
template <typename T>
class miniMultiSet
{
public:
typedef typename list<T>::iterator iterator;
typedef typename list<T>::const_iterator const_iterator;
// miniMultiSet iterators are simply list iterators
//miniMultiSet();
// default constructor
bool empty() const{return l.empty();}
// is the multiset empty?
int size() const{return l.size();}
// return the number of elements in the multiset
int count (const T& item)
{
int tally = 0;
std::list<int>::iterator is;
for(is=l.begin();is!=l.end();++is)
{
if(*is==item)
tally++;
}
return tally;
}
// return the number of duplicate occurrences of item
// in the multiset
iterator find (const T& item)
{
std::list<int>::iterator it;
for(it=l.begin();it!=l.end();++it)
{
if(*it==item)
break;
}
return it;
}
// search for item in the multiset and return an iterator
// pointing at the first occurrence matching item, or end()
// if it is not found
const_iterator find (const T& item) const
{
int count=0;
std::list<int>::iterator it;
for(it=l.begin();it!=l.end();++it)
{
if(*it==item)
break;
}
}
// constant version
iterator insert(const T& item)
{
l.insert(l.end(), item);
return l.end();
}
// insert item into multi set and return an
// iterator pointing at the new element.
int erase(const T& item)
{
int count=0;
std::list<int>::iterator it;
std::list<int>::iterator il;
for(it=l.begin();it!=l.end();++it)
{
if(*it==item)
{
it=l.erase((it));
it--;
++count;
}
}
return count;
}
// erase all occurrences of item from the multi set
// and return the number of items erased.
iterator begin(){return l.begin();}
// return an iterator pointing at the first member
// in the multiset
const_iterator begin() const{return l.cbegin();}
// constant version
iterator end(){return l.end();}
// return an iterator pointing just past the last
// member in the muktiset
const_iterator end() const{return l.cend();}
// constant version
private:
list<T> l;
// multiset implemented using a list
};
#endif // MINIMULTISET_H_INCLUDED
MAIN CPP FILE
#include <iostream>
#include <list>
#include <set>
#include <algorithm>
#include "miniMultiSet.h"
using namespace std;
int main()
{
miniMultiSet<int> A;
A.insert(80);
A.insert(90);
A.insert(90);
A.insert(90);
A.insert(95);
A.insert(100);
A.insert(105);
A.insert(110);
A.insert(115);
A.insert(120);
if(A.empty())
cout << "Set is empty." << endl;
else
cout << "Set is NOT empty." << endl;
cout << endl;
cout << endl;
cout << "This size of the Multi Set is: " << A.size() << endl;
cout << endl;
cout << endl;
cout << "The first element is: " << *A.begin() << endl;
if(A.find(90)!=A.end())
cout << "90 was found" << endl;
cout << endl;
cout << endl;
cout << "90 was found " << A.count(90) << " times." << endl;
cout << endl;
cout << endl;
//pair<int, int>(90);
cout << "90 was found " << A.erase(90) << " times and erased." << endl;
cout << endl;
cout << endl;
cout << "This size of the Multi Set is: " << A.size() << endl;
cout << endl;
cout << endl;
cout << "90 was found " << A.count(90) << " times." << endl;
return 0;
}
Not the prettiest or most efficient, but it works. Thank you for all the help.

Error when compile the primer example code using g++ on Ubuntu Linux

The code is from C++ primer(3 third).
The error is :
*filterString.cpp: In function ‘int main()’:
filterString.cpp:32:68: error: cannot convert ‘__gnu_cxx::__normal_iterator*, std::vector > >’ to ‘std::string* {aka std::basic_string}’ in initialization
pls help me analyse the error,
thanks.
code:
#include <string>
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
using namespace std;
template <class InputIterator>
void filter_string(InputIterator first, InputIterator last, string filt_elems = string("\",?.")) {
for (; first != last; first++){
string:: size_type pos = 0;
while ((pos = (*first).find_first_of(filt_elems, pos)) != string::npos)
(*first).erase(pos, 1);
}
}
bool length_less (string s1, string s2) {
return s1.size() < s2.size();
}
int main() {
istream_iterator<string> input(cin), eos;
vector<string> text;
copy(input, eos, back_inserter(text));
string filt_elems("\",.?;:");
filter_string(text.begin(), text.end(), filt_elems);
int cnt = text.size();
string *max = max_element(text.begin(), text.end(), length_less);
int len = max->size();
cout << "The number of words read is " << cnt << endl;
cout << "The longest word has a length of " << len << endl;
cout << "The longest word is " << *max << endl;
return 0;
}
In line 32 ,
std::max_element(text.begin(), text.end(), length_less);
this function returns a forward iterator addressing the position of the first occurrence of the largest element in the range searched and not a string .
What you can do instead of this line:
string *max = max_element(text.begin(), text.end(), length_less);
you have to do this ,
//First find the index of the max_element , by subtracting the forward iterator you get from calling max_element from the iterator for first element .
int index=max_element(text.begin(), text.end(), length_less) - text.begin();
//And then find string stored on that index.
string *max = text.at(index);
This is interesting. Iterators behave much like pointers, but not exactly. In particular, you can't convert an iterator to a pointer.
However, you can change this code to use an iterator as a sort of string* pointer:
vector<string>::iterator max = max_element(text.begin(), text.end(), length_less);
That declares max to be not a pointer to a string, but an iterator into a vector of strings, which is what the max_element algorithm returns when applies to a vector of strings.
You can also use a pointer, but it's a bad habit. Just for testing the idea, you can:
string *max = &*max_element(text.begin(), text.end(), length_less);
The *max_element(...) returns a reference to the string the returned iterator points to (just like dereferencing a real pointer) and & creates a (string*) pointer to that string.
This invites trouble, since a structural modification of the vector could quietly invalidate that pointer. Subsequent use of the pointer would be treating "random" memory as a string object. Even worse, it might work during your testing and not fail until the software was shipped!
A decent implementation of iterators should detect the invalidation and throw an exception. A predictable fail is better than a random meltdown.
OK, so I went overboard. Here is what I think is a more modern solution using lambdas and auto. I leave it to others to decide if it is easier to understand.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <ostream>
#include <string>
#include <vector>
using namespace std;
template <class InputIterator>
void filter_string(InputIterator first, InputIterator last,
const string filt_elems = const string("\",?."))
{
for_each(first, last,
[filt_elems](string& s)
{
s.erase(
// Shift valid characters up before erasing the undesirable
remove_if(s.begin(), s.end(),
[filt_elems](string::value_type c)
{ return filt_elems.find_first_of(c) != string::npos; }),
s.end());
});
}
int main()
{
istream_iterator<string> input(cin);
istream_iterator<string> eos;
vector<const string> words;
copy(input, eos, back_inserter(words));
const string filt_elems("\",.?;:");
filter_string(words.begin(), words.end(), filt_elems);
const int count = words.size();
// Get a reference to the longest word
const auto& max_word = *max_element(words.cbegin(), words.cend(),
[](const string& lhs, const string& rhs)
{ return lhs.size() < rhs.size(); });
const int length = max_word.size();
cout << "The number of words read is " << count << endl;
cout << "The longest word has a length of " << length << endl;
cout << "The longest word is " << max_word << endl;
return 0;
}