howa wrote:
> Hi,
>
> Are there any simple method to read string into dynamic array of
> string, e.g. char**
Juggling with char* or char** isn't very C++'ish. Normally you
would solve these kind of stuff with
std::(vector|list|any other container of your liking)<std::string>.
But since you insist and since it's a C++ group I would suggest
the following C++'ish approach.
To keep the number of calls to new char[] as small as possible you might
maintain a list of char chunks of a appropriate size. Ideally enough
space to hold all chars of all expected strings.
Write characters into these chunks until they are filled up and
allocate a new chunk whenever there isn't enough space left.
If you happen to read a string with a length exceeding the default
chunk size you have to allocate a private chunk for this one
string.
In an array of char * you also have to remember the starting position
of the current string. This array may have to be re-sized before
overflowing.
<snip>
#include <iostream>
#include <list>
#include <boost/shared_ptr.hpp>
#include <memory>
class charBucket {
public:
enum {
stdChunkSize = 1000,
stdPtrSize = 1000
};
charBucket() : size(0),
max_size(stdPtrSize),ptrs(new const char *[stdPtrSize]) {
ptrs[0]=NULL;
}
void put(const std::string &s) {
boost::shared_ptr<chunk> c;
if(s.length()+1 > stdChunkSize ) {
chunks.push_front(
boost::shared_ptr<chunk>( new chunk(s.length()+1) ) );
c = chunks.front();
} else {
if(chunks.empty() ||
(size_t)(chunks.back()->e-chunks.back()->p) < (s.length()+1) ) {
chunks.push_back( boost::shared_ptr<chunk>(new
chunk(stdChunkSize)) );
}
c = chunks.back();
}
put_ptr(c->p);
::strncpy(c->p,s.c_str(),s.length()+1);
c->p+=s.length()+1;
}
const char ** get() const {
return ptrs;
}
private:
charBucket(const charBucket &);
// Add a char * to ptrs. Resize if necessary
void put_ptr(const char *p) {
if(size == max_size-1) {
max_size+=stdPtrSize;
const char ** np = new const char *[max_size];
for(int i=0;i<size;i++) {
np[i]=ptrs[i];
}
delete[] ptrs;
ptrs=np;
}
ptrs[size++]=p;
ptrs[size]=NULL;
}
// A inner class represents a chunk of
// char. b == beginning, e==end, p==current put pointer
class chunk {
public:
chunk(int s) : b(new char[s]),p(b),e(b+s) {
}
~chunk() {
delete[] b;
}
char *b;
char *p;
char *e;
private:
chunk(const chunk &);
chunk();
};
// List of shared_ptr to chunk
// automatically deleted on destruction of charBucket
std::list< boost::shared_ptr<chunk> > chunks;
int size;
int max_size;
// represents the result i.e. char** returned by the
// get() method
const char ** ptrs;
// debugging
friend
std::ostream & operator<<(std::ostream &, const charBucket &);
};
std::ostream & operator<<(std::ostream & os,const charBucket & cb) {
os
<< "CB:: chunks="
<< cb.chunks.size()
<< "; ptrMaxSize="
<< cb.max_size
<< "; ptrSize=" << cb.size;
return os;
}
int main() {
std::string s;
charBucket c;
int i=0;
std::cerr << "Reading and storing strings from std::cin" << std::endl;
while(std::cin >> s) {
std::cerr << ++i << " '" << s << "'" << std::endl;
c.put(s);
}
std::cerr << "Dumping stored char**" << std::endl;
for(int i=0;c.get()[i]!=NULL;i++) {
std::cerr << i+1 << " '" << c.get()[i] << "'" << std::endl;
}
std::cerr << "CharBucket Info" << std::endl;
std::cerr << c << std::endl;
}
</snip>
All this might be overkill if you just expect a handfull of strings coming
in. Then you might just allocate the chars dynamically for each
string coming in and re-size an associated char* array as needed.
Also note that my proposition only checks in the last chunk of the
chunklist if there is enough space for the current string. It could
traverse the whole list to identify the chunk with enough space left
for the current string.
Hope that helps
O.
--
[ See http://www.gotw.ca/resources/clcm.htm
for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


|