Home > database >  Issue using FILE* with std::getline()
Issue using FILE* with std::getline()

Time:09-16

I would like to get some advice on an issue I've encountered once working on small adjustments to an existing program.

The program itself has to:

  1. Open a file and read it line-by-line preferably
  2. Pack the lines into istringstream and then split to 2 strings on a ':' separator
  3. Insert those 2 strings line1 and line2 into an existing std::map container
  4. Than I can do more stuff with the map and the data from it.

My code looks like that:

int main()
{
    FILE *fpFile;
    map<string, string>mapOfPci;
    std::string tempBuff="";
    std::string line1="", line2="";
        
    fpFile = fopen(PCI_MAPPING_PATH, "r");
    
    if(!fpFile)
        return false;
    
    while(getline(fpFile, tempBuff)){
        istringstream iSs(tempBuff);
        iSs >> line1;
        iSs.ignore(numeric_limits<std::streamsize>::max(), ':');
        iSs >> line2;
        mapOfPci.insert(make_pair(line1, line2)); 
    }
    for(const auto &m : mapOfPci){
        cout << m.first << " : " << m.second << "\n";
    }
    
    fclose(fpFile);
    return (0);
}

Now what I'm getting in my compiler feedback is:

mismatched types 'std::basic_istream<_CharT, _Traits>' and 'FILE* {aka _iobuf*}'
  while(getline(fpFile, tempBuff))

At this point I presume that this is due to the usage of FILE* file handling method.

I might not be able to use the C std::ifstream, std::fstream, so is there any method to move this further with the current FILE* usage?

CodePudding user response:

std::getline() expects an std::istream-derived class, like std::ifstream, so you simply can't pass your own FILE* to it (unless you wrap it inside of a custom std::streambuf-derived object assigned to a standard std::istream object. std::ifstream uses std::filebuf, which uses FILE* internally, but you can't supply it with your own FILE*).

Otherwise, you can use C's getline() function instead, but it doesn't work with std::string, as it allocates its own output char[] buffer which you will then have to free afterwards (you can assign the contents of that buffer to a std::string, though), eg:

#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <limits>
#include <cstdio>
using namespace std;

int main()
{
    FILE *fpFile = fopen(PCI_MAPPING_PATH, "r");
    if (!fpFile)
        return false;
    
    map<string, string> mapOfPci;
    char *tempBuff = nullptr;
    size_t size = 0;
    int nRead;

    while ((nRead = getline(&tempBuff, &size, fpFile)) != -1){
        istringstream iSs(string(tempBuff, nRead));
        string line1, line2;
        iSs >> line1;
        iSs.ignore(numeric_limits<streamsize>::max(), ':');
        iSs >> line2;
        mapOfPci.insert(make_pair(line1, line2)); 
        free(tempBuff); tempBuff = nullptr;
        size = 0;
    }
    free(tempBuff);

    for(const auto &m : mapOfPci){
        cout << m.first << " : " << m.second << "\n";
    }
    
    fclose(fpFile);
    return 0;
}

But, since you are using other C standard classes, there really is no good reason not to use std::ifstream instead, eg:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <limits>
using namespace std;

int main()
{
    ifstream fpFile(PCI_MAPPING_PATH);
    if (!fpFile.is_open())
        return false;
    
    map<string, string> mapOfPci;
    string tempBuff;

    while (getline(fpFile, tempBuff)){
        istringstream iSs(tempBuff);
        string line1, line2;
        iSs >> line1;
        iSs.ignore(numeric_limits<streamsize>::max(), ':');
        iSs >> line2;
        mapOfPci.insert(make_pair(line1, line2)); 
    }

    for(const auto &m : mapOfPci){
        cout << m.first << " : " << m.second << "\n";
    }
    
    return 0;
}
  • Related