2012-08-09 105 views
7

我想只讀取文本文件的最後一行(我在UNIX上,可以使用Boost)。我所知道的所有方法都需要掃描整個文件以獲取最後一行,而這根本沒有效率。有沒有一種有效的方法來獲得最後一行?c +最快的方式來只讀取最後一行文本文件?

此外,我需要它足夠強大,即使有問題的文本文件不斷被另一個進程追加,它也能正常工作。

+0

是否有*任何*在某人的事實中穩健*不斷*修改文件?在那種情況下,你甚至會如何定義「健壯」? – 2012-08-09 03:19:00

+1

@ user788171您應該能夠尋找到最後並向後掃描行終止符。我可能會建議你不要在這裏使用原始文件,因爲它聽起來更像是你想要一個管道。 – oldrinb 2012-08-09 03:23:48

回答

15

使用seekg跳轉至文件末尾,然後讀回直到找到第一個換行符。 下面是一些使用MSVC關閉頭部的示例代碼。

#include <iostream> 
#include <fstream> 
#include <sstream> 

using namespace std; 

int main() 
{ 
    string filename = "test.txt"; 
    ifstream fin; 
    fin.open(filename); 
    if(fin.is_open()) { 
     fin.seekg(-1,ios_base::end);    // go to one spot before the EOF 

     bool keepLooping = true; 
     while(keepLooping) { 
      char ch; 
      fin.get(ch);       // Get current byte's data 

      if((int)fin.tellg() <= 1) {    // If the data was at or before the 0th byte 
       fin.seekg(0);      // The first line is the last line 
       keepLooping = false;    // So stop there 
      } 
      else if(ch == '\n') {     // If the data was a newline 
       keepLooping = false;    // Stop at the current position. 
      } 
      else {         // If the data was neither a newline nor at the 0 byte 
       fin.seekg(-2,ios_base::cur);  // Move to the front of that data, then to the front of the data before it 
      } 
     } 

     string lastLine;    
     getline(fin,lastLine);      // Read the current line 
     cout << "Result: " << lastLine << '\n';  // Display it 

     fin.close(); 
    } 

    return 0; 
} 

下面是一個測試文件。它在文本文件中以空行,單行和多行數據成功。

This is the first line. 
Some stuff. 
Some stuff. 
Some stuff. 
This is the last line. 
+1

所以,我實際測試了它,它並沒有實際工作。 lastLine始終爲空。 – user788171 2013-05-29 21:24:56

+3

有趣的是,我在發佈之前對它進行了測試。你的test.txt在最後有一個額外的空行嗎? – derpface 2013-05-29 23:48:38

+0

這對我不起作用,因爲[textfiles應該以一個新行字符結尾](https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline)和[many編輯自動插入該字符](https://stackoverflow.com/questions/14171254/why-would-vim-add-a-new-line-at-the-end-of-a-file)。 – phinz 2018-01-19 09:24:08

4

跳轉到最後,並開始向後讀取塊,直到找到某條線的標準爲止。如果最後一個塊沒有以某一行「結束」,那麼您可能還需要嘗試向前掃描(假設主動附加到文件中的行很長)。

+0

你究竟如何跳到最後並開始向後讀塊呢? – user788171 2012-08-09 03:28:37

+0

@ user788171通過使用類似istream :: seekg(0,ios_base :: end)的東西。然後,您可以使用seekg在流中向前/向後移動。 – Yuushi 2012-08-09 03:37:32

1

您可以使用seekg()跳轉到文件的末尾,落後的閱讀,僞代碼如下:

ifstream fs 
fs.seekg(ios_base::end) 
bytecount = fs.tellg() 
index = 1 
while true 
    fs.seekg(bytecount - step * index, ios_base::beg) 
    fs.read(buf, step) 
    if endlinecharacter in buf 
     get endlinecharacter's index, said ei 
     fs.seekg(bytecount - step*index + ei) 
     fs.read(lastline, step*index - ei) 
     break 
    ++index 
+0

'seekg'也許? – 2012-08-09 03:40:27

+0

@JesseGood我的錯誤,你是對的。 – carter2000 2012-08-09 12:45:47

0

我也在努力解決這個問題,因爲我運行了uberwulu的代碼並且也有空白行。 這是我發現的。我使用下列.csv文件爲例:

date  test1 test2 
20140908  1  2 
20140908  11  22 
20140908  111 235 

要了解在代碼中的命令,請注意下面的位置及其對應的字符。 (Loc,char):...(63,'3'),(64,'5'),(65, - ),(66,'\ n'),(EOF, - )。

#include<iostream> 
#include<string> 
#include<fstream> 

using namespace std; 

int main() 
{ 
    std::string line; 
    std::ifstream infile; 
    std::string filename = "C:/projects/MyC++Practice/Test/testInput.csv"; 
    infile.open(filename); 

    if(infile.is_open()) 
    { 
     char ch; 
     infile.seekg(-1, std::ios::end);  // move to location 65 
     infile.get(ch);       // get next char at loc 66 
     if (ch == '\n') 
     { 
      infile.seekg(-2, std::ios::cur); // move to loc 64 for get() to read loc 65 
      infile.seekg(-1, std::ios::cur); // move to loc 63 to avoid reading loc 65 
      infile.get(ch);      // get the char at loc 64 ('5') 
      while(ch != '\n')     // read each char backward till the next '\n' 
      { 
       infile.seekg(-2, std::ios::cur);  
       infile.get(ch); 
      } 
      string lastLine; 
      std::getline(infile,lastLine); 
      cout << "The last line : " << lastLine << '\n';  
     } 
     else 
      throw std::exception("check .csv file format"); 
    } 
    std::cin.get(); 
    return 0; 
} 
1

雖然derpface的答案絕對正確,但它通常會返回意想不到的結果。原因在於,至少在我的操作系統(Mac OSX 10.9.5)中,許多文本編輯器使用'end line'字符來終止它們的文件。

例如,當我打開vim,輸入只是單個字符「A」(沒有返回),然後保存,現在,該文件將包含(十六進制):

61 0A 

其中61封信'a'和0A是行尾字符。

這意味着derpface的代碼將在由這樣的文本編輯器創建的所有文件上返回一個空字符串。雖然我可以想象當一個以'end line'結尾的文件應該返回空字符串時,我認爲在處理常規文本文件時忽略最後一個'end line'字符會更合適;如果文件被一個'end line'字符終止,我們可以忽略它,如果文件沒有被'end line'字符終止,我們不需要檢查它。

我無視輸入文件的最後一個字符的代碼是:

#include <iostream> 
#include <string> 
#include <fstream> 
#include <iomanip> 

int main() { 
    std::string result = ""; 
    std::ifstream fin("test.txt"); 

    if(fin.is_open()) { 
     fin.seekg(0,std::ios_base::end);  //Start at end of file 
     char ch = ' ';      //Init ch not equal to '\n' 
     while(ch != '\n'){ 
      fin.seekg(-2,std::ios_base::cur); //Two steps back, this means we 
               //will NOT check the last character 
      if((int)fin.tellg() <= 0){  //If passed the start of the file, 
       fin.seekg(0);     //this is the start of the line 
       break; 
      } 
      fin.get(ch);      //Check the next character 
     } 

     std::getline(fin,result); 
     fin.close(); 

     std::cout << "final line length: " << result.size() <<std::endl; 
     std::cout << "final line character codes: "; 
     for(size_t i =0; i<result.size(); i++){ 
      std::cout << std::hex << (int)result[i] << " "; 
     } 
     std::cout << std::endl; 
     std::cout << "final line: " << result <<std::endl; 
    } 

    return 0; 
} 

將輸出:

final line length: 1 
final line character codes: 61 
final line: a 

在單一的 'A' 文件。

編輯︰線if((int)fin.tellg() <= 0){實際上導致問題,如果文件太大(> 2GB),因爲tellg不只是從文件的開頭(tellg() function give wrong size of file?)返回的字符數。單獨測試文件fin.tellg()==tellgValueForStartOfFile的開始和錯誤fin.tellg()==-1可能會更好。該tellgValueForStartOfFile可能是0,但要確保一個更好的方式很可能是:

fin.seekg (0, is.beg); 
tellgValueForStartOfFile = fin.tellg(); 
0

最初,這是設計來讀取的最後一個系統日誌條目。鑑於EOF之前的最後一個字符是'\n',我們試圖找到'\n'的下一個匹配項,然後我們將該行存儲到一個字符串中。

#include <fstream> 
#include <iostream> 

int main() 
{ 
    const std::string filename = "test.txt"; 
    std::ifstream fs; 
    fs.open(filename.c_str(), std::fstream::in); 
    if(fs.is_open()) 
    { 
    //Got to the last character before EOF 
    fs.seekg(-1, std::ios_base::end); 
    if(fs.peek() == '\n') 
    { 
     //Start searching for \n occurrences 
     fs.seekg(-1, std::ios_base::cur); 
     int i = fs.tellg(); 
     for(i;i > 0; i--) 
     { 
     if(fs.peek() == '\n') 
     { 
      //Found 
      fs.get(); 
      break; 
     } 
     //Move one character back 
     fs.seekg(i, std::ios_base::beg); 
     } 
    } 
    std::string lastline; 
    getline(fs, lastline); 
    std::cout << lastline << std::endl; 
    } 
    else 
    { 
    std::cout << "Could not find end line character" << std::endl; 
    } 
    return 0; 
} 
相關問題