Monday, April 11, 2011

Alternative to fgets()?

Description:

  • Obtain output from an executable

Note:

  • Will not compile, due to fgets() declaration

Question:

  • What is the best alternative to fgets, as fgets requires char *?
  • Is there a better alternative?

Illustration:

void Q_analysis (const char *data)
{
string buffer;
size_t found;
found = buffer.find_first_of (*data);

FILE *condorData = _popen ("condor_q", "r");
while (fgets (buffer.c_str(), buffer.max_size(), condorData) != NULL)
{
 if (found == string::npos)
 {
  Sleep(2000);
 } else {
  break;
 }
}
return;
}
From stackoverflow
  • You should be using the string.getline function for strings cppreference

    however in your case, you should be using a char[] to read into.

    eg

    string s;
    char buffer[ 4096 ];
    fgets(buffer, sizeof( buffer ), condorData);
    s.assign( buffer, strlen( buffer ));
    

    or your code:

    void Q_analysis( const char *data )
    {
     char buffer[ 4096 ];
    
     FILE *condorData = _popen ("condor_q", "r");
     while( fgets( buffer, sizeof( buffer ), condorData ) != NULL )
     {
      if( strstr( buffer, data ) == NULL )
      {
        Sleep(2000);
      }
      else
      {
        break;
      }
     }
    }
    
    anon : Doesn't work with FILE *
    sfossen : ya, I re-read the question and realized it was a pipe.
  • Instead of declaring you buffer as a string declare it as something like:

    char buffer[MY_MAX_SIZE]
    

    call fgets with that, and then build the string from the buffer if you need in that form instead of going the other way.

    The reason what you're doing doesn't work is that you're getting a copy of the buffer contents as a c-style string, not a pointer into the gut of the buffer. It is, by design, read only.

    -- MarkusQ

  • You're right that you can't read directly into a std::string because its c_str and data methods both return const pointers. You could read into a std::vector<char> instead.

    You could also use the getline function. But it requires an iostream object, not a C FILE pointer. You can get from one to the other, though, in a vendor-specific way. See "A Handy Guide To Handling Handles" for a diagram and some suggestions on how to get from one file type to another. Call fileno on your FILE* to get a numeric file descriptor, and then use fstream::attach to associate it with an fstream object. Then you can use getline.

    anon : But note the vector won't grow like I think the OP is expecting
  • Try the boost library - I believe it has a function to create an fstream from a FILE*

    or you could use fileno() to get a standard C file handle from the FILE, then use fstream::attach to attach a stream to that file. From there you can use getline(), etc. Something like this:

    FILE *condorData = _popen ("condor_q", "r");
    std::ifstream &stream = new std::ifstream();
    stream.attach(_fileno(condorData));
    
  • I haven't tested it all too well, but the below appears to do the job:

    //! read a line of text from a FILE* to a std::string, returns false on 'no data'
    bool stringfgets(FILE* fp, std::string& line)
    {
      char buffer[1024];
      line.clear();
    
      do {
        if(!fgets(buffer, sizeof(buffer), fp))
          return !line.empty();
    
        line.append(buffer); 
      } while(!strchr(buffer, '\n'));
      return true;
    }
    

    Be aware however that this will happily read a 100G line of text, so care must be taken that this is not a DoS-vector from untrusted source files or sockets.

0 comments:

Post a Comment