Strings & such


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 11 of 11

Thread: Strings & such

  1. #1
    Join Date
    Dec 2003
    Posts
    3,366

    Strings & such

    Ok, so I have been trying to get string & stringstream to work in place of char arrays, and had a few questions because I cannot seem to get want the way I want it.

    First, how do you edit a stringstream, or, how do you "sprintf" into a string? I can get stringstreams to do something like sprintf, with shift operations like cout, but I cannot for the life of me figure out how to go into the result and edit it. I cannot seem to get anything like sprintf for a string, but that one I can edit with [] operations so long as I do not change its length (is that acceptable or bad, by the way?). I realize you can get a temporary string out of a stringstream and edit that for use or similar (if the above [] usage is ok), but do you really have to use 2 classes to get the job done?

    Second, what is best for storage of raw bytes such as binary soup sent over a tcpip connection? The two string classes do not seem to fit, and vector of char seems wrong too. Shoud this remain a byte array? Keep in mind most of the low level tcpip functions all use char or unsigned char pointers as parameters.

    Any other tips for getting the flexibility of char array from these two classes?

    Thanks
    J

  2. #2
    Join Date
    Oct 2007
    Posts
    369
    I'm not sure I understand your question. Many times I use an ostringstream and the "<<" (inserter) operators (with associated formats), then call the .str() method to get the string. (An istringstream can be initialized with a string and read from.)
    While it is an ostringstream it is a *stream*, which means the data should be thought of as a continuous stream and not a random-access array.

    I've never done it myself, but I've thought about creating a tcpip stream class where you would hand it a socket and a buffer and then one could use ">>" and "<<", and other such iostream functions.
    TCP/IP is full-duplex and, possibly, makes more sense as an iostream.
    Either way, you're going to end up using the OS socket functions unless and until they add them to the C++ language or standard library.

  3. #3
    Join Date
    Dec 2003
    Posts
    3,366
    --I'm not sure I understand your question. Many times I use an ostringstream and the "<<" (inserter) operators (with associated formats), then call the .str() method to get the string.--

    This is my question. Is there no way to drop data into a string (or a stringstream) and later edit it? I want to be able to put text & doubles into a string, and then feed that to another routine that will parse and modify that string. I can do this with a char array. How do I do it with something more modern?

  4. #4
    Join Date
    Oct 2007
    Posts
    369
    Do you mean something like this?
    (In my post, I had out and in reversed)
    [Code]
    istringstream in;

    in << "Hello World. PI=" << 3.1415 << " or therabouts." << endl;

    ostringstream out(in.str());

    string word;
    out >> word;
    ...

  5. #5
    Join Date
    Dec 2003
    Posts
    3,366
    Quote Originally Posted by hendrixj
    Do you mean something like this?
    (In my post, I had out and in reversed)
    [Code]
    istringstream in;

    in << "Hello World. PI=" << 3.1415 << " or therabouts." << endl;

    ostringstream out(in.str());

    string word;
    out >> word;
    ...
    Sort of. How about this (granted, trivial example):

    int count = 0;
    char fname[50];
    sprintf(fname,"File%i.dat", count);
    count ++;
    ...
    int i = 0;
    while(fname[i] != '.') i++;
    i++;
    fname[i++] = 'b';
    fname[i++] = 'a';
    fname[i++] = 'k';
    ...

    so here, unless I goofed, you create a filename file0.dat, file1.dat .... N however many you needed, and later, you also create backups by changing the filename to file0.bak, etc.

    How do you do this with just stringstreams or just strings? Or do you absolutely have to have 2 containers for 1 piece of data?

  6. #6
    Join Date
    Dec 2007
    Posts
    401
    a stringstream class (like std::ostringstream), is just a dozen or so lines of code. it inherits from std::ostream, has a member variable of type std::stringbuf and a constructor that sets up the stream to use the stringbuf as the stream's buffer. it also has a one-line wrapper function, str(), that returns stringbuf::str(). all the meat is in std::stringbuf. if std::ostringstream wasn't there, but std::stringbuf was available, it wouldn't really matter. just that creating a string-based stream object would take two lines instead of one:
    Code:
    std::stringbuf buf ;
    std::ostream ostrstm( &buf ) ;
    the simplest solution would perhaps be to just implement a custom streambuf class and use it instead of std::stringbuf. for example:
    Code:
    #include <iostream>
    #include <vector>
    #include <streambuf>
    #include <cstring>
    #include <iomanip>
    
    template< typename CNTR = std::string, std::size_t BUFSZ = 128 >
    struct container_ostreambuf : public std::streambuf
    {
        container_ostreambuf( CNTR& c ) : cntr(c), pos(0)
        { setp( buf, buf + BUFSZ ) ; }
        ~container_ostreambuf() { sync() ; }
    
        const CNTR& container() const { sync() ; return cntr ; }
        CNTR& container() { sync() ; return cntr ; }
    
      protected:
        int sync()
        {
          std::size_t reqd = pos + std::streamoff( pptr() - pbase() ) ;
          if( cntr.size() < reqd ) cntr.resize(reqd) ;
          std::copy( pbase(), pptr(), cntr.begin()+pos ) ;
          pos = reqd ;
          setp( buf, buf + BUFSZ );
          return 1;
        }
    
        int overflow( int c )
        {
          sync() ;
          if( c != EOF )
          {
            *pptr() = c ;
            pbump(1) ;
          }
          return 1 ;
        }
    
        std::streampos seekoff( std::streamoff off, std::ios_base::seekdir way,
                                std::ios_base::openmode which = std::ios_base::in )
        {
          switch(way)
          {
            case std::ios_base::beg: pos = 0 ; break ;
            case std::ios_base::end: pos = cntr.size()-1 ; break ;
            default: ; // nothing
          }
          pos = pos + off ;
          return seekpos( pos ) ;
        }
       std::streampos seekpos( std::streampos sp, std::ios_base::openmode which = std::ios_base::in )
       {
         sync() ;
         pos = sp ;
         if( cntr.size() < pos ) cntr.resize(pos) ;
         return pos ;
       }
    
      private:
        CNTR& cntr ;
        char buf[BUFSZ];
        std::streampos pos ;
    };
    
    template< typename OSTREAM > inline
    void write_number( OSTREAM& ostm, std::streampos pos,
                       int number, int width, char fill = '0'  )
    {
      ostm.seekp( pos, std::ios_base::beg ) ;
      ostm << std::setw(width) << std::setfill(fill)
           << number << std::flush ;
    }
    
    int main()
    {
      std::string file_name = "file00.dat" ;
      enum { NUMPOS = 4, EXTPOS = NUMPOS+3, EXTLEN=3 } ;
      container_ostreambuf<> stmbuf( file_name ) ;
    
      {
        std::ostream ostm( &stmbuf ) ;
    
        for( int i=8 ; i<12 ; ++i )
        {
          write_number( ostm, NUMPOS, i, 2 ) ; 
          std::cout << file_name << '\n' ;
        }
        
        std::strncpy( &file_name[EXTPOS], "bak", EXTLEN ) ;
        for( int i=8 ; i<12 ; ++i )
        {
          write_number( ostm, NUMPOS, i, 2 ) ;
          std::cout << file_name << '\n' ;
        }
    
        file_name.insert( EXTPOS, "old." ) ;
        for( int i=8 ; i<12 ; ++i )
        {
          write_number( ostm, NUMPOS, i, 2 ) ; 
          std::cout << file_name << '\n' ;
        }
    
        file_name.insert( NUMPOS, 1, ' ' ) ;
        for( int i=98 ; i<102 ; ++i )
        {
          write_number( ostm, NUMPOS, i, 3 ) ;
          std::cout << file_name << '\n' ;
        }
      }
    }
    output:
    Code:
    >c++ cntrbuf.cc && ./a.out
    file08.dat
    file09.dat
    file10.dat
    file11.dat
    file08.bak
    file09.bak
    file10.bak
    file11.bak
    file08.old.bak
    file09.old.bak
    file10.old.bak
    file11.old.bak
    file098.old.bak
    file099.old.bak
    file100.old.bak
    file101.old.bak
    there is also a std::strstream (similar to std::stringstream, but works with c-style character arrays instead of with std::string). it is an obsolete feature and is deprecated. it requires complicated memory allocation and deallocation protocols and is hard to use correctly.

  7. #7
    Join Date
    Dec 2003
    Posts
    3,366
    ...

    Thats very helpful, thank you very much!

    I am terribly let down by this though... you are telling me that c++ strings and tools cannot really do anything useful and that you end up writing your own string class just like we did back before the stl?

  8. #8
    Join Date
    Nov 2003
    Posts
    4,118
    Admittedly, this is a sad situation. C++ designers have ignored this for decades, pretending that iomanips and the overloaded << and >> operators can replace the easy (though bug-prone) format flags of <stdio.h>.
    Your question can be split into two separate issues: how to store a raw array of bytes, and how to format a string. For the former purpose, I *would* use vector<char>. It gives you three advantages: you can easily splice new bytes into the array, you can use the STL algorithms to manipulate the data easily. Finally, you are freed from manual memory management.
    Alternatively, if you really must use a built-in array rather than a class (for example, becaus eyou want to restrict the buffer to the stack and never allocate it from the heap), uses std::tr1::array<T> class.
    As for format flags: Boost has a Boost.Format library that supports <stdio.h> style format flags with a C++ iostream interface. It takes some time to digest it, and you need to download and install that library because it's not available in Standard C++ (not even C++09, as yet at least). But it does the trick.
    To be honest? I'm inclined to propose the following: if you all you need is convert data types automatically and safely, use stringstream. If you need to format and edit a string, stick to stdio.h format flags and sprintf. It's not an orthodox solution but no one is going to convince me that manipulators and overloaded operators can do the same job, as easily, intuitively and efficiently as stdio.h format flags.
    Last edited by Danny; 06-01-2008 at 07:34 AM.
    Danny Kalev

  9. #9
    Join Date
    Dec 2003
    Posts
    3,366
    Thanks Danny. I was mostly just going through my code, and asking myself why I never use certain parts of the stl, and strings came to the top of that list so I started a small test program and .... couldnt get them to do anything.

    I probably will replace the "byte arrays" and leave the "strings" as they are.

  10. #10
    Join Date
    Dec 2007
    Posts
    401
    to format, edit and manipulate character sequences, you may find the Boost String Algorithms Library (specially in conjunction with the C++09 / Boost Regex Library) quite nifty.
    http://www.boost.org/doc/libs/1_35_0...ing_algo.intro
    i find this combination to be both more powerful and also much easier to use correctly (wrt C stdio.h and string.h). here is an appetizer:
    Code:
    #include <string>
    #include <iostream>
    #include <iterator>
    #include <boost/algorithm/string.hpp>
    #include <boost/regex.hpp>
    #include <boost/algorithm/string/regex.hpp>
    #include <algorithm>
    #include <vector>
    /**
    link with the regex library. eg:
    > g++42 -Wall -std=c++98 -pedantic -Werror -I /usr/local/include -L /usr/local/lib -lboost_regex string_manip.cc
    **/
    
    using namespace std ;
    using namespace boost ;
    
    inline string paranthesize_formatter( const iterator_range<string::const_iterator>& range )
    { return string( "(" + string( range.begin(), range.end() ) + ")" ) ; }
    
    int main()
    {
      {
        string str1 = "abc__(456)__123__(123)__cde" ;
        cout << replace_all_regex_copy( str1, regex("\\(([0-9]+)\\)"), string("#$1#") ) << '\n' ;
        cout << erase_all_regex_copy( str1, regex("[[:alpha:]]+") ) << '\n' ;
        replace_all_regex( str1, regex("_(\\([^\\)]*\\))_"), string("-$1-") );
        cout << str1 << '\n' ;
      }
      
      {
        string str2= "abC-xxxx-AbC-xxxx-abc" ;
        cout << erase_all_copy( str2, "xxxx" ) << endl;
        cout << replace_all_copy( str2, "xxxx", "yyy" ) << endl;
        cout << find_format_all_copy( str2, first_finder( "abc", is_iequal() ),
                                      paranthesize_formatter ) << '\n' ;
      }
    
      {
        string str3 = "abc1234def->ijkl->mnop56qrs->tuv7890wxyz" ;
        vector< string > tokens ;
        boost::algorithm::split_regex( tokens, str3, regex( "[0-9]+|->" ) ) ;
        copy( tokens.begin(), tokens.end(),
              ostream_iterator<string>( cout, "\n" ) ) ;
      }
    
      {
        string str4 = "123<(abc 56 xyz>]7890" ;
        cout << trim_copy_if( str4, is_digit() ) << '\n' ;
        cout << trim_copy_if( trim_copy_if( str4, is_digit() ),
                              is_any_of("<>[]()") ) << '\n' ;
      }
    }
    output:
    Code:
    abc__#456#__123__#123#__cde
    __(456)__123__(123)__
    abc_-(456)-_123_-(123)-_cde
    abC--AbC--abc
    abC-yyy-AbC-yyy-abc
    (abC)-xxxx-(AbC)-xxxx-(abc)
    abc
    def
    ijkl
    mnop
    qrs
    tuv
    wxyz
    <(abc 56 xyz>]
    abc 56 xyz

  11. #11
    Join Date
    May 2007
    Posts
    843
    Vijayan, Danny and Jonnin has did a good job here.

Similar Threads

  1. Replies: 6
    Last Post: 02-23-2009, 02:13 PM
  2. Strings in Structures
    By S0n1C in forum C++
    Replies: 8
    Last Post: 03-31-2007, 08:31 PM
  3. deleting empty strings
    By Matrix.net in forum .NET
    Replies: 6
    Last Post: 10-16-2006, 01:10 PM
  4. Replies: 5
    Last Post: 06-19-2006, 10:48 AM
  5. Allocating memory in VB (Custom strings)
    By Mark Alexander Bertenshaw in forum VB Classic
    Replies: 4
    Last Post: 03-04-2002, 08:04 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center