-1

I have a file, at the end of each line there is possibly a newline:

111\n
100\n
101
imonaboat
  • 29
  • 7

1 Answers1

2

In C++ you can load the lines of a file into an array of byte strings like this:

auto lines_from( istream& is )
    -> vector<string>
{
    string line;
    vector<string> result;
    while( getline( is, line ) )
    {
        result.push_back( line );
    }
    return result;
}

auto main() -> int
{
    vector<string> const lines = lines_from( cin );
    // Use it.
}

Here string is std::string from the <string> header, getline is std::getline from the same header, and vector is std::vector from the <vector> header. I chose to use a descriptive name for the function, lines_from. However, it's commonly named readall.


Where you absolutely need a char**, presumably with an assumption of some given buffer size for each string, then you can use a vector of pointers, pointing to buffers that e.g. are managed by a class like this:

class C_strings
{
private:
    vector<string>  buffers_;
    vector<char*>   pointers_;
    int             bufsize_;

    C_strings( C_strings const& ) = delete;
    auto operator=( C_strings const& ) -> C_strings& = delete;

public:
    auto pointer() -> char** { return pointers_.data(); }
    auto bufsize() const -> int { return bufsize_; }

    C_strings( vector<string> const& strings, int const bufsize )
        : buffers_( strings )
        , bufsize_( bufsize )
    {
        pointers_.reserve( buffers_.size() + 1 );
        for( string& s : buffers_ )
        {
            s.reserve( bufsize );
            if( s.empty() or s.back() != '\0' ) { s += '\0'; }
            pointers_.push_back( &s[0] );
        }
        pointers_.push_back( nullptr );
    }

    C_strings( C_strings&& other )
        : buffers_( move( other.buffers_ ) )
        , pointers_( move( other.pointers_ ) )
    {}
};

Then let's say you want to call a double-star function like this:

void doublestarfunc( char** const lines )
{
    using std::cout;
    for( char** pps = lines; *pps != nullptr; ++pps )
    {
        if( strlen( *pps ) < 40 ) { strcat( *pps, " < Oh la la!" ); }
        cout << *pps << '\n';
    }
    cout << '\n';
}

It can be done very simply:

using namespace std;        // cin, cout
int const columns           = 80;
int const cstring_bufsize   = columns + 1;

auto c_strings = C_strings( lines_from( cin ), cstring_bufsize );
doublestarfunc( c_strings.pointer() );

But is it a good idea? No, except when you have to relate to an existing C style API. For C++ code, better restructure it to use C++ std::string throughout.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I need the data in a char**. Thank you for your code. I will try to see if vector const lines and char** are the same. Otherwise I need to rewrite a large part of my code, I did the file reading in last, thinking it would be easy. – imonaboat Dec 03 '15 at 07:43
  • @imonaboat: You would be well adviced to fix the other code. That means, not using `char**`. However, for completeness I'm adding info about how to get a `char**`. – Cheers and hth. - Alf Dec 03 '15 at 08:56
  • Wow Alf, you have done a lot to help. What can I say? Thank you so much. It does seem like c++ adds a lot to C, and you are using the latest c++ (so says my compiler). – imonaboat Dec 03 '15 at 10:57
  • Your longer answer is really helpful. It shows how to move from C to C++ properly. Which was part of the question. Now I solved my problem by looking at your sizeof() comment and my own question update. scanf does work for files, I add the code to my question. – imonaboat Dec 03 '15 at 12:45