2017-08-01 57 views
4

我遇到了使用C++從文件讀取msg的問題。通常人們做的是創建一個文件流,然後使用getline()函數來獲取味精。 getline()函數可以接受一個額外的參數作爲分隔符,以便它返回由新分隔符分隔的每個「行」,但不是默認的「\ n」。但是,這個分隔符必須是char。在我的用例中,msg中的分隔符可能與「| - |」類似,所以我嘗試獲得解決方案,以便它接受字符串作爲分隔符而不是char。使用任意分隔符從FileStream中讀取

我已經搜索了一下StackOverFlow,發現了一些有趣的帖子。 Parse (split) a string in C++ using string delimiter (standard C++) 這一個給出了一個解決方案,使用string::find()string::substr()解析任意分隔符。然而,所有的解決方案假設輸入是一個字符串,而不是一個流,在我的情況下,文件流數據太大/浪費,以便一次裝入內存,因此它應該通過msg讀取msg(或msg中的大部分msg一旦)。

實際上,通過讀取gdb實現的std::getline()函數,似乎更容易處理的情況分隔符是一個單字符。由於每次加載大量字符時,都可以搜索分隔符並將它們分開。雖然分隔符不止一個字符是不同的,但分隔符本身可能會跨越兩個不同的塊並導致許多其他角落案例。

不確定是否有其他人曾經遇到過這種要求,以及您是如何優雅地處理它的。似乎有一個像istream& getNext (istream&& is, string& str, string delim)這樣的標準功能會很好嗎?這似乎是我的一般用例。爲什麼不在Standard庫中,這樣人們就不再單獨實現自己的版本了?

非常感謝您

+0

帶字符串的getline需要向前看,因此它可能會比較慢。只是猜測。我們需要實現我們自己的定製getline。 – AndyG

+0

有沒有優雅的實現。正如你所提到的,lookahead使代碼變得複雜。也許FSM是一個優雅的解決方案? –

+0

向前看將是一個簡單的FSM哈哈,只是不像正則表達式那麼複雜。程序的要點是讀取字符,直到達到「定界符」狀態,然後將這些字符解析爲字符串。如果您只是對「有效」解決方案感興趣,可以使用「std :: vector」並進行遊戲。 「最優」解決方案會稍微困難一些。如果沒有人回答,我會寫點東西。 – AndyG

回答

0

如果你確定與字節讀取字節,你可以建立一個有限狀態機的狀態轉移表實現識別您的停止條件

std::string delimeter="someString"; 
//initialize table with a row per target string character, a column per possible char and all zeros 
std::vector<vector<int> > table(delimeter.size(),std::vector<int>(256,0)); 
int endState=delimeter.size(); 
//set the entry for the state looking for the next letter and finding that character to the next state 
for(unsigned int i=0;i<delimeter.size();i++){ 
    table[i][(int)delimeter[i]]=i+1; 
} 

現在你可以這樣使用它

int currentState=0; 
int read=0; 
bool done=false; 
while(!done&&(read=<istream>.read())>=0){ 
    if(read>=256){ 
     currentState=0; 
    }else{ 
     currentState=table[currentState][read]; 
    } 
    if(currentState==endState){ 
     done=true; 
    } 
    //do your streamy stuff 
} 
授予如果分隔符是ASCII擴展這僅適用

,但它會正常工作像你的榜樣一些事情。

0

STL根本不支持你要求的東西。你將不得不編寫你自己的功能(或者找到第三方功能),以滿足你的需求。

例如,您可以使用std::getline()來讀取分隔符的第一個字符,然後使用std::istream::get()來讀取後續字符並將它們與分隔符的其餘部分進行比較。例如:

std::istream& my_getline(std::istream &input, std::string &str, const std::string &delim) 
{ 
    if (delim.empty()) 
     throw std::invalid_argument("delim cannot be empty!"); 

    if (delim.size() == 1) 
     return std::getline(input, str, delim[0]); 

    str.clear(); 

    std::string temp; 
    char ch; 
    bool found = false; 

    do 
    { 
     if (!std::getline(input, temp, delim[0])) 
      break; 

     str += temp; 

     found = true; 

     for (int i = 1; i < delim.size(); ++i) 
     { 
      if (!input.get(ch)) 
      { 
       if (input.eof()) 
        input.clear(std::ios_base::eofbit); 

       str.append(delim.c_str(), i); 
       return input; 
      } 

      if (delim[i] != ch) 
      { 
       str.append(delim.c_str(), i); 
       str += ch; 
       found = false; 
       break; 
      } 
     } 
    } 
    while (!found); 

    return input; 
} 
0

看來,這是最容易產生類似getline():讀取到分離器的最後字符。然後檢查字符串是否足夠分隔符,如果是,則以分隔符結束。如果不是,請繼續閱讀:

std::string getline(std::istream& in, std::string& value, std::string const& separator) { 
    std::istreambuf_iterator<char> it(in), end; 
    if (separator.empty()) { // empty separator -> return the entire stream 
     return std::string(it, end); 
    } 
    std::string rc; 
    char  last(separator.back()); 
    for (; it != end; ++it) { 
     rc.push_back(*it); 
     if (rc.back() == last 
      && separator.size() <= rc.size() 
      && rc.substr(rc.size() - separator.size()) == separator) { 
      return rc.resize(rc.size() - separator.size()); 
     } 
    } 
    return rc; // no separator was found 
} 
相關問題