2012-08-09 15 views
7

मैं केवल एक टेक्स्ट फ़ाइल की अंतिम पंक्ति पढ़ना चाहता हूं (मैं यूनिक्स पर हूं, बूस्ट का उपयोग कर सकता हूं)। मुझे पता है कि सभी विधियों को अंतिम फ़ाइल प्राप्त करने के लिए पूरी फ़ाइल के माध्यम से स्कैनिंग की आवश्यकता है जो बिल्कुल कुशल नहीं है। क्या केवल अंतिम पंक्ति पाने का कोई प्रभावी तरीका है?सी ++ पाठ फ़ाइल की केवल अंतिम पंक्ति पढ़ने के लिए सबसे तेज़ तरीका?

इसके अलावा, मुझे यह पर्याप्त मजबूत होने की आवश्यकता है कि यह काम करता है भले ही पाठ फ़ाइल में लगातार दूसरी प्रक्रिया द्वारा जोड़ा जा रहा हो।

+0

क्या कोई * कुछ * है जो किसी के तथ्य में मजबूत है * लगातार * फ़ाइल को संशोधित करता है? आप उस परिस्थिति में "मजबूत" को कैसे परिभाषित करेंगे? –

+1

@ user788171 आप अंत तक खोजना और लाइन टर्मिनेटर के लिए पीछे स्कैन करने में सक्षम होना चाहिए। मैं शायद सुझाव दूंगा कि आप कच्ची फ़ाइल का उपयोग न करें, हालांकि, ऐसा लगता है कि आप एक पाइप चाहते हैं। – oldrinb

उत्तर

15

उपयोग seekg, फ़ाइल के अंत में जाने के लिए फिर वापस पढ़ा जब तक आपको पहली नई लाइन न मिल जाए। नीचे एमएसवीसी का उपयोग कर मेरे सिर के ऊपर से कुछ नमूना कोड है।

#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

तो, मैंने वास्तव में इसका परीक्षण किया और यह वास्तव में काम नहीं करता है। आखिरी रेखा हमेशा खाली होती है। – user788171

+3

मजेदार, मैंने पोस्ट करने से पहले इसका परीक्षण किया। क्या आपके test.txt के अंत में एक अतिरिक्त खाली रेखा है? – derpface

+0

यह मेरे लिए काम नहीं करता है क्योंकि [टेक्स्टफाइल को एक नए लाइन वर्ण के साथ समाप्त होना चाहिए] (https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline) और [कई संपादक स्वचालित रूप से उस चरित्र को सम्मिलित करते हैं] (https://stackoverflow.com/questions/14171254/why-would-vim-add-a-new-line-at-the-end-of-a-file)। – phinz

4

तब तक कूदें, और जब तक आप किसी रेखा के लिए अपना मानदंड नहीं पाते हैं तब तक ब्लॉक को पीछे से पढ़ना शुरू करें। यदि अंतिम ब्लॉक किसी रेखा के साथ "अंत" नहीं होता है, तो आपको संभवतः आगे भी कोशिश करने और स्कैन करने की आवश्यकता होगी (फ़ाइल में सक्रिय रूप से संलग्न में वास्तव में लंबी लाइन मानना)।

+0

आप अंत तक कैसे कूदते हैं और पीछे की ओर ब्लॉक पढ़ना शुरू करते हैं? – user788171

+0

@ user788171 आईट्रीम :: seekg (0, ios_base :: end) जैसे कुछ का उपयोग करके। फिर आप स्ट्रीम में आगे/पीछे की ओर जाने के लिए वहां से तलाश कर सकते हैं। – Yuushi

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'? –

+0

@ जेसे मेरी गलती, आप सही हैं। – carter2000

0

मैं समस्या पर भी संघर्ष कर रहा था क्योंकि मैं उबेरवूलू कोड चला गया और खाली रेखा भी मिली। यहां मुझे मिला है। मैं एक उदाहरण के रूप में निम्नलिखित .csv फ़ाइल उपयोग कर रहा हूँ:

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

कोड में आदेश को समझने के लिए निम्न स्थानों और उनकी संगत वर्ण नोटिस करें। (लोक, चार): ... (63, '3'), (64, '5'), (65, -), (66, '\ n'), (ईओएफ, -)।

#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

जबकि डरपैस द्वारा उत्तर निश्चित रूप से सही है, यह अक्सर अप्रत्याशित परिणाम देता है। इसका कारण यह है कि, कम से कम मेरे ऑपरेटिंग सिस्टम (मैक ओएसएक्स 10.9.5) पर, कई टेक्स्ट एडिटर्स अपनी फाइलों को 'एंड लाइन' चरित्र के साथ समाप्त कर देते हैं।

उदाहरण के लिए, जब मैं vim खोलते हैं, तो सिर्फ एक वर्ण 'एक' (कोई वापसी) टाइप करें, और बचाने के लिए, फ़ाइल अब (हेक्स में) में शामिल होंगे:

61 0A 

कहाँ 61 अक्षर है 'ए' और 0 ए लाइन वर्ण का अंत है।

इसका मतलब है कि डरपैस द्वारा कोड ऐसे टेक्स्ट एडिटर द्वारा बनाई गई सभी फ़ाइलों पर एक खाली स्ट्रिंग लौटाएगा।

जबकि मैं निश्चित रूप से ऐसे मामलों की कल्पना कर सकता हूं जहां एक 'अंत रेखा' से समाप्त फ़ाइल को खाली स्ट्रिंग वापस करनी चाहिए, मुझे लगता है कि नियमित पाठ फ़ाइलों से निपटने के दौरान अंतिम 'अंत पंक्ति' चरित्र को अनदेखा करना अधिक उपयुक्त होगा; अगर फ़ाइल को 'एंड लाइन' वर्ण से समाप्त कर दिया गया है, तो हम इसे ठीक से अनदेखा करते हैं, और यदि फ़ाइल को 'एंड लाइन' वर्ण से समाप्त नहीं किया गया है तो हमें इसे जांचने की आवश्यकता नहीं है।

इनपुट फ़ाइल का अंतिम वर्ण अनदेखी के लिए मेरे कोड है:

#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 

एकल 'एक' फ़ाइल पर।

संपादित करें: फ़ाइल if((int)fin.tellg() <= 0){ वास्तव में समस्या का कारण बनती है यदि फ़ाइल बहुत बड़ी है (> 2 जीबी), क्योंकि टेलग फ़ाइल (tellg() function give wrong size of file?) की शुरुआत से वर्णों की संख्या को वापस नहीं लौटाता है। फ़ाइल fin.tellg()==tellgValueForStartOfFile की शुरुआत के लिए अलग-अलग परीक्षण करना और fin.tellg()==-1 त्रुटियों के लिए बेहतर हो सकता है। tellgValueForStartOfFile शायद 0 है, लेकिन यह सुनिश्चित करने के लिए एक बेहतर तरीका शायद होगा:

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

प्रारंभ में यह पिछले syslog प्रविष्टि को पढ़ने के लिए डिजाइन किया गया था। यह देखते हुए कि ईओएफ से पहले अंतिम चरित्र '\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; 
} 
संबंधित मुद्दे