2013-01-17 51 views
2

我在C++ 11中實現一個簡單的文件解析器時遇到問題,該解析器逐行讀取文件並標記行。它應該妥善管理其資源。解析器的用法應該是這樣的:在C++中實現逐行文件解析器時遇到困難

Parser parser; 
parser.open("/path/to/file"); 
std::pair<int> header = parser.getHeader(); 
while (parser.hasNext()) { 
    std::vector<int> tokens = parser.getNext(); 
} 
parser.close(); 

所以Parser類需要一個成員std::ifstream file

1)構造函數應該如何初始化this->file(或std::ifstream* file?)?

2)open方法應如何將this->file設置爲輸入文件?

3)文件的下一行應該如何加載到字符串中? (這是你會用的:std::getline(this->file, line))?

你能給點建議嗎?理想情況下,您可以將該課程作爲代碼示例進行勾畫。

回答

3

由於Parser可能是在一個相當無用的狀態,一旦你建造它,你打開文件之前,我建議讓您的使用情況是這個樣子:

Parser parser("/path/to/file"); 
std::pair<int> header = parser.getHeader(); 
while (parser.hasNext()) { 
    std::vector<int> tokens = parser.getNext(); 
} 
parser.close(); 

在這種情況下,你應該使用構造函數的成員初始化列表來初始化file MEMB ER(其中,是的,應該是std::ifstream類型):

Parser::Parser(std::string file_name) 
    : file(file_name) 
{ 
    // ... 
} 

如果你不停的構造函數和open成員函數分開,你可以只留構造爲默認因爲file成員將默認構建的給你文件流不與任何文件關聯。然後你會得到Parser::open到文件名轉發到std::ifstream::open,像這樣:

void Parser::open(std::string file_name) 
{ 
    file.open(file_name); 
} 

然後,是的,從文件中讀取行,你想使用類似這樣的東西:

std::string line; 
while (std::getline(file, line)) { 
    // Do something with line 
} 

做好沒有陷入做while (!file.eof())的陷阱。

+0

是否'的std :: getline'工作如果不是從while循環調用,但每次調用getNext()時都是如此? – clstaudt

+0

@cls是的,它的工作原理完全一樣。確保你用'if(std :: getline(file,line))'檢查它是否成功。 –

3

它可以用很多方式設計。

  1. 您可能會要求用戶爲您提供一個流而不是指定文件名。 這將更通用,並將在所有流中工作。

這樣,你應該有一個std::ifstream&成員變量,雖然你可以有一個指針類型很好,但你需要做*_stream <<調用任何操作。

  1. 如果你把一個文件,你墊在你的構造函數構造一個流,如果打開關閉它在析構函數
2

實際上,還有一種方法可以將文件的名稱提供給Parser:您可以爲它提供一個std::istream。有趣的是,這種方式可以使用任何派生類std::istream,因此您可以提供它,例如,std::istringstream,這使得編寫單元測試更容易。

class Parser { 
public: 
    explicit Parser(std::istream& is); 

    /**/ 

private: 
    std::istream& _stream; 
    /**/ 
}; 

接下來,迭代。在C++中有一個has後跟一個get並不是慣用的。 std::istream支持迭代(使用輸入迭代器),你可以完美地設計你的解析器,所以它也可以。這樣你就可以獲得與許多STL算法兼容的好處。

class ParserIterator: 
    public std::iterator< std::input_iterator_tag, std::vector<int> > 
{ 
public: 
    ParserIterator(): _stream(nullptr) {} // end 

    ParserIterator(std::istream& is): _stream(&is) { this->advance(); } 

    // Accessors 
    std::vector<int> const& operator*() const { return _vec; } 
    std::vector<int> const* operator->() const { return &_vec; } 

    bool equals(ParserIterator const& other) const { 
     if (_stream != other._stream) { return false; } 

     if (_stream == nullptr) { return true; } 

     return false; 
    } 

    // Modifiers 
    ParserIterator& operator++() { this->advance(); return *this; } 

    ParserIterator operator++(int) { 
     ParserIterator tmp(*this); 
     this->advance(); 
     return tmp; 
    } 
private: 
    void advance() { 
     assert(_stream && "cannot advance an end iterator"); 

     _vec.clear(); 

     std::string buffer; 
     if (not getline(*_stream, buffer)) { 
      _stream = 0; // end of story 
     } 

     // parse here 
    } 

    std::istream* _stream; 
    std::vector<int> _vec; 
}; // class ParserIterator 

inline bool operator==(ParserIterator const& left, ParserIterator const& right) { 
    return left.equals(right); 
} 

inline bool operator!= (parserIterator const& left, ParserIterator const& right) { 
    return not left.equals(right); 
} 

有了這樣的,我們可以增加我們的解析器:

ParserIterator Parser::begin() const { 
    return ParserIterator(_stream); 
} 

ParserIterator Parser::end() const { 
    return ParserIterator(); 
} 

我將離開getHeader方法和實際的解析內容給你;)