
//
// To jest przykładowy kod z podrozdziału 11.7 „Stosowanie niestandardowych separatorów” książki 
// "Programowanie. Teoria i praktyka z wykorzystaniem C++" Bjarne'a Stroustrupa.
//

#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

//------------------------------------------------------------------------------

class Punct_stream { // Podobna do istream, ale umożliwia rozszerzanie
                     // zestawu białych znaków.
public:
    Punct_stream(istream& is)
        : source(is), sensitive(true) { }

    void whitespace(const string& s)       // s jest zbiorem białych znaków.
    { white = s; }    
    void add_white(char c) { white += c; } // Dodaje znak do zbioru białych znaków.
    bool is_whitespace(char c);            // Czy c należy do zbioru białych znaków?

    void case_sensitive(bool b) { sensitive = b; }
    bool is_case_sensitive() { return sensitive; }

    Punct_stream& operator>>(string& s);
    operator bool();
private:
    istream& source;       // źródło znaków
    istringstream buffer;  // Formatowanie wykonuje bufor.
    string white;          // Znaki uznawane za „białe”.
    bool sensitive;        // Czy strumień rozpoznaje wielkość liter?
};

//------------------------------------------------------------------------------

Punct_stream& Punct_stream::operator>>(string& s)
{
    while (!(buffer>>s)) {    // Próba wczytania danych z bufora.
        if (buffer.bad() || !source.good()) return *this;
        buffer.clear();

        string line;
        getline(source,line); // Pobranie wiersza danych ze źródła (source).

        // Zastępowanie znaków zgodnie z potrzebą:
        for (int i =0; i<line.size(); ++i)
            if (is_whitespace(line[i]))
                line[i]= ' ';
            else if (!sensitive)
                line[i] = tolower(line[i]);

        buffer.str(line);     // Wstawianie łańcucha do strumienia.
    }
    return *this;
}

//------------------------------------------------------------------------------

bool Punct_stream::is_whitespace(char c)
{
    for (int i = 0; i<white.size(); ++i) if (c==white[i]) return true;
    return false;
}

//------------------------------------------------------------------------------

Punct_stream::operator bool()
{
    return !(source.fail() || source.bad()) && source.good();
}

//------------------------------------------------------------------------------

int main()
// Przyjmuje tekst. Tworzy posortowaną listę wszystkich słów w tym tekście,
// ignoruje interpunkcję i różnice wielkości liter oraz
// usuwa duplikaty z danych wyjściowych.

{
    Punct_stream ps(cin);
    ps.whitespace(";:,.?!()\"{}<>/&$@#%^*|~"); // Zauważ, że \" oznacza " w łańcuchu.
    ps.case_sensitive(false);

    cout << "Wpisz słowa.\n";
    vector<string> vs;
    string word;
    while (ps>>word) vs.push_back(word);    // Wczytuje słowa.

    sort(vs.begin(),vs.end());              // Sortuje w porządku leksykograficznym.
    for (int i=0; i<vs.size(); ++i)         // Zapisuje słownik.
        if (i==0 || vs[i]!=vs[i-1]) cout << vs[i] << endl;
}

//------------------------------------------------------------------------------
