• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 578
  • Last Modified:

problems with string and char pointer

Hello. I try to read this file into a string. But I only get some strange characters as output.
It works when I pass the filename as string directly like ifstream("test.html")

      string ab = "test.html";
      ifstream file(ab.c_str());

      // Get the size of the file - the only cross-platform way
      file.seekg(0, ios_base::end);
      long size = file.tellg();
      // Create the char * and retrieve the contents
      char *temp_contents = new char[size + 1];
      memset(temp_contents, size + 1, 0);
      // Go back to the beginning of the file
      file.seekg(0, ios_base::beg);

      // Read the contents
      file.read(temp_contents, size);

      // Assign the contents
      string contents = temp_contents;
      delete [] temp_contents;

      cout << contents;
0
m-jansen
Asked:
m-jansen
  • 20
  • 10
  • 8
  • +1
2 Solutions
 
rstaveleyCommented:
> memset(temp_contents, size + 1, 0);

memset(temp_contents, 0, size + 1);

> string contents = temp_contents;

string contents(temp_contents, size);
0
 
rstaveleyCommented:
Your temp_contents character array will not have been an ASCIIZ ('\0'-terminated string) because of the memset usage error.
0
 
itsmeandnobodyelseCommented:
>>>> Get the size of the file - the only cross-platform way

No, there is a portable way using stat.h

#include <sys/stat.h>


    ...
    struct stat filestat;
    if (stat(ab.c_str(), &filestat) == 0)
    {
          // file exists
          int size = filestat.st_size;
    }

Note, the size you get is a binary size. If you read text files at Windows platform using text mode, the real size is less than st_size as Windows converts any carriagereturn-linefeed pair to one single linefeed character.

>>>> It works when I pass the filename as string directly like ifstream("test.html")

Don't think that this is true. There is no difference.

I assume the problem is here:

>>>> file.seekg(0, ios_base::end);

Here - most likely - the EOF bit was set, what makes the stream fail for the next operations. You would have to reset the bit by calling file.clear() but I strongly recommend to use stat function.

Regards, Alex

0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
itsmeandnobodyelseCommented:
>>>> will not have been an ASCIIZ ('\0'-terminated string) because of the memset usage error

Yes, rstaveley is right.

You could omit the memset and do that:

    // Read the contents
    file.read(temp_contents, size);
    int nBytesRead =  file.gcount();
    temp_contents[nBytesRead] = '\0';
 
     // Assign the contents
     string contents = temp_contents;

Regards, Alex
0
 
rstaveleyCommented:
BTW... It's preferable to avoid character arrays, pointers etc.

Here's a neat way to load a std::string using stream buffer iterators, which are very efficient. It uses the iterator constructor for std::string.
--------8<--------
#include <iostream>
#include <string>
#include <fstream>
#include <iterator>
using namespace std;

int main()
{
      string ab = "test.html";
      ifstream file(ab.c_str());
      typedef istreambuf_iterator<char> Itr;
      string contents(Itr(file),(Itr()));
      cout << contents;
}
--------8<--------
0
 
srinimsCommented:
there won't be any difference between usage
 of
     string ab = "test.html";
     ifstream file(ab.c_str());
or
     ifstream file("test.html");

the error might be due to some other thing

srini ms

0
 
srinimsCommented:
use

memset(temp_contents, 0,size+1);

instead of

memset(temp_contents, size+1, 0);


0
 
m-jansenAuthor Commented:
>>>>>> It works when I pass the filename as string directly like ifstream("test.html")

>>Don't think that this is true. There is no difference

Sorry I meant:
ifstream file("test.html");
And that works
0
 
itsmeandnobodyelseCommented:
>>>> to load a std::string using stream buffer iterators

Wow.

Unfortunately, it isn't easy to remember ...

What do you think of that?

#include <iostream>
#include <string>
#include <fstream>
#include <sys/stat.h>
using namespace std;

int main()
{
    string ab = "test.html";
    struct stat filestat;
    if (stat(ab.c_str(), &filestat) != 0)
        return 1;
    string contents(filestat.st_size, '\0');
    ifstream file(ab.c_str());
    file.read(contents.begin(), contents.length());
    contents.resize(file.gcount());
    cout << contents;
    return 0;
}


Regards, Alex

0
 
itsmeandnobodyelseCommented:
>>>> ifstream file("test.html");
>>>> And that works

It works by incident, means the buffer contains zeros by incident what makes a correct termination.
0
 
m-jansenAuthor Commented:
file.read(contents.begin(), contents.length()); // Error      4      error C2664: 'std::basic_istream<_Elem,_Traits>::read' : cannot convert parameter 1 from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'char *'
0
 
m-jansenAuthor Commented:
:rstaveley
With this method:

#include <iostream>
#include <string>
#include <fstream>
#include <iterator>
using namespace std;

int main()
{
     string ab = "test.html";
     ifstream file(ab.c_str());
     typedef istreambuf_iterator<char> Itr;
     string contents(Itr(file),(Itr()));
     cout << contents;
}
the out put looks like:
x&#9786;:

And that's not right.
0
 
m-jansenAuthor Commented:
>>there won't be any difference between usage
>> of
>>     string ab = "test.html";
>>     ifstream file(ab.c_str());
>>or
>>     ifstream file("test.html");
>>
>>the error might be due to some other thing
>>
>>srini ms
What could it be? I have no clue.
0
 
m-jansenAuthor Commented:
I tried with
memset(temp_contents, 0,size+1);
But it didn't help
0
 
srinimsCommented:
it's working fine with microsoft compiler in windows.
can we know, which one is using in which os
0
 
m-jansenAuthor Commented:
i also use microsoft compiler in windows (vs8)
0
 
m-jansenAuthor Commented:
Alex:
I get error in your last code example at:
file.read(contents.begin(), contents.length()); // Error     4     error C2664: 'std::basic_istream<_Elem,_Traits>::read' : cannot convert parameter 1 from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'char *'
0
 
srinimsCommented:
try to read some text file which doesn't contain any html tag.
and try in small file.
0
 
srinimsCommented:
is your program works correctly with
ifstream file("test.html") ?
0
 
m-jansenAuthor Commented:
same strange output when test.html contains:
hdkshafiwdohwidxpnwdcjiwpdqoncqwjiedopc
wqcndwquicndhwqiudconqwio

my code looks like this:
      string ab;
      ab = "test.html";
      ifstream file(ab.c_str());

      // Get the size of the file - the only cross-platform way
      file.seekg(0, ios_base::end);
      long size = file.tellg();
      // Create the char * and retrieve the contents
      char *temp_contents = new char[size + 1];
      //memset(temp_contents, size + 1, 0);
      memset(temp_contents, 0, size + 1);

      // Go back to the beginning of the file
      file.seekg(0, ios_base::beg);

      // Read the contents
      file.read(temp_contents, size);

      // Assign the contents
      //string contents = temp_contents;
      string contents(temp_contents, size);

      delete [] temp_contents;

      cout << contents;
0
 
m-jansenAuthor Commented:
>>is your program works correctly with
>>ifstream file("test.html") ?
yes, except that Alex says that it works by incident
0
 
srinimsCommented:
try to debug on each place. the file pointer get created, reading part. and keep a watch on the memory of character pointer, on which it is getting read.

and let us know the any difference in
ifstream file. by passing direct string and string.c_str()
0
 
itsmeandnobodyelseCommented:
>>>> I tried with
>>>> memset(temp_contents, 0,size+1);
>>>> But it didn't help

You might have to check whether the read was successfull:

     // Create the char * and retrieve the contents
     char *temp_contents = new char[size + 1];
     memset(temp_contents, 0, size + 1 );
     // Go back to the beginning of the file
     file.seekg(0, ios_base::beg);

     // Read the contents
     if (!file.read(temp_contents, size))
     {
            // you have to include <errno.h>
            // On Windows platform you may use GetLastError() instead of errno
            cout << " read failed, error = " << errno << endl;
            return 2;
     }
     int nBytesRead = file.gcount();
     temp_contents[nBytesRead] = '\0';

>>there won't be any difference between usage
>> of
>>     string ab = "test.html";
>>     ifstream file(ab.c_str());
>>or
>>     ifstream file("test.html");

The only difference is that the compiler may allocate some dynamic memory for string ab. So,

     char *temp_contents = new char[size + 1];

might get a different allocation because of that.

Did you try my solution using stat.h and std::string?

Regards, Alex


 

0
 
m-jansenAuthor Commented:
hey now it works...
0
 
m-jansenAuthor Commented:
I was messing. Was in debug mode and tried to run my app in the release map.... :) Sorry sorry.
I still wonder about that solution to Alex and why it does not compile.
0
 
m-jansenAuthor Commented:
I'm looking at the last code now...
0
 
m-jansenAuthor Commented:
Alex:
Why does not this code compile:
#include <iostream>
#include <string>
#include <fstream>
#include <sys/stat.h>
using namespace std;

int main()
{
    string ab = "test.html";
    struct stat filestat;
    if (stat(ab.c_str(), &filestat) != 0)
        return 1;
    string contents(filestat.st_size, '\0');
    ifstream file(ab.c_str());
    file.read(contents.begin(), contents.length()); // get error here: Error      4      error C2664: 'std::basic_istream<_Elem,_Traits>::read' : cannot convert parameter 1 from 'std::_String_iterator<_Elem,_Traits,_Alloc>' to 'char *'
    contents.resize(file.gcount());
    cout << contents;
    return 0;
}
0
 
m-jansenAuthor Commented:
What can it be?
0
 
m-jansenAuthor Commented:
I would prefer the platform independent code.
0
 
rstaveleyCommented:
> the out put looks like:
> x&#9786;:

Is the HTML file a wide character file? [If you open it with notepad, what does it say its encoding is, if you file save as...?]
0
 
m-jansenAuthor Commented:
ANSI
0
 
itsmeandnobodyelseCommented:
>>>> What can it be?

You have a different compiler version. At VC6 string::begin() returns a char*.

   file.read(&contents[0], contents.length());

That should compile at any platform.

Regards, Alex
0
 
rstaveleyCommented:
That x&#9786; is a &#9786;
0
 
rstaveleyCommented:
[Smiley face.]
0
 
rstaveleyCommented:
What were you expecting to see?
0
 
m-jansenAuthor Commented:
I remeber I saw a smiley face...

but I expected to see a lot of text.
btw: my problem is solved. It works great now. I just wait for Alex to tell me why I get comile error on his method, because I want to use his method.
0
 
rstaveleyCommented:
See http:/M_1365934.html#16487462 for Alex's fix. His initial solution assumed that the iterator was the same type as a char*, which wasn't a portable assumption.
0
 
rstaveleyCommented:
I mean http:/Q_21818805.html#16487462 (got the wrong link from view source)
0
 
m-jansenAuthor Commented:
thanks
0
 
m-jansenAuthor Commented:
How could your method be used in a dedicated method?
What to do with return 1;?

inline string FileToString(string ab) {
    string ab = "test.html";
    struct stat filestat;
    if (stat(ab.c_str(), &filestat) != 0)
        return 1;
    string contents(filestat.st_size, '\0');
    ifstream file(ab.c_str());
      file.read(&contents[0], contents.length());
    contents.resize(file.gcount());
    return contents;
}
0
 
rstaveleyCommented:
Try throwing an exception.
0
 
itsmeandnobodyelseCommented:
>>>> which wasn't a portable assumption.

Bingo.

>>>> file.read(&contents[0], contents.length());

Actually, that's a trick cause std::string officially doesn't provide a writeable char buffer. However, as std::string::operator[] returns a writeable reference of the first character of the *internal* string buffer it works (and will work in future).

If you don't want to work with tricks you might consider the following solution:

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

int main()
{
    string ab = "test.html";
    string contents, line;
    ifstream file(ab.c_str());
    while (getline(file, line))
    {
          contents += line;
          contents += '\n';
    }
    cout << contents << endl;
    return 0;
}

or

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;

int main()
{
    string ab = "test.html";
    string line;
    vector<string> lines;
    ifstream file(ab.c_str());
    while (getline(file, line))
    {
          lines.push_back(line);
    }
    for (int i = 0; i < lines.size(); ++i)
        cout << lines[i] << endl;
    return 0;
}

You see, both standard solutions were less tricky and error-prone than all solutions above.

Regards, Alex
0
 
itsmeandnobodyelseCommented:
>>>> What to do with return 1;?

Generally, there are two valid error concepts:

A. exceptions

B. error returns

With A you need to put all code that might throw an exception within try-catch blocks and either handle the error or throw it again. Finally in main or WinMain there should be a try-catch where you could output an "I am sorry" message (and may be more if your exception classes were properly defined). I personally don't like exceptions cause they make the code ugly and were rarely complete in real projects ...

B means (normally) that methods that could go wrong, do log the error to a logfile or print some message to the screen (or both). Then the methods return a bool (false) or an error code different from OK (which can be defined in an enum).  

>>>> string FileToString(string ab)

That is a *conversion* method cause it returns the output type. Case (A) you would need tto call it like that:

    string contents;
    try
    {
          contents = FileToString("ab.html");
    }
    catch (MyException me)
    {
           // handle exception or throw it again
    }
    catch (...)
    {
         // handle unknown exception
    }

I think you understand why I don't like exceptions ...


With (B) you would have two choices:

B1:
        string contents = FileToString("ab.html");
        if (contents.empty())
        {
               // something goes wrong
        }

B2:
      string contents;
      if (!GetStringFromFile("ab.html", contents))
      {
            // something goes wrong
      }

       bool GetStringFromFile(const string& ab, string& contents);

I personally would prefer B1 over B2 (whenever, the return type could be checked for an error return) but both methods were valid.

In both cases you would log the original error message, e. g. "GetStringFromFile: File ab.html does not exist", to a logfile when it occurs by using a standard logging function.

Regards, Alex
0
 
m-jansenAuthor Commented:
Thanks a lot all
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 20
  • 10
  • 8
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now