2011-11-27 212 views
4

我有一個大文件,我只需要從它得到最後一行(\n只是行分隔符)。
我需要這個在iOS設備上完成,所以它不會佔用太多的內存或CPU時間(如閱讀整個文件)。
如何在Objective-C,C++或C++ 11中執行此操作?讀取文件的最後一行

回答

3

我在我的生產代碼中有這個功能。想法是試着通過查找和閱讀來閱讀最後一行。請看一看。

bool readLastLine(std::string const& filename, std::string& lastLine) 
{ 
    std::ifstream in(filename.c_str(),std::ifstream::binary); 
    if(!in) return false; 
    in.seekg(0, std::ifstream::end); 
    const std::streamoff len = in.tellg(); 
    //empty file 
    if(len == 0) 
    { 
     lastLine = ""; 
     return true; 
    } 
    int buf_size = 128; 
    std::vector<char> buf; 
    while(in) 
    { 
     if(buf_size > len) 
     { 
      buf_size = len; 
     } 
     buf.resize(buf_size); 
     in.seekg(0 - buf_size, std::ifstream::end); 
     in.read(&buf[0],buf_size); 
     //all content is in the buffer or we already have the complete last line 
     if(len == buf_size || std::count(buf.begin(), buf.end(), '\n') > 1) 
     { 
      break; 
     } 
     //try enlarge the buffer 
     buf_size *= 2; 
    } 
    //find the second line seperator from the end if any 
    auto i = std::find(++buf.rbegin(),buf.rend(), '\n'); 
    lastLine.assign(i == buf.rend() ? buf.begin() : buf.begin() + std::distance(i, buf.rend()), buf.begin() + buf_size); 
    return true; 
} 
+0

在這裏你有一個問題,你多次閱讀結尾,並且當它沒有壞道時多次查看結束行 - 你已經閱讀過,並且你知道它沒有新行。 – Dani

+0

@Dani你可以做任何優化。對於大多數情況,只要最後一行的長度小於128,我們只讀取最後一行。我可以將默認緩衝區大小更改爲512.它適用於我的產品。 – BruceAdi

5

從概念上講,我認爲你應該打開文件並尋找到最後減去N個字節(可能是80或者其他東西)的整個方法。然後閱讀並查找\ n。如果你沒有找到它,那麼先查找N個字節,然後在N個字節的集合上嘗試,直到找到\ n。

至於具體的調用,這只是一個查找如何打開一個文件,尋找它並讀取數據的問題。應該很簡單。但我認爲以上是你想要做的,並選擇一個不太大的N大小。

+0

確實N = 80比N = 1有什麼優勢? – Dani

+0

@Dani - 少了'fseek'調用。 – MByD

+0

是的,尋求的速度可能會很慢,而幾個閱讀速度與閱讀速度幾乎一樣。 –

2

@Nerdtron答案似乎是最適合我,如果你沒有在你的文件格式的控制,但是...

如果你有在文件格式的控制,你可以做到這一點與O(1)複雜性。只需將文件開頭的最後一行的起始偏移量寫入(常量)偏移量中,然後再向其中寫入數據。當你想讀取它時,讀取這個偏移量,並轉到它指定的偏移量。

0

我想出了這個,試圖改善布魯斯,上面爲緩衝區並不需要調整,只是不停地從EOF以往更遠讀取字符的相同大小的塊:

std::string lastLine(std::ifstream &file) 
{ 
    if (!file.good()) throw exception("Bad stream on input"); 

    const size_t bufSize = 80; // because why not? tweak if need to 
    char buf[bufSize]; 
    string line; 

    int seek, nloff; 
    // iterate over multiples of bufSize while file ok 
    for (size_t n = 1; file; ++n) 
    { 
     // next seek position will be a multiple of bufSize 
     seek = -static_cast<int>(n * bufSize); 
     file.seekg(seek, file.end); 
     // read "bufSize" bytes into buffer 
     file.read(buf, bufSize); 

     // in case no newline found, seek past eof 
     nloff = -seek; 
     // find offset of last newline in buffer 
     for (size_t i = 0; i < bufSize; ++i) 
     { 
      if (buf[i] == '\n') nloff = i; 
     } 
     seek += nloff + 1; // new seek position is one character after found newline 
     if (seek >= 0) continue; // just kidding about the "past eof" part ;) 

     // seek to after found newline and get line 
     file.seekg(seek, file.end); 
     getline(file, line); 
     if (!line.empty()) break; // have result, break and return 
    } 

    if (file.good()) return line; 
    else return string(); 
} 
相關問題