0

I am a beginner at C++ programming, and I encountered an issue. I want to be able to convert the contents of a file to a char*, and I used file and string streams. However, it's not working.

This is my function that does the work:

char* fileToChar(std::string const& file){

    std::ifstream in(file);
    if (!in){
        std::cout << "Error: file does not exist\n"; 
        exit(EXIT_FAILURE);
    }

    std::stringstream buffer;
    buffer << in.rdbuf() << std::flush;
    in.close();

    return const_cast<char *>(buffer.str().c_str());
}

However, when I test the method out by outputting its contents into another file like this:

std::ofstream file("test.txt");
file << fileToChar("fileTest.txt");

I just get tons of strange characters like this:

îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþ[...etc]

What exactly is going on here? Is there anything I missed? And if there's a better way to do this, I would be glad to know!

  • 2
    It's so sad that beginner C++ programmers learn about `char*`. – DeiDei Oct 31 '15 at 17:05
  • 1
    One question per question please. If you have a new question, go and post a new question. You may include a link to this one if the context is relevant. – Baum mit Augen Oct 31 '15 at 17:32
  • 2
    StackOverflow stopped me from posting and it says that I can only post every 90 minutes. I'll post the other question into a new question after the time anyways. – flylord1234 Oct 31 '15 at 17:46
  • 1
    @sciencelord: I can understand that this bothers you. In your case, there would be absolutely no problem with multiple questions in shorter time, but as you may imagine, not all new users in a huge network like SO behave reasonably. It's just kind of a quality-assurance mechanism. – Christian Hackl Oct 31 '15 at 17:48

3 Answers3

4
return const_cast<char *>(buffer.str().c_str());

returns a pointer to the internal char buffer of a temporary copy of the internal buffer of the local stringstream. Long story short: As soon as you exit the function, this pointer points to garbage.

Btw, even if that was not a problem, the const_cast would be dangerous nonsense, you are not allowed to write through the pointer std::string::c_str returns. Legitimate uses of const_cast are extremely rare.

And for the better way: The best and easiest way would be returning std::string. Only if this is not allowed, a std::vector<char> (preferred) or new char[somelength] (frowned on) would be viable solutions.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
3
char* fileToChar(std::string const& file){

This line already shows that something is going into the wrong direction. You return a pointer to some string, and it's completely unclear to the user of the function who is responsible for releasing the allocated memory, if it has to be released at all, if nullptr can be returned, and so on.

If you want a string, then by all means use std::string!

std::string fileToChar(std::string const& file){
return const_cast<char *>(buffer.str().c_str());

Another line that should make all alarms go off. const_cast is always a workaround to some underlying problem (or some problem with external code).

There is usually a good reason why something is const. By forcing the compiler to turn off the security check and allowing it to attempt modifications of unmodifiable data, you typically turn compilation errors into hard-to-diagnose run-time errors.

Even if this function worked correctly, any attempt to modify the result would be undefined behaviour:

char* file_contents = fileToChar("foo.txt");
file_contents[0] = 'x'; // undefined behaviour

But it does not work correctly anyway. buffer.str() returns a temporary std::string object. c_str() returns a pointer to that temporary object's internally managed memory. The object's lifetime ends when the full expression return const_cast<char *>(buffer.str().c_str()) has been evaluated. Using the resulting pointer is therefore undefined behaviour, too.


The problems sound complicated, but the fix is easy. Make the function return std::string and turn the last statement into return buffer.str();.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
-1

If your question is, how to read the content of a file into an buffer, consider my following suggestion. But take care that buffer is big enough for the file content. A file size check and preallocation of the memory is advised before calling fileToChar().

bool fileToChar(std::string const& file, char* buffer, unsigned int &buffer_size )
{
    FILE *f = fopen( file.c_str(), "rb" );

    if( f == nullptr )
    {
        return false;
    }

    fseek(f , 0, SEEK_END );

    const int size = ftell( f );

    rewind( f );
    fread( buffer, 1, size, f );
    fclose( f );

    return true;
}
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
Viktor Liehr
  • 508
  • 4
  • 14
  • @ChristianHackl: Apperently, somebody has -1 day, today. – Viktor Liehr Oct 31 '15 at 17:38
  • First of all, your tone is inappropriate. Second, I did *not* downvote this answer, even though it contains some IMO questionable advice and employs a strange mix between C and C++. – Christian Hackl Oct 31 '15 at 17:40
  • @ChristianHackl: Can you explain further what you think that my tone is inappropriate? And can you explain is in this example to strange to mix C und C++. Both language features go hand in hand, why not using them? – Viktor Liehr Oct 31 '15 at 17:46
  • Accusing someone of unjustified anonymous downvoting and being cynical about it is what I would consider inappropriate. Mixing C and C++ is always a rather strange thing to do, because they are different languages with different goals, very often co-existing just for backward compatibility. For example, C means more pointers, no exceptions, less type safety, no automatic cleanup via destructors. What you get for that is less "invisible" code but a more or less 1:1 mapping between code and behaviour. – Christian Hackl Oct 31 '15 at 17:51
  • @ChristianHackl: Aha, very interesting. So you are saying that to achieve a goal, in this case reading the file content, is strange to use easy C function that are readable without looking into the documentation? What pitfalls do you see in my code snippet? – Viktor Liehr Oct 31 '15 at 17:56
  • Yes, I am saying that, and I contest the claim that these C functions are "easy to use". Some pitfalls I can see: `buffer` could be `nullptr`, `buffer_size` is unused, `buffer_size` could be incorrect, `SEEK_END` means potential portability/maintenance troubles. What are the advantages to a pure C++ solution? – Christian Hackl Oct 31 '15 at 18:05
  • @ChristianHackl: I hope you read this this my post, "A file size check and preallocation of the memory is advised before calling fileToChar()". Where do you see portability/maintenance problems?? You have far less portability issues with C than with C++. Again, I do not see your point! – Viktor Liehr Oct 31 '15 at 18:09
  • I have read that. But you should also show the check and preallocation it in your answer. As for `SEEK_END`, I'd like to refer to http://stackoverflow.com/questions/19045637/should-i-use-fseek-seek-end (or other questions about `SEEK_END` here on SO). Note that I have nothing against C, but a C++ question should generate C++ answers, not C answers. – Christian Hackl Oct 31 '15 at 18:21
  • P.S.: I don't know if the downvoter shares all my opinions, of course. – Christian Hackl Oct 31 '15 at 18:21
  • @ChristianHackl: Thanks for pointing to the other SO thread. It is stated. "Since C++ inherits fseek and SEEK_END from C", so it covers my opinion. Why should I give a copy+paste code in my answer. when I describe what the user has to do to use it? Sorry buddy. but I still missing what you are about to. – Viktor Liehr Oct 31 '15 at 18:34