I have seen many posts but didn't find something like i want.
I am getting wrong output :
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ...... // may be this is EOF character
Going into infinite loop.
My algorithm:
Go to end of file.
decrease position of pointer by 1 and read character by
character.
exit if we found our 10 lines or we reach beginning of file.
now i will scan the full file till EOF and print them //not implemented in code.
code:
#include<iostream>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
int main()
{
FILE *f1=fopen("input.txt","r");
FILE *f2=fopen("output.txt","w");
int i,j,pos;
int count=0;
char ch;
int begin=ftell(f1);
// GO TO END OF FILE
fseek(f1,0,SEEK_END);
int end = ftell(f1);
pos=ftell(f1);
while(count<10)
{
pos=ftell(f1);
// FILE IS LESS THAN 10 LINES
if(pos<begin)
break;
ch=fgetc(f1);
if(ch=='\n')
count++;
fputc(ch,f2);
fseek(f1,pos-1,end);
}
return 0;
}
UPD 1:
changed code: it has just 1 error now - if input has lines like
3enil
2enil
1enil
it prints 10 lines only
line1
line2
line3ÿine1
line2
line3ÿine1
line2
line3ÿine1
line2
line3ÿine1
line2
PS:
1. working on windows in notepad++
this is not homework
also i want to do it without using any more memory or use of STL.
i am practicing to improve my basic knowledge so please don't post about any functions (like tail -5 tc.)
please help to improve my code.
Comments in the code
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *in, *out;
int count = 0;
long int pos;
char s[100];
in = fopen("input.txt", "r");
/* always check return of fopen */
if (in == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
out = fopen("output.txt", "w");
if (out == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
fseek(in, 0, SEEK_END);
pos = ftell(in);
/* Don't write each char on output.txt, just search for '\n' */
while (pos) {
fseek(in, --pos, SEEK_SET); /* seek from begin */
if (fgetc(in) == '\n') {
if (count++ == 10) break;
}
}
/* Write line by line, is faster than fputc for each char */
while (fgets(s, sizeof(s), in) != NULL) {
fprintf(out, "%s", s);
}
fclose(in);
fclose(out);
return 0;
}
There are a number of problems with your code. The most
important one is that you never check that any of the functions
succeeded. And saving the results an ftell in an int isn't
a very good idea either. Then there's the test pos < begin;
this can only occur if there was an error. And the fact that
you're putting the results of fgetc in a char (which results
in a loss of information). And the fact that the first read you
do is at the end of file, so will fail (and once a stream enters
an error state, it stays there). And the fact that you can't
reliably do arithmetic on the values returned by ftell (except
under Unix) if the file was opened in text mode.
Oh, and there is no "EOF character"; 'ÿ' is a perfectly valid
character (0xFF in Latin-1). Once you assign the return value
of fgetc to a char, you've lost any possibility to test for
end of file.
I might add that reading backwards one character at a time is
extremely inefficient. The usual solution would be to allocate
a sufficiently large buffer, then count the '\n' in it.
EDIT:
Just a quick bit of code to give the idea:
std::string
getLastLines( std::string const& filename, int lineCount )
{
size_t const granularity = 100 * lineCount;
std::ifstream source( filename.c_str(), std::ios_base::binary );
source.seekg( 0, std::ios_base::end );
size_t size = static_cast<size_t>( source.tellg() );
std::vector<char> buffer;
int newlineCount = 0;
while ( source
&& buffer.size() != size
&& newlineCount < lineCount ) {
buffer.resize( std::min( buffer.size() + granularity, size ) );
source.seekg( -static_cast<std::streamoff>( buffer.size() ),
std::ios_base::end );
source.read( buffer.data(), buffer.size() );
newlineCount = std::count( buffer.begin(), buffer.end(), '\n');
}
std::vector<char>::iterator start = buffer.begin();
while ( newlineCount > lineCount ) {
start = std::find( start, buffer.end(), '\n' ) + 1;
-- newlineCount;
}
std::vector<char>::iterator end = remove( start, buffer.end(), '\r' );
return std::string( start, end );
}
This is a bit weak in the error handling; in particular, you
probably want to distinguish the between the inability to open
a file and any other errors. (No other errors should occur,
but you never know.)
Also, this is purely Windows, and it supposes that the actual
file contains pure text, and doesn't contain any '\r' that
aren't part of a CRLF. (For Unix, just drop the next to the
last line.)
This can be done using circular array very efficiently.
No additional buffer is required.
void printlast_n_lines(char* fileName, int n){
const int k = n;
ifstream file(fileName);
string l[k];
int size = 0 ;
while(file.good()){
getline(file, l[size%k]); //this is just circular array
cout << l[size%k] << '\n';
size++;
}
//start of circular array & size of it
int start = size > k ? (size%k) : 0 ; //this get the start of last k lines
int count = min(k, size); // no of lines to print
for(int i = 0; i< count ; i++){
cout << l[(start+i)%k] << '\n' ; // start from in between and print from start due to remainder till all counts are covered
}
}
Please provide feedback.
int end = ftell(f1);
pos=ftell(f1);
this tells you the last point at file, so EOF.
When you read, you get the EOF error, and the ppointer wants to move 1 space forward...
So, i recomend decreasing the current position by one.
Or put the fseek(f1, -2,SEEK_CUR) at the beginning of the while loop to make up for the fread by 1 point and go 1 point back...
I believe, you are using fseek wrong. Check man fseek on the Google.
Try this:
fseek(f1, -2, SEEK_CUR);
//1 to neutrialize change from fgect
//and 1 to move backward
Also you should set position at the beginning to the last element:
fseek(f1, -1, SEEK_END).
You don't need end variable.
You should check return values of all functions (fgetc, fseek and ftell). It is good practise. I don't know if this code will work with empty files or sth similar.
Use :fseek(f1,-2,SEEK_CUR);to back
I write this code ,It can work ,you can try:
#include "stdio.h"
int main()
{
int count = 0;
char * fileName = "count.c";
char * outFileName = "out11.txt";
FILE * fpIn;
FILE * fpOut;
if((fpIn = fopen(fileName,"r")) == NULL )
printf(" file %s open error\n",fileName);
if((fpOut = fopen(outFileName,"w")) == NULL )
printf(" file %s open error\n",outFileName);
fseek(fpIn,0,SEEK_END);
while(count < 10)
{
fseek(fpIn,-2,SEEK_CUR);
if(ftell(fpIn)<0L)
break;
char now = fgetc(fpIn);
printf("%c",now);
fputc(now,fpOut);
if(now == '\n')
++count;
}
fclose(fpIn);
fclose(fpOut);
}
I would use two streams to print last n lines of the file:
This runs in O(lines) runtime and O(lines) space.
#include<bits/stdc++.h>
using namespace std;
int main(){
// read last n lines of a file
ifstream f("file.in");
ifstream g("file.in");
// move f stream n lines down.
int n;
cin >> n;
string line;
for(int i=0; i<k; ++i) getline(f,line);
// move f and g stream at the same pace.
for(; getline(f,line); ){
getline(g, line);
}
// g now has to go the last n lines.
for(; getline(g,line); )
cout << line << endl;
}
A solution with a O(lines) runtime and O(N) space is using a queue:
ifstream fin("file.in");
int k;
cin >> k;
queue<string> Q;
string line;
for(; getline(fin, line); ){
if(Q.size() == k){
Q.pop();
}
Q.push(line);
}
while(!Q.empty()){
cout << Q.front() << endl;
Q.pop();
}
Here is the solution in C++.
#include <iostream>
#include <string>
#include <exception>
#include <cstdlib>
int main(int argc, char *argv[])
{
auto& file = std::cin;
int n = 5;
if (argc > 1) {
try {
n = std::stoi(argv[1]);
} catch (std::exception& e) {
std::cout << "Error: argument must be an int" << std::endl;
std::exit(EXIT_FAILURE);
}
}
file.seekg(0, file.end);
n = n + 1; // Add one so the loop stops at the newline above
while (file.tellg() != 0 && n) {
file.seekg(-1, file.cur);
if (file.peek() == '\n')
n--;
}
if (file.peek() == '\n') // If we stop in the middle we will be at a newline
file.seekg(1, file.cur);
std::string line;
while (std::getline(file, line))
std::cout << line << std::endl;
std::exit(EXIT_SUCCESS);
}
Build:
$ g++ <SOURCE_NAME> -o last_n_lines
Run:
$ ./last_n_lines 10 < <SOME_FILE>
Related
I am pretty new to Arduino business. How do I read the last line from a SD Card? With following code snippet I can read the first line (all characters before "\n"). Now I would like to include a "backwards" statement (or something).
My code so far:
#include <SD.h>
#include <SPI.h>
File SD_File;
int pinCS = 10;
char cr;
void setup() {
Serial.begin(9600);
SD.begin();
SD_File = SD.open("test.txt", FILE_WRITE);
SD_File.println("hello");
SD_File.close();
SD_File = SD.open("test.txt");
while(true){
cr = SD_File.read();
if((cr == '\n') && ("LAST LINE?"))
break;
Serial.print(cr);
}
SD_File.close();
}
void loop() {
}
Your help is much appreciated.
Since you are technically opening text files, you could use seekg to jump to the end of the file and read the last line, as described in this answer.
If this is not helpful, adding a bit more context and an example file would help us understand your question better.
I am not sure I understood your question.
"How do I implement seekg?" There is not seekg. There is however, a seek.
This is the documentation page for the SD library. In the right side of the page there is a list of all File class methods (seek among others).
" How do I read the last line..." There is no line reading in your code. If you just want to go to the end of file use: SD_File.seek( SD_File.size() ); If you want to read the last line, the simplest way is to write a getline function and read the whole file line by line until end. Assuming MAX_LINE is large enough and getline returns zero on success:
//...
char s[ MAX_LINE ];
while ( getline( f, s, MAX_LINE , '\n' ) == 0 )
;
// when reaching this point, s contains the last line
Serial.print( "This is the last line: " );
Serial.print( s );
Here's a getline idea (no warranty - not tested):
/*
s - destination
count - maximum number of characters to write to s, including the null terminator. If
the limit is reached, it returns -2.
delim - delimiting character ('\n' in your case)
returns:
0 - no error
-1 - eof reached
-2 - full buffer
*/
int getline( File& f, char* s, int count, char delim )
{
int ccount = 0;
int result = 0;
if ( 0 < count )
while ( 1 )
{
char c = f.peek();
if ( c == -1 )
{
f.read(); // extract
result = -1;
break; // eof reached
}
else if ( c == delim )
{
f.read(); // extract
++ccount;
break; // eol reached
}
else if ( --count <= 0 )
{
result = -2;
break; // end of buffer reached
}
else
{
f.read(); // extract
*s++ = c;
++ccount;
}
}
*s = '\0'; // end of string
return ccount == 0 ? -1 : result;
}
I'm writing code that counts the amount of lines and characters of a file.
#include <fstream>
#include <iostream>
#include <stdlib.h>
using namespace std;
int main(int argc, char* argv[])
{
ifstream read(argv[1]);
char line[256];
int nLines=0, nChars=0, nTotalChars=0;
read.getline(line, 256);
while(read.good()) /
{
nChars=0;
int i=0;
while(line[i]!='\n')
{
if ((int)line[i]>32) {nChars++;}
i++;
}
nLines++;
nTotalChars= nTotalChars + nChars;
read.getline(line, 256);
}
cout << "The number of lines is "<< nLines << endl;
cout << "The number of characters is "<< nTotalChars << endl;
}
The line while(line[i]!='\n') seems to be the cause of the following error
Segmentation fault (core dumped)
I can't figure out what's wrong. The internet tells me that I'm checking for the end of a line correctly as far as I can tell.
Your code will not find '\n' because it is discarded from the input sequence. From the documentation of getline:
The delimiting character is the newline character [...]: when found in the input sequence, it is extracted from the input sequence, but discarded and not written to s.
You should be searching for '\0':
while(line[i])
{
if ((int)line[i]>32) {nChars++;}
i++;
}
Because getline will not store \n, so the loop:
while(line[i]!='\n')
{
if ((int)line[i]>32) {nChars++;}
i++;
}
will never end, until line[i] exceeds the array length and causes segmentation fault.
You do not have an end of line character in the line. So, you should be checking for a NULL character (end of string) instead of the end of line. Also make sure that you do not go past the size of your buffer (256) in your case.
I think a for loop would be safer:
for ( unsigned int i = 0; i < line.size(); i++ ) {
//whatever
}
There are several problems with your code, but for starters, you
shouldn't be reading lines into a char[]. If you use
std::string, then you don't have to worry about reading
partial lines, etc.
Then there's the fact that getline extracts the '\n' from
the file, but does not store it, so your code (even modified
to use std::string) will never see a '\n' in the buffer. If
you're using string, you iterate from line.begin() to
line.end(); if you're using a char[], you iterate over the
number of bytes returned by read.gcount(), called after the
call to getline. (It's very difficult to get this code right
using a char[] unless you assume that no text file in the
world contains a '\0'.)
Finally, if the last line doesn't end with a '\n' (a frequence
case under Windows), you won't process it. If you're using
std::string, you can simply write:
std::getline( read, line );
while ( read ) {
// ...
std::getline( read, line );
}
or even:
while ( std::getline( read, line ) ) {
++ nLines;
for ( std::string::const_iterator current = line.begin();
current != line.end();
++ current ) {
// process character *current in line...
}
}
(The latter is ubiquitous, even if it is ugly.)
With char[], you have to replace this with:
while ( read.getline( buffer, sizeof(buffer) ) || read.gcount() != 0 ) {
int l = read.gcount();
if ( read ) {
++ nLines;
} else {
if ( read.eof() ) {
++ nLines; // Last line did not end with a '\n'
} else {
read.clear(); // Line longer than buffer...
}
for ( int i = 0; i != l; ++ i ) {
// process character buffer[i] in line...
}
}
One final question: what is (int)line[i] > 32 supposed to
mean? Did you want !isspace( line[i] ) &&
!iscntrl( line[i] )? (That's not at all what it does, of
course.)
So, I'm trying to get this code to parse each line inputted from the file into individual tokens, then add each one in turn to tklist array. Then the main just prints out each token. It's printing blanks though, and when I step into the code it looks like the strncpy isn't working. Any ideas what the issue is? I get no errors.
Here's the main function:
#include <iostream>
#include <fstream>
using namespace std;
#include "definitions.h"
#include "system_utilities.h"
int main()
{
ifstream inFile;
char line[MAX_CMD_LINE_LENGTH];
char* token[MAX_TOKENS_ON_A_LINE];
int numtokens;
system("pwd");
inFile.open("p4input.txt", ios::in);
if(inFile.fail()) {
cout << "Could not open input file. Program terminating.\n\n";
return 0;
}
while (!inFile.eof())
{
inFile.getline(line, 255);
line[strlen(line)+1] = '\0';
numtokens = parseCommandLine(line, token);
int t;
for (t=1; t <= numtokens; t++) {
cout << "Token "<< t << ": " << token[t-1] << "\n";
}
}
return 0;
}
And here's the parseCommandLine function:
int parseCommandLine(char cline[], char *tklist[]){
int i;
int length; //length of line
int count = 0; //counts number of tokens
int toklength = 0; //counts the length of each token
length = strlen(cline);
for (i=0; i < length; i++) { //go to first character of each token
if (((cline[i] != ' ' && cline[i-1]==' ') || i == 0)&& cline[i]!= '"') {
while ((cline[i]!=' ')&& (cline[i] != '\0') && (cline[i] != '\r')){
toklength++;
i++;
}
//---------------
tklist[count] = (char *) malloc( toklength +1);
strncpy(tklist[count], &cline[i-toklength], toklength);
//--------------
count ++;
toklength = 0;
}
if (cline[i] == '"') {
do {
toklength++;
i++;
if (cline[i] == ' ') {
toklength--;
}
} while (cline[i]!='"');
//--------------
tklist[count] = (char *) malloc( toklength +1);
strncpy(tklist[count], &cline[i-toklength], toklength);
//--------------
count ++;
toklength = 0;
}
}
int j;
for (j = 0; j < count; j++) {
free( (void *)tklist[j] );
}
return count;
}
Like I said, when I debug it looks like a problem with copying, but I'm a beginner so I suspect I'm doing something wrong.
Thanks for any help you can give!!
Try something like
tklist[count][toklength]='\0';
after
strncpy(tklist[count], &cline[i-toklength], toklength);
strncpy() does not necessarily add a null terminator for you. strncpy needs some care to use safely.
No null-character is implicitly appended at the end of destination if
source is longer than num..
Just for starters... there are other deeper issues as mentioned in comments.
To start with the generic equivalent of malloc/free is new/delete (heap memory allocation).
Second you seem to be confusing strings and c_strings (good old char*). getline uses strings, your parsing function uses c_strings they are not the same things and there is .c_str() a member function of string to do the conversion.
So, I'm trying to get this code to parse each line inputted from the
file into individual tokens, then add each one in turn to tklist
array.
For
each line inputted from the file
use
std::ifstream ifs;
std::string s;
/**/
std::getline(ifs, s);
adopted to your loop.
To
parse [..] into individual tokens
look how
std::string
can help you on that task (or use boost::tokenizer).
And this
then add each [token] in turn to tklist array.
almost cries for std::list or std::vector instead of a plain C array, the choice, which container to use depends e.g. on what you intend to do with the tokens found.
I want to read line by line from a file in C or C++, and I know how to do that when I assume some fixed size of a line, but is there a simple way to somehow calculate or get the exact size needed for a line or all lines in file? (Reading word by word until newline is also good for me if anyone can do it that way.)
If you use a streamed reader, all this will be hidden from you. See getline. The example below is based from the code here.
// getline with strings
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main () {
string str;
ifstream ifs("data.txt");
getline (ifs,str);
cout << "first line of the file is " << str << ".\n";
}
In C, if you have POSIX 2008 libraries (more recent versions of Linux, for example), you can use the POSIX getline() function. If you don't have the function in your libraries, you can implement it easily enough, which is probably better than inventing your own interface to do the job.
In C++, you can use std::getline().
Even though the two functions have the same basic name, the calling conventions and semantics are quite different (because the languages C and C++ are quite different) - except that they both read a line of data from a file stream, of course.
There isn't an easy way to tell how big the longest line in a file is - except by reading the whole file to find out, which is kind of wasteful.
I would use an IFStream and use getline to read from a file.
http://www.cplusplus.com/doc/tutorial/files/
int main () {
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
You can't get the length of line until after you read it in. You can, however, read into a buffer repeatedly until you reach the end of line.
For programming in c, try using fgets to read in a line of code. It will read n characters or stop if it encounters a newline. You can read in a small buffer of size n until the last character in the string is the newline.
See the link above for more information.
Here is an example on how to read an display a full line of file using a small buffer:
#include <stdio.h>
#include <string.h>
int main()
{
FILE * pFile;
const int n = 5;
char mystring [n];
int lineLength = 0;
pFile = fopen ("myfile.txt" , "r");
if (pFile == NULL)
{
perror ("Error opening file");
}
else
{
do
{
fgets (mystring , n , pFile);
puts (mystring);
lineLength += strlen(mystring);
} while(mystring[strlen ( mystring)-1] != '\n' && !feof(pFile));
fclose (pFile);
}
printf("Line Length: %d\n", lineLength);
return 0;
}
In C++ you can use the std::getline function, which takes a stream and reads up to the first '\n' character. In C, I would just use fgets and keep reallocating a buffer until the last character is the '\n', then we know we have read the entire line.
C++:
std::ifstream file("myfile.txt");
std::string line;
std::getline(file, line);
std::cout << line;
C:
// I didn't test this code I just made it off the top of my head.
FILE* file = fopen("myfile.txt", "r");
size_t cap = 256;
size_t len = 0;
char* line = malloc(cap);
for (;;) {
fgets(&line[len], cap - len, file);
len = strlen(line);
if (line[len-1] != '\n' && !feof(file)) {
cap <<= 1;
line = realloc(line, cap);
} else {
break;
}
}
printf("%s", line);
getline is only POSIX, here is an ANSI (NO max-line-size info needed!):
const char* getline(FILE *f,char **r)
{
char t[100];
if( feof(f) )
return 0;
**r=0;
while( fgets(t,100,f) )
{
char *p=strchr(t,'\n');
if( p )
{
*p=0;
if( (p=strchr(t,'\r')) ) *p=0;
*r=realloc(*r,strlen(*r)+1+strlen(t));
strcat(*r,t);
return *r;
}
else
{
if( (p=strchr(t,'\r')) ) *p=0;
*r=realloc(*r,strlen(*r)+1+strlen(t));
strcat(*r,t);
}
}
return feof(f)?(**r?*r:0):*r;
}
and now it's easy and short in your main:
char *line,*buffer = malloc(100);
FILE *f=fopen("yourfile.txt","rb");
if( !f ) return;
setvbuf(f,0,_IOLBF,4096);
while( (line=getline(f,&buffer)) )
puts(line);
fclose(f);
free(buffer);
it works on windows for Windows AND Unix-textfiles,
it works on Unix for Unix AND Windows-textfiles
Here is a C++ way of reading the lines, using std algorithms and iterators:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
struct getline :
public std::iterator<std::input_iterator_tag, std::string>
{
std::istream* in;
std::string line;
getline(std::istream& in) : in(&in) {
++*this;
}
getline() : in(0) {
}
getline& operator++() {
if(in && !std::getline(*in, line)) in = 0;
}
std::string operator*() const {
return line;
}
bool operator!=(const getline& rhs) const {
return !in != !rhs.in;
}
};
int main() {
std::vector<std::string> v;
std::copy(getline(std::cin), getline(), std::back_inserter(v));
}
I want to get the last but not empty line in a txt file.
This is my code:
string line1, line2;
ifstream myfile(argv[1]);
if(myfile.is_open())
{
while( !myfile.eof() )
{
getline(myfile, line1);
if( line1 != "" || line1 != "\t" || line1 != "\n" || !line1.empty() )
line2 = line1;
}
myfile.close();
}
else
cout << "Unable to open file";
The problem is I cannot check the empty line.
Okay, let's start with the obvious part. This: while( !myfile.eof() ) is essentially always wrong, so you're not going to detect the end of the file correctly. Since you're using getline to read the data, you want to check its return value:
while (getline(myfile, line1)) // ...
Likewise, the logic here:
if( line1 != "" || line1 != "\t" || line1 != "\n" || !line1.empty() )
line2 = line1;
...is clearly wrong. I'm guessing you really want && instead of || for this. As it stands, the result is always true, because no matter what value line1 contains, it must be unequal to at least one of those values (i.e., it can't simultaneously contain only a tab and contain only a new-line and contain nothing at all -- but that would be necessary for the result to be false). Testing for both !line1.empty() and line1 != "" appears redundant as well.
Why not read the file backwards? That way you don't have to scan the entire file to accomplish this. Seems like it ought to be possible.
int main(int argc, char **argv)
{
std::cout<<"Opening "<<fn<<std::endl;
std::fstream fin(fn.c_str(), std::ios_base::in);
//go to end
fin.seekg(0, std::ios_base::end);
int currpos = fin.tellg();
//go to 1 before end of file
if(currpos > 0)
{
//collect the chars here...
std::vector<char> chars;
fin.seekg(currpos - 1);
currpos = fin.tellg();
while(currpos > 0)
{
char c = fin.get();
if(!fin.good())
{
break;
}
chars.push_back(c);
currpos -= 1;
fin.seekg(currpos);
}
//do whatever u want with chars...
//this is the reversed order
for(std::vector<char>::size_type i = 0; i < chars.size(); ++i)
{
std::cout<<chars[i];
}
//this is the forward order...
for(std::vector<char>::size_type i = chars.size(); i != 0; --i)
{
std::cout<<chars[i-1];
}
}
return 0;
}
It wouldn't be enough to change your ||'s to &&'s to check if the line is empty. What if there are seven spaces, a tab character, another 3 spaces and finally a newline? You can't list all the ways of getting only whitespace in a line. Instead, check every character in the line to see if it is whitespace.
In this code, is_empty will be false if any non-space character is found in the line.
bool is_empty = true;
for (int i = 0; i < line.size(); i++) {
char ch = line[i];
is_empty = is_empty && isspace(ch);
}
Full solution:
#include <iostream>
#include <fstream>
#include <cctype>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
string line;
string last_line;
ifstream myfile(argv[1]);
if(myfile.is_open())
{
while( getline(myfile, line) ) {
bool is_empty = true;
for (int i = 0; i < line.size(); i++) {
char ch = line[i];
is_empty = is_empty && isspace(ch);
}
if (!is_empty) {
last_line = line;
}
}
myfile.close();
cout << "Last line: " << last_line << endl;
}
else {
cout << "Unable to open file";
}
return 0;
}
Additional to what the others said:
You can avoid reading whitespace by doing myfile >> std::ws before you call std::getline(). This will consume all leading whitespaces.
Then your condition reduces to !line1.empty(). This would also work when the line contains nothing but several whitespaces, for which your version fails.
I wasn't able to google an appropriate get_last_line function for my needs and here's what i came up with. You can even read multiple non-empty last lines by recalling the instream get_last_line func without resetting the seeker. It supports a 1 char only file. I added the reset parameter, which can be set to ios_base::end to allow output operations after reading the last line(s)
std::string& get_last_line(
std::istream& in_stream,
std::string& output = std::string(),
std::ios_base::seekdir reset = std::ios_base::cur)
{
output.clear();
std::streambuf& buf = *in_stream.rdbuf();
bool text_found = false;
while(buf.pubseekoff(-1, std::ios_base::cur) >= 0)
{
char c = buf.sgetc();
if(!isspace(c))
text_found = true;
if(text_found)
{
if(c == '\n' || c == -1)
break;
output.insert(0, sizeof c, c);
}
}
buf.pubseekoff(0, reset);
return output;
}
std::string& get_last_line(
const std::string& file_name,
std::string& output = std::string())
{
std::ifstream file_in(
file_name.c_str(),
std::ios_base::in | std::ios_base::ate);
if(!file_in.is_open())
{
output.clear();
return output;
}
get_last_line(file_in, output);
file_in.close();
return output;
}