2011-01-27 18 views
10

ifstream के साथ खोले गए फ़ाइल में वर्तमान पंक्ति की लाइन संख्या प्राप्त करने का सबसे अच्छा तरीका क्या होगा? इसलिए मैं डेटा में पढ़ रहा हूं और मुझे उस लाइन नंबर को स्टोर करने की आवश्यकता है, जिस पर यह है कि यदि मैं विनिर्देशों से मेल नहीं खाता तो मैं इसे बाद में प्रदर्शित कर सकता हूं।सी ++ में किसी फ़ाइल से लाइन नंबर कैसे प्राप्त करें?

उत्तर

9

किसी इस्टस्ट्रीम बिंदु से कोई लाइन नंबर नहीं है। यदि आप लाइन लाइन में लाइन लाइन में पढ़ते हैं, तो आपको बस इसे ट्रैक रखना होगा।

+0

नोट: आप या तो लाइन ('std :: getline') द्वारा लाइन पढ़ सकते हैं या बस पास होने वाले \ n' वर्णों की संख्या को गिन सकते हैं। –

+0

@ मैथियू: यह मान रहा है कि प्रत्येक '\ n' आपके प्लेटफॉर्म पर एक नई लाइन का प्रतीक है। मुझे नई लाइन पर कॉल ट्रैक करना बेहतर लगता है (एक साधारण रैपर करेगा ...) – rubenvb

+0

@ रूबेनव: पाठ मोड में \ n' एक नई पंक्ति का प्रतिनिधित्व करता है, 'iostream' इसे बनाने के लिए लोकेल का उपयोग करता है (दोनों पढ़ने और लेखन)। यदि आप बाइनरी मोड में पढ़ते हैं, तो निश्चित रूप से कोई रूपांतरण नहीं होता है ... –

4

प्रत्येक पंक्ति को एक-एक करके पढ़ने के लिए std::getline का उपयोग करें। आपके द्वारा पढ़ी गई लाइनों की संख्या को इंगित करने वाला एक पूर्णांक रखें: इसे शून्य पर प्रारंभ करें और प्रत्येक बार जब आप std::getline पर कॉल करते हैं और यह सफल होता है, तो इसे बढ़ाएं।

8

तो आप std::streambuf से व्युत्पन्न वर्ग इस्तेमाल कर सकते हैं std::getline अपने आप को सीमित करने के लिए नहीं आप करते तो चाहते हैं, और जो वर्तमान पंक्ति संख्या का ट्रैक रखता है:

class CountingStreamBuffer : public std::streambuf { /* see below */ }; 

// open file 
std::ifstream file("somefile.txt"); 

// "pipe" through counting stream buffer 
CountingStreamBuffer cntstreambuf(file.rdbuf()); 
std::istream is(&cntstreambuf); 

// sample usage 
is >> x >> y >> z; 
cout << "At line " << cntstreambuf.lineNumber(); 
std::getline(is, str); 
cout << "At line " << cntstreambuf.lineNumber(); 

यहाँ एक है CountingStreamBuffer का नमूना कार्यान्वयन:

#include <streambuf> 

class CountingStreamBuffer : public std::streambuf 
{ 
public: 
    // constructor 
    CountingStreamBuffer(std::streambuf* sbuf) : 
     streamBuf_(sbuf), 
     lineNumber_(1), 
     lastLineNumber_(1), 
     column_(0), 
     prevColumn_(static_cast<unsigned int>(-1)), 
     filePos_(0) 
    { 
    } 

    // Get current line number 
    unsigned int  lineNumber() const { return lineNumber_; } 

    // Get line number of previously read character 
    unsigned int  prevLineNumber() const { return lastLineNumber_; } 

    // Get current column 
    unsigned int  column() const { return column_; } 

    // Get file position 
    std::streamsize  filepos() const { return filePos_; } 

protected: 
    CountingStreamBuffer(const CountingStreamBuffer&); 
    CountingStreamBuffer& operator=(const CountingStreamBuffer&); 

    // extract next character from stream w/o advancing read pos 
    std::streambuf::int_type underflow() 
    { 
     return streamBuf_->sgetc(); 
    } 

    // extract next character from stream 
    std::streambuf::int_type uflow() 
    { 
     int_type rc = streamBuf_->sbumpc(); 

     lastLineNumber_ = lineNumber_; 
     if (traits_type::eq_int_type(rc, traits_type::to_int_type('\n'))) 
     { 
      ++lineNumber_; 
      prevColumn_ = column_ + 1; 
      column_ = static_cast<unsigned int>(-1); 
     } 

     ++column_; 
     ++filePos_; 
     return rc; 
    } 

    // put back last character 
    std::streambuf::int_type pbackfail(std::streambuf::int_type c) 
    { 
     if (traits_type::eq_int_type(c, traits_type::to_int_type('\n'))) 
     { 
      --lineNumber_; 
      lastLineNumber_ = lineNumber_; 
      column_ = prevColumn_; 
      prevColumn_ = 0; 
     } 

     --column_; 
     --filePos_; 

     if (c != traits_type::eof()) 
      return streamBuf_->sputbackc(traits_type::to_char_type(c)); 
     else 
      return streamBuf_->sungetc(); 
    } 

    // change position by offset, according to way and mode 
    virtual std::ios::pos_type seekoff(std::ios::off_type pos, 
            std::ios_base::seekdir dir, 
            std::ios_base::openmode mode) 
    { 
     if (dir == std::ios_base::beg 
     && pos == static_cast<std::ios::off_type>(0)) 
     { 
      lastLineNumber_ = 1; 
      lineNumber_ = 1; 
      column_ = 0; 
      prevColumn_ = static_cast<unsigned int>(-1); 
      filePos_ = 0; 

      return streamBuf_->pubseekoff(pos, dir, mode); 
     } 
     else 
      return std::streambuf::seekoff(pos, dir, mode); 
    } 

    // change to specified position, according to mode 
    virtual std::ios::pos_type seekpos(std::ios::pos_type pos, 
            std::ios_base::openmode mode) 
    { 
     if (pos == static_cast<std::ios::pos_type>(0)) 
     { 
      lastLineNumber_ = 1; 
      lineNumber_ = 1; 
      column_ = 0; 
      prevColumn_ = static_cast<unsigned int>(-1); 
      filePos_ = 0; 

      return streamBuf_->pubseekpos(pos, mode); 
     } 
     else 
      return std::streambuf::seekpos(pos, mode); 
    } 


private: 
    std::streambuf*  streamBuf_;  // hosted streambuffer 
    unsigned int  lineNumber_; // current line number 
    unsigned int  lastLineNumber_;// line number of last read character 
    unsigned int  column_;  // current column 
    unsigned int  prevColumn_; // previous column 
    std::streamsize  filePos_;  // file position 
}; 
0

एक अक्षम लेकिन मृत सरल तरीका है एक ऐसा फ़ंक्शन है जिसने स्ट्रीम दिया है, यह धारा की शुरुआत से वर्तमान स्थिति तक नए लाइन वर्णों की गणना करता है। यह अक्षम है क्योंकि यदि आप कई स्ट्रीम पोजिशन के लिए लाइन जानना चाहते हैं, तो आपको प्रत्येक बार स्ट्रीम की शुरुआत से गिनती शुरू करने के लिए इसे कई बार कॉल करना होगा। कुछ कोड में मैं काम कर रहा हूं, अगर मुझे अमान्य इनपुट का सामना करना पड़ता है तो मुझे केवल लाइन नंबर जानने में दिलचस्पी है, उस मामले में आयात तुरंत रद्द कर दिया जाता है। चूंकि फ़ंक्शन को केवल एक बार अक्षमता कहा जाता है जब वास्तव में कोई समस्या नहीं होती है।

विधि के
int getCurrentLine(std::istream& is) 
{ 
    int lineCount = 1; 
    is.clear();  // need to clear error bits otherwise tellg returns -1. 
    auto originalPos = is.tellg(); 
    if (originalPos < 0) 
     return -1; 
    is.seekg(0); 
    char c; 
    while ((is.tellg() < originalPos) && is.get(c)) 
    { 
     if (c == '\n') ++lineCount; 
    } 
    return lineCount; 
} 

लाभ यह है कि कोई बदलाव नहीं जगह है जहाँ धारा का निर्माण किया है पर जरूरत है है, तो आप सिर्फ समारोह जहां आपको उसकी आवश्यकता कॉल करने की आवश्यकता:

यहाँ ऐसा करने के लिए मेरे कोड है । निम्नलिखित एक पूर्ण उदाहरण है:

#include <iostream> 
#include <sstream> 


int getCurrentLine(std::istream& is) 
{ 
    int lineCount = 1; 
    is.clear();  // need to clear error bits otherwise tellg returns -1. 
    auto originalPos = is.tellg(); 
    if (originalPos < 0) 
     return -1; 
    is.seekg(0); 
    char c; 
    while ((is.tellg() < originalPos) && is.get(c)) 
    { 
     if (c == '\n') ++lineCount; 
    } 
    return lineCount; 
} 

void ReadDataFromStream(std::istream& s) 
{ 
    double x, y, z; 
    while (!s.fail() && !s.eof()) 
    { 
     s >> x >> y >> z; 
     if (!s.fail()) 
      std::cout << x << "," << y << "," << z << "\n"; 
    } 

    if (s.fail()) 
     std::cout << "Error at line: " << getCurrentLine(s) << "\n"; 
    else 
     std::cout << "Read until line: " << getCurrentLine(s) << "\n"; 
} 

int main(int argc, char* argv[]) 
{ 
    std::stringstream s; 
    s << "0.0 0.0 0.0\n"; 
    s << "1.0 ??? 0.0\n"; 
    s << "0.0 1.0 0.0\n"; 
    ReadDataFromStream(s); 

    std::stringstream s2; 
    s2 << "0.0 0.0 0.0\n"; 
    s2 << "1.0 0.0 0.0\n"; 
    s2 << "0.0 1.0 0.0"; 
    ReadDataFromStream(s2); 

    return 0; 
} 
संबंधित मुद्दे