c ++ forma más rápida de leer solo la última línea de archivo de texto?

Me gustaría leer solo la última línea de un archivo de texto (estoy en UNIX, puedo usar Boost). Todos los métodos que conozco requieren escanear todo el archivo para obtener la última línea que no es eficiente en absoluto. ¿Hay una manera eficiente de obtener solo la última línea?

Además, necesito que esto sea lo suficientemente robusto como para que funcione incluso si el archivo de texto en cuestión se adjunta constantemente a otro proceso.

Use seekg para saltar al final del archivo, luego vuelva a leer hasta que encuentre la primera nueva línea. A continuación se muestra un código de muestra de la parte superior de mi cabeza con MSVC.

#include  #include  #include  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; } 

Y a continuación hay un archivo de prueba. Lo logra con datos vacíos, de una sola línea y multilíneas en el archivo de texto.

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

Salta hasta el final y comienza a leer bloques hacia atrás hasta que encuentres tus criterios para una línea. Si el último bloque no “finaliza” con una línea, probablemente también deba intentar escanear hacia adelante (asumiendo una línea muy larga en un archivo adjuntado activamente al archivo).

Puede usar seekg () para saltar al final del archivo y leer hacia atrás, el pseudo-código es como:

 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 

Si bien la respuesta por derpface es definitivamente correcta, a menudo arroja resultados inesperados. La razón de esto es que, al menos en mi sistema operativo (Mac OSX 10.9.5), muchos editores de texto terminan sus archivos con un carácter de ‘línea final’.

Por ejemplo, cuando abro vim, escribo solo el carácter ‘a’ (sin retorno) y guardo, el archivo ahora contendrá (en hexadecimal):

 61 0A 

Donde 61 es la letra ‘a’ y 0A es un carácter de fin de línea.

Esto significa que el código por derpface devolverá una cadena vacía en todos los archivos creados por dicho editor de texto.

Si bien puedo imaginar casos en los que un archivo terminado con una ‘línea final’ debería devolver la cadena vacía, creo que ignorar el último carácter ‘línea final’ sería más apropiado cuando se trata de archivos de texto regulares; si el archivo termina con un carácter de ‘línea final’, lo ignoramos adecuadamente, y si el archivo no termina con un carácter de ‘línea final’, no es necesario que lo verifiquemos.

Mi código para ignorar el último carácter del archivo de entrada es:

 #include  #include  #include  #include  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() < 

Que dará salida:

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

En el único archivo 'a'.

EDITAR: La línea if((int)fin.tellg() <= 0){ realidad causa problemas si el archivo es demasiado grande (> 2GB), porque tellg no solo devuelve el número de caracteres desde el inicio del archivo ( función tellg () da el tamaño incorrecto del archivo? ). Puede ser mejor probar por separado el inicio del archivo fin.tellg()==tellgValueForStartOfFile y para los errores fin.tellg()==-1 . El tellgValueForStartOfFile es probablemente 0, pero una mejor manera de asegurarse sería probablemente:

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

También estaba luchando en el problema porque corrí el código de uberwulu y también obtuve una línea en blanco. Esto es lo que encontré. Estoy usando el siguiente archivo .csv como ejemplo:

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

Para comprender los comandos en el código, observe las siguientes ubicaciones y sus caracteres correspondientes. (Loc, char): … (63, ‘3’), (64, ‘5’), (65, -), (66, ‘\ n’), (EOF, -).

 #include #include #include 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; } 

Inicialmente, esto fue diseñado para leer la última entrada de syslog. Dado que el último carácter antes del EOF es '\n' buscamos de nuevo para encontrar la siguiente aparición de '\n' y luego almacenamos la línea en una cadena.

 #include  #include  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; }