Defining an end of input stream

Nov 16, 2008 at 9:00am
Hi, mates!
I have to defina a pattern(template) manipulator with one argument, with several functions. One of them is to make it accept not just ' ' or '\n' for the end of the stream, but any other character. Probably that character should be the argument, but im not sure about that.
Any hint would be of help.
Thank you in advance!
Last edited on Nov 16, 2008 at 10:06am
Nov 16, 2008 at 9:14am
Nov 16, 2008 at 10:04am
Thank you Scipio,
But as far as i know, the methods cin.get() / getline() do not read the last delimiting character (and it must be of char type, not optional). In my case will need that 'object'(type T, not necessary char) from the stream, cause i have to compare it to my manipulator's argument.
Nov 16, 2008 at 11:00am
I think i got you wrong.

Lets say you want to read the input until 'b'. Then you could use this:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

using namespace std;

int main()
{
    char a[100];
    cin.getline (a, 100, 'b');
    cout<<a<<endl;
    system("pause");
    return 0;
}


Is this wat you need, or have i understanded you wrong?
Nov 16, 2008 at 11:26am
Well not exactly. My task is to create an input stream manipulator with one argument(of type T - char, int, double, user type ect...). One of its specifications should be to accept an optional charactaer as an end of the stream.
Last edited on Nov 16, 2008 at 3:15pm
Nov 16, 2008 at 9:45pm
I think i would manage to do it myself, if i find a function similar to get/getline() wich uses a string(not just a single char) as a deliminator of the stream...
Nov 16, 2008 at 11:49pm
Manipulators should not care about EOF. Let the stream handler do that.

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// A simple version of the Unix 'tee' utility
// http://en.wikipedia.org/wiki/Tee_(command)
//
// Only works for text files. (Does NOT work for binary files!)
//

#include <algorithm>
#include <cctype>
#include <ciso646>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
using namespace std;

//----------------------------------------------------------------------------
template <
  typename ValueType,
  typename StreamA = basic_ostream <char, char_traits <char> >,
  typename StreamB = basic_ostream <char, char_traits <char> >
  >
class tee_iterator: public iterator <output_iterator_tag, void, void, void, void>
  {
  private:
    StreamA* a;
    StreamB* b;
  public:
    tee_iterator( StreamA& a, StreamB& b ): a( &a ), b( &b ) { }

    tee_iterator <ValueType, StreamA, StreamB> &
    operator = ( const ValueType& value )
      {
      *a << value;
      *b << value;
      return *this;
      }
  
    tee_iterator <ValueType, StreamA, StreamB> & operator *  ()    { return *this; }
    tee_iterator <ValueType, StreamA, StreamB> & operator ++ ()    { return *this; }
    tee_iterator <ValueType, StreamA, StreamB> & operator ++ (int) { return *this; }
  };

//----------------------------------------------------------------------------
int usage( ostream& outs )
  {
  outs << "usage:\n"
          "  tee [-a] FILE < input > output\n"
          "  command | tee [-a] FILE | command\n"
          "  etc\n\n"

          "The FILE is created if it doesn't exist. If -a is specified, output is\n"
          "appended to the file. Otherwise the file is overwritten.\n";
  return 1;
  }

//----------------------------------------------------------------------------
int main( int argc, char** argv )
  {
  ofstream        file;
  vector <string> args( argv, argv + argc );

  // Parse and validate arguments; open tee file.
  switch (args.size())
    {
    case 1:
      return usage( cout );

    case 3:
      transform(
        args[ 1 ].begin(),
        args[ 1 ].end(),
        args[ 1 ].begin(),
        ptr_fun( (int(*)(int))tolower )
        );
      if ((args[ 1 ] != "-a") and (args[ 1 ] != "/a"))
        {
        cerr << "Given two arguments, first argument must be -a\n";
        return usage( cerr );
        }

    case 2:
      file.open(
        args.back().c_str(),
        (args.size() == 3)
          ? (ios::out | ios::app)
          : (ios::out)
        );
      if (!file)
        {
        cerr << "Failure to open file \"" << args.back() << "\"\n";
        return 1;
        }
      break;

    default:
      cerr << "Too many arguments.\n";
      return usage( cerr );
    }

  // Copy all standard input to both standard output and the file
  cin >> noskipws;
  copy(
    istream_iterator <char> ( cin ),
    istream_iterator <char> (),
    tee_iterator <char> ( cout, file )
    );

  // All done
  file.close();
  return 0;
  }

Nov 17, 2008 at 7:22am
Thank you Douas,
So your idea is to copy the input stream to a file and work on it there as you showed? cause im not sure i've got all of your point.
Nov 17, 2008 at 9:06pm
And one more question:
for example the code :
string inputStr;
cin>>inputStr; //How are you ?
How can i read the hole line and put it in inputStr (including ws)?
I cant use cin.getline() cause i have to enter a size of the input there that could not be exceeded later.
Nov 17, 2008 at 10:27pm
use the getline() function you can find in string.h
http://www.cplusplus.com/reference/string/getline.html
Last edited on Nov 17, 2008 at 10:44pm
Nov 17, 2008 at 11:06pm
Hmm, looking back, I'm not sure I get my point either... maybe I was responding to something else I read.

No, if you are just supposed to write a manipulator, stick to that. (But there is no EOF manipulator --I think you are talking about the end of input: whitespace for cin >> string, and newlines for getline( cin, string ).)

I think Scipio understood your problem right away. For example, given

This is a test
file. $ The end.

If the delimiter is to be the '$', you can use getline() directly:

getline( inf, s, '$' );

However, getline() only delineates on a single character --not useful if you need to break on a set of characters.

For that, you can either write a direct function (like getline()), or you can write a special data type. From what you wrote I think you want the first. (A stream manipulator is a function.)

Firstly, there are plenty of bad examples of simple manipulators. Here's one I found near the top of Google:
http://www.java2s.com/Tutorial/Cpp/0100__Development/Createaninputmanipulator.htm

Don't ever use other streams in a manipulator. It breaks all structure and is antithetical to the design of C++ iostreams. A manipulator should only work on the argument stream, and should avoid side effects as much as possible. If that is not the case, use a regular function instead of a manipulator. (That example fails in other ways too.)

Simple Manipulators
A simple, no-argument manipulator is just a function that takes a single stream class as argument:
1
2
3
4
5
6
7
8
9
10
11
12
#include <cctype>
#include <iostream>
...
istream& skipdigits( istream& ins )
  {
  // While the next available character is a digit, read it.
  while (ins.good() && isdigit( ins.peek(), ins.getloc() ))
    ins.get();
  // If any kind of read error occurred, we'll let it propagate back.
  // (Hence, we don't need to cin.clear() at any time in this function.)
  return ins;
  }
Thereafter, we can skip lists of numbers in input, exactly like we can skip whitespace with the skipws manipulator:
1
2
3
4
5
istringstream ss( "123.456" );
double d;
// Get only the fractional part
ss >> skipdigits >> d;
// Now d == 0.456 

Manipulators will typically return the stream passed as argument. However, they don't have to. More on this in a moment.

Alas, you have now reached the end of most manipulator tutorials.

Parameterized Manipulators
Now for the stuff hard to find on the internet: manipulators with additional arguments. How to do it has actually changed

somewhat. Before C++ had templates, you needed to #include <iomanip> and use some of the macros in that file to create a

manipulator.

However, these days you can just use some template magic. Here is a manipulator that lets you skip any set of characters, and an

example of how to use it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

struct skipthese
  {
  const string& s;
  skipthese( const string& s ): s( s ) { }
  };

template <typename CharType, typename TraitsType>
inline
basic_istream <CharType, TraitsType> &
operator >> (
  basic_istream <CharType, TraitsType> & ins,
  const skipthese& s
  ) {
  while (ins.good() && (s.s.find( ins.peek() ) != string::npos))
    ins.get();
  return ins;
  }

int main()
  {
  string skip, input, result;

  cout << "Enter string of chars to skip> ";
  getline( cin, skip );

  cout << "Enter string beginning with any sequence of chars to skip\n> ";
  getline( cin, input );

  istringstream iss( input );
  iss >> skipthese( skip );
  getline( iss, result, '\0' );

  cout << "The string, sans chars to skip, is: " << result << endl;
  return 0;
  }

Hope this helps.

[edit] cin.getline() is different than the getline() function that you get by #including <string> [/edit]
Last edited on Nov 17, 2008 at 11:06pm
Nov 17, 2008 at 11:26pm
Thank you, Douas. It does help, although its not exactly what i need.
I hope i wont find anymore troubles to finish the task. Otherwise i'll ask again here, cause it seems its the only help i can get :)
Thanks a lot again!
Topic archived. No new replies allowed.