How to load a text file into a char **?

A

amphetaman

Is there a safe (unlikely to cause overflows or segfaults) way to load
a text file into a char ** array? I thought of using getline, but it
needs a fixed-length string, and I don't know how many lines or
characters per line the file has.
 
A

acehreli

Is there a safe (unlikely to cause overflows or segfaults) way to load
a text file into a char ** array? I thought of using getline, but it
needs a fixed-length string, and I don't know how many lines or
characters per line the file has.

You have wrong information: getline needs a string, not a fixed-length
one, and string is never fixed-length anyway.

After you read it in to the string, then you can make a copy of the
contents with the c_str() member function.

Ali
 
A

amphetaman

You have wrong information: getline needs a string, not a fixed-length
one, and string is never fixed-length anyway.

After you read it in to the string, then you can make a copy of the
contents with the c_str() member function.

Ali

I was talking about fstream::getline. I think it takes a char * and a
size argument.

But how would I go about allocating the first part of the array (the
number of lines)?
 
J

James Kanze

That would mean you need to read them all out to count them.

Not really. He could always reimplement the logic of
std::vector.
As I recall, text editors don't have an array of lines, they
keep a linked list. Perhaps that's what you should
consider...

I don't think that there's any absolute rule as to how text
editors work. Many, I believe, keep all of the text in a
single, large buffer, and not stored as lines, often keeping the
text before the cursor at the front, the text after the cursor
at the end, and the empty space in the array at the cursor
position.
 
J

James Kanze

A char** array? Are you sure you got that right?

Maybe he needs to interface with some legacy C code.
// puts the entire contents in a char array
vector<char> read( const char* fileName )
{
vector<char> result;
ifstream file( fileName, ios::binary );
char ch;
while ( file.get( ch ) )
result.push_back( ch );
return result;
}

// puts the entire contents in a char* array,
// breaks file by newlines.
vector< vector<char> > read( const char* fileName )
{
vector< vector<char> > result;
ifstream file( fileName );
string str;
while ( getline( file, str ) ) {
result.push_back( vector<char>( str.begin(), str.end() ) );
result.back().push_back( 0 ); // null terminate each line?
}
return result;
}
I guess for a char** array, you could put each word in a separate
block...

I think he wants one string per line. But I'd still use a
vector of string for the reading, only converting into char**
once the file had been read, e.g.:

std::vector< std::string > tmp ;
std::string line ;
while ( std::getline( file, line ) ) {
tmp.push_back( line ) ;
}
std::vector< char const* > result ;
for ( std::vector< std::string >::const_iterator
iter = tmp.begin() ;
iter != tmp.end() ;
++ iter ) {
result.push_back( iter->c_str() ) ;
}
result.push_back( NULL ) ; // if needed.
// use &result[0].

(In fact, I just did exactly this yesterday, to interface with
openldap.)

Note that in this case, you cannot simply return &result[0], and
expect it to work. For obvious reasons, you must use &result[0]
before either tmp or result go out of scope. In a larger
application, the solution, I think would be to create a class
which contained these two members, contructed the above in its
constructor, and had a function to return the char**. (It the
needed type really is char**, as was the case with openldap,
you'll have to const_cast.)
 
J

James Kanze

A vector<string> would equate to a char* array, not a char** array.

You're right. I was thinking of the more usual case, and just
read it as a typo: an array that you "access" through a char**.
char[] char_array = "hello world";
char*[] char_ptr_array = { "a", "b", "c" };
char**[] char_ptr_ptr_array = ?
Maybe the OP just messed up a bit in his terminology?

Probably. Although there's an outside chance that he wanted an
array of lines, each line being an array of words, I suspect
that that's not really the case.
The below is the same as what I had before, except the last
step of turning the vector<vector<char> > into a char** is
added.

Yes. I was basing my posting on yours, since you seemed to be
the only one posting with the correct approach.
Note that in this case, you cannot simply return &result[0],
and expect it to work. For obvious reasons, you must use
&result[0] before either tmp or result go out of scope. In
a larger application, the solution, I think would be to
create a class which contained these two members, contructed
the above in its constructor, and had a function to return
the char**. (It the needed type really is char**, as was
the case with openldap, you'll have to const_cast.)
I agree, but I say avoid the (multiple) const_casts by using a vector<
vector< char > > instead of a vector< string >.

That's a possibility. I think it depends. If the data really
is supposed to be immutable, and it's just the legacy interface
which doesn't use const, I prefer the const_cast, since it says
what I mean.

In my case (which by coincidence, showed up two days ago), I was
dealing with data from the client; pushing an std::vector<char>
out to the interface level, when I was logically dealing in
strings, didn't seem appropriate (although I find I often use
vectors of char, instead of strings, at the implementation
level).
Note that our solutions are remarkably similar. The only
difference is that I don't have to go through the extra step
to remove the const.

Great minds think alike:). I wasn't criticizing your
suggestions, just completing them. I did think it important to
point out that even if the char** was imposed by some sort of
legacy interface, some sort of std::vector was still the
preferred option. I may prefer vector< string > and the
const_cast, while you prefer vector< vector< char > >, but the
difference is peanuts, compared to the difference between one of
those and char** or char *[].
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,276
Latest member
Sawatmakal

Latest Threads

Top