Python parsing stdin much faster than C++ - c++

I have a python function which I was hoping to translate into C++ to try and gain some extra speed (as it will be used to parse >100GB files). I am very inexperienced with C++ and was horrified to find my C++ function running much slower after my basic translation. Any pointers as to why this is, or what I can do to improve my C++ code would be much appreciated.
Script overview: the function reads stdin from another program, checks each line for any substring matches, and prints each line to stdout
Python function:
def find_tagPy(conditions):
# conditions e.g. ['TTAT', 'TAT'] etc
for line in stdin:
# Check conditionss against this line
l = line.split("\t")
if l[0][0] == "#":
stdout.write(line)
continue
FLAG = int(l[1])
if 1 & FLAG: # Read has a pair
for bases in conditions:
if bases in l[9]:
ADD_MATE = 1
stdout.write(line)
break # stop looking
C++ function:
void find_tagCpp (vector<string> conditions) {
cin.sync_with_stdio(false);
cin.tie(NULL);
string line;
while (getline(cin, line)) {
vector<string> l;
boost::split(l, line, boost::is_any_of("\t"), boost::token_compress_on);
if (l[0][0] == '#') {
cout << line << "\n";
continue;
}
int FLAG = stoi(l[1]);
int pair_FLAG = 1;
if (pair_FLAG & FLAG) { // Read has a pair
for (int i=0; i < conditions.size(); i++) { // If bases in SEQ
if (l[9].find(conditions[i]) != string::npos) {
cout << line << "\n";
break; // Stop looking
}
}
}
}
}
An example of a stdin line is:
FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31
On my machine the python function takes 1.97 s and the C++ function takes 11.05 s (file size around 25 mb, but this includes processing with upstream and downstream tools)
EDIT:
I've found a bottleneck in boost::split which is a bit suprising:
Python:
for i in range(100000):
l = line.split("\t")
C++:
for (int i=0; i < 100000; i++) {
vector<string> l;
boost::split(l, line, boost::is_any_of("\t"), boost::token_compress_on);
}
Python = 0.0325 s
C++ = 1.245 s
However my file is only 156,980 lines, so this cant be the whole problem.

The split copies the pieces into new strings. This is slow and you do not need them. Instead search the line for the start of the piece you want ( 10th ) and then call find starting from there.

I realised my original code is not amenable to testing, so I thought I would refactor it here, and discuss what I have found.
I turned on compiler optimisations using the -Ofast (fastest, aggressive optimisations, Apple LLVM 6.1) as suggested, and for comparison Python is 2.7.10.
Python function
import time
def fun(line):
l = line.split(" ", 10)
if 'TTAGGG' in l[9]:
pass
line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31"
time0 = time.time()
for i in range(100000):
fun(line)
print time.time() - time0
C++ function
void fun(string* line, string* substring) {
vector<string> l;
boost::split(l, *line, boost::is_any_of(" "));
if (l[9].find(*substring) != string::npos) {
// Do nothing
}
}
int main(int argc, const char * argv[]) {
string line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31";
string substring = "TTAGGG";
boost::timer t;
for (int i=0; i<100000; i++) {
fun(&line, &substring);
}
cout << t.elapsed() << endl;
return 0;
}
On my machine I now time the c++ function at 205 ms and the python function at 66 ms. Interestingly, now almost the entire runtime is taken up by the boost::split function.
If I get rid of this function and use string.find to search the whole line (not quite what I wanted though):
if ((*line).find(*substring) != string::npos) {
// Do nothing
}
The c++ runtime is reduced to about <1 ms! So it appears boost::split was the problem all alone. Thanks for the suggestions.

Try this code with some optimization
C++ function:
void find_tagCpp (vector<string> conditions) {
cin.sync_with_stdio(false);
cin.tie(NULL);
string line;
vector<string> l;
while (getline(cin, line)) {
l.clear();
boost::split(l, line, boost::is_any_of("\t"), boost::token_compress_on);
if (l[0][0] == '#') {
cout << line << "\n";
continue;
}
int FLAG = stoi(l[1]);
int pair_FLAG = 1;
if (pair_FLAG & FLAG) { // Read has a pair
for (int i=0; i < conditions.size(); i++) { // If bases in SEQ
if (l[9].find(conditions[i]) != string::npos) {
printf("%s\n", line.c_str());
break; // Stop looking
}
}
}
}
}

Related

Output issue with .CSV file

Whenever I attempt to output a line, it outputs the data from the file vertically instead of outputting the full line horizontally. My main goal is to output each line individually and remove commas and repeat till no more lines are in the CSV file.
An example when I run the code:
cout << data[1] << "\t";
Output:
Huggenkizz Pinzz White Dwarf Dildock Operknockity DeVille
What I'm trying to get is:
Huggenkizz Amanda 3/18/1997 Sales Associate 2 A A F
My CSV File:
ID,Last Name,First Name,DOB,DtHire,Title,Level,Region,Status,Gender
1,Huggenkizz,Amanda,3/18/1997,,Sales Associate,2,A,A,F
2,Pinzz,Bobby,5/12/1986,,Sales Associate,3,B,A,F
3,White,Snow,12/23/1995,,Sales Associate,2,C,A,F
4,Dwarf,Grumpy,9/8/1977,,Sales Associate,2,C,A,M
5,Dildock,Dopey,4/1/1992,,Sales Associate,1,B,A,M
6,Operknockity,Michael,10/2/1989,,Sales Associate,1,A,S,M
9,DeVille,Cruella,8/23/1960,,Sales Manager,,,A,F
My Code:
vector<string> SplitString(string s, string delimiter)
{
string section;
size_t pos = 0;
vector<string> annualSalesReport;
while ((pos = s.find(delimiter)) != string::npos) //finds string till, if not returns String::npos
{
section = (s.substr(0, pos)); // returns the substring section
annualSalesReport.push_back(section); // places comma split section into the next array
s.erase(0, pos + delimiter.length()); // removes the previous string up to the current pos
}
annualSalesReport.push_back((s));
return annualSalesReport;
}
int main()
{
vector<string> data;
string readLine;
ifstream myIFS;
myIFS.open("SalesAssociateAnnualReport.csv");
int lineCounter = 0;
while (getline(myIFS, readLine))
{
lineCounter++;
if (lineCounter > 1)
{
data = SplitString(readLine, ",");
if (data.size() > 1) //removes top line
{
cout << data[1]<< "\t";
}
}
}
myIFS.close();
return 0;
}
Please change your main function as follows
int main()
{
vector<vector<string>> data;
string readLine;
ifstream myIFS;
myIFS.open("SalesAssociateAnnualReport.csv");
int lineCounter = 0;
while (getline(myIFS, readLine))
{
lineCounter++;
if (lineCounter > 1)
{
vector<string> dataLine = SplitString(readLine, ",");
data.push_back(dataLine);
}
}
myIFS.close();
// output the first data line of csv file without delimiter and without first column
for (size_t i = 1; i < data[0].size(); i++)
{
cout << data[0][i] << '\t';
}
return 0;
}
to get your desired output of
Huggenkizz Amanda 3/18/1997 Sales Associate 2 A AF
without having to change your SplitString function.
Please be aware that C++ first array index is always 0 instead of 1.
I've separated the CSV file input processing and the output generation, just to follow the simple programming model IPO:
Input -> Process -> Output
Therefore I've introduced the matrix of strings vector<vector<string>> to store the whole desired CSV file data.
As mentioned in the comments, the SplitString function may be refactored and it should also be fixed to split the last two columns properly.
Hope it helps?

Checking stringstream line char by char C++

I will keep it short and simple. After making sure that user is able to open a file succesfully, I have written the following piece of code to take a line from the inputFile.
string line;
int counter = 0;
DynIntStack stack;
while (!inputFile.eof())
{
getline(inputFile, line);
stringstream inputLine(line);
counter++;
//I NEED TO DO IT HERE
}
This will be used to write program to check balanced paranthesis in an input cpp file and I have to use stacks. Classic CS homework as I understand from the topics I have checked :)
counter is updated after every line and the line number(counter) is to be pushed to the stack if it has a opening bracket and it must be popped from the stack if it is a closing bracket. after these, the output should look something like this:
block: 3 - 3
block: 12 - 14
block: 10 - 14
block: 5 - 16
Syntax error in line 21.
But I do not know how to check the line I got char by char. I need a loop to check the chars and apply the previously mentioned things if an opening or closing bracket is found. How can I check the line char by char.
using any data container other than stacks is forbidden.
thank you very much :)
But I do not know how to check the line I got char by char
Is this what you want?
string line;
int counter = 0;
DynIntStack stack;
while (getline(inputFile, line))
{
counter++;
for(size_t i = 0; i < line.length(); i++) {
// line[i] is i'th character
if(line[i] == '(') {
// do stuff
}
else if(line[i] == ')') {
// do stuff
}
}
}
In addition to the correct answer by Kaidul Islam, a std::string support range based for loops.
string line;
int counter = 0;
DynIntStack stack;
while (getline(inputFile, line))
{
++counter;
for (char const c : line)
{
if (c == '(')
{
// do stuff
}
else if (c == ')')
{
// do stuff
}
}
}

Txt to 2 different arrays c++

I have a txt file with a lot of things in it.
The lines have this pattern: 6 spaces then 1 int, 1 space, then a string.
Also, the 1st line has the amount of lines that the txt has.
I want to put the integers in an array of ints and the string on an array of strings.
I can read it and put it into an array , but only if I'm considering the ints as chars and putting into one array of strings.When I try to separate things I have no idea on how I'd do it. Any ideas?
The code I used for putting everything in an array was this:
int size()
{
ifstream sizeX;
int x;
sizeX.open("cities.txt");
sizeX>>x;
return x;
};
int main(void)
{
int size = size();
string words[size];
ifstream file("cities.txt");
file.ignore(100000,'\n');
if(file.is_open())
{
for(int i=0; i<size; i++)
{
getline(file,words[i]);
}
}
}
Just to start I'm going to provide some tips about your code:
int size = size();
Why do you need to open the file, read the first line and then close it? That process can be done opening the file just once.
The code string words[size]; is absolutely not legal C++. You cannot instantiate a variable-length-array in C++. That C feature has been not included in C++ standard (some ref). I suggest you to replace with std::vector, which is more C++ code.
Here I write a snippet of function which perform what you need.
int parse_file(const std::string& filename,
std::vector<std::string>* out_strings,
std::vector<int>* out_integers) {
assert(out_strings != nullptr);
assert(out_integers != nullptr);
std::ifstream file;
file.open(filename, std::ios_base::in);
if (file.fail()) {
// handle the error
return -1;
}
// Local variables
int num_rows;
std::string line;
// parse the first line
std::getline(file, line);
if (line.size() == 0) {
// file empty, handle the error
return -1;
}
num_rows = std::stoi(line);
// reserve memory
out_strings->clear();
out_strings->reserve(num_rows);
out_integers->clear();
out_integers->reserve(num_rows);
for (int row = 0; row < num_rows; ++row) {
// read the line
std::getline(file, line);
if (line.size() == 0) {
// unexpected end of line, handle it
return -1;
}
// get the integer
out_integers->push_back(
std::stoi(line.substr(6, line.find(' ', 6) - 6)));
// get the string
out_strings->push_back(
line.substr(line.find(' ', 6) + 1, std::string::npos));
}
file.close();
return 0;
}
You can definitely improved it, but I think it's a good point where to start.
The last suggest I can give you, in order to improve the robustness of your code, you can match each line with a regular expression. In this way you can be sure your line is formatted exactly how you need.
For example:
std::regex line_pattern("\\s{6}[0-9]+\\s[^\\n]+");
if (std::regex_match(line, line_pattern) == false) {
// ups... the line is not formatted how you need
// this is an error
}

How to delete characters in vector of strings?

I am writing a code where I read a subtitle file and remove the text in () including the brackets themselves, that is subtitles for hearing impaired which have background noise in ().
The example:
13
00:01:08,535 --> 00:01:10,127 // remove this
(PIANO PLAYING) // remove this
125
00:07:09,162 --> 00:07:12,393
BOTH: (SINGING WITH RADIO) Teach // remove only the text in parenthesis, including ()
them well and let them lead the way
The code is here:
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
using namespace std;
void subRem();
int main() {
subRem();
system("PAUSE");
}
void subRem() {
ofstream out;
ifstream in;
out.open("whip it2.srt");
if (out.fail()) {
perror("whip it2.srt");
}
in.open("whip it.srt");
if (out.fail()) {
perror("whip it.srt");
}
vector<string> input;
string inc;
while (getline(in, inc)) {
input.push_back(inc);
}
vector<int> len;
for (int i = 0; i < input.size(); i++) {
len.push_back(input[i].size());
}
for (int i = 0; i < input.size(); i++) {
for (int j = 0; j < len[i]; j++) {
if (input[i][j] == '(') {
for (int k = j; k < len[i]; k++) {
j = k;
if (input[i][k] == ')') {
if (k == (len[i] - 1)) {
input[i - 1] = "";
}
input[i][k] = '\0';
break;
}
input[i][k] = '\0';
}
}
}
}
for (int k = 0; k < input.size(); k++) {
out << input[k] << endl;
}
}
I want to delete the characters in parenthesis, so I am using:
input[i][k] = '\0';
The problem is the characters are removed but they are replaced by whitespace, for example:
(SHOUTING) with her?
I get:
___________with her?
(____ are whitespaces because I couldn't make them appear)
There is the white space. If it was string, I could do:
input[i][k] = "";
but with characters I get the error when I do:
input[i][k] = '';
quoted string should contain at least one character
I plan to improve the code further by renaming the line numbers and deleting extra newlines, but I want to create like an app where I can drag and drop the subtitle file and click run, to get the modified subtitle file. What do I need to know to create the GUI? Do I need to learn Qt or some other libraries?
std:;string can contain \0 without problems, it's not the end-of-string character inside a std::string. MikeCAT's suggestion is the correct answer: use std::string::erase.
(Please don't ask multiple questions at once, but yes Qt is a reasonable way to create GUI's)
Try using substr. This method gives you a substring between two given positions. Although this solves the problem for your second problem, it leaves empty subtitles for strings on the first case. I would recommend checking for an empty result and removing the string at all.
Since you're basically copying characters from one file to another, I'd just keep track of whether you're in a subtitle as you copy, and if so, don't copy characters until you encounter a close parenthesis again.
#include <string>
#include <iostream>
#include <sstream>
int main() {
std::istringstream in{
R"(13
00:01:08,535 --> 00:01:10,127
(PIANO PLAYING)
125
00:07:09,162 --> 00:07:12,393
BOTH: (SINGING WITH RADIO) Teach
them well and let them lead the way)"
};
bool in_subtitle = false;
std::string temp;
while (std::getline(in, temp)) {
unsigned line_len = 0;
for (char ch : temp) {
switch (ch) {
case '(': in_subtitle = true; break;
case ')': in_subtitle = false; break;
default:
if (!in_subtitle) {
std::cout << ch;
++line_len;
}
break;
}
}
if (line_len != 0) std::cout << "\n";
}
}
#include <iostream>
#include <regex>
int main() {
std::string text("this text (remove this) and (remove this) end.");
// First Method: with regular expression
std::regex expr("\\(.*?\\)");
std::cout << std::regex_replace (text, expr, "");
// Second Method: with stl
auto begin = text.find_first_of("(");
auto end = text.find_last_of(")") + 1;
if (std::string::npos != begin && std::string::npos != end && begin <= end)
text.erase(begin, end-begin);
// Optional
std::cout << text << std::endl;
}

reading last n lines from file in c/c++

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>