Video Coming Soon...
33: tail
The tail command is the inverse of the head command. It prints the last -n number of lines. It's also more difficult to implement efficiently. You'll make a version here that will be "good enough."
The Challenge
The challenge for this exercise is to once again use what you know from implementing head to create a totally new project from scratch that does tail. Read the documentation for tail to make sure you understand how it works, and at a minimum implement the -n option.
The Code
Here's my simple implementation of tail:
See my first version of tail
View Source file tail.cpp Only#include <fmt/core.h>
#include <iostream>
#include <unistd.h>
#include <fstream>
#include <vector>
#include <algorithm>
void collect_tail(std::istream& in, int count) {
std::string line;
std::vector<std::string> lines;
while(in) {
getline(in, line);
lines.emplace_back(line);
}
int start = std::max(0, int(lines.size()) - count);
for(size_t i = start; i < lines.size(); i++) {
fmt::println("{}", lines[i]);
}
}
int main(int argc, char* argv[]) {
int opt = 0;
int number = 10;
while((opt = getopt(argc, argv, "n:")) != -1) {
switch(opt) {
case 'n':
number = std::stoi(optarg);
break;
default:
fmt::println("USAGE: tail -n <NUM> [file...]");
return 1;
}
}
if(optind < argc) {
for(int i = optind; i < argc; i++) {
std::ifstream in_file{argv[i]};
collect_tail(in_file, number);
}
} else {
collect_tail(std::cin, number);
}
}
The Discussion
This version of tail is not efficient. It'll work fine for small files, but once the files become large you'll run into two problems:
- It'll take forever to get to the end of the file since this
tailneeds to read the whole file to find the end. - It has to store the whole file into memory just to find the end.
For a simple beginner implementation this is fine, but it can be better.
Further Study
How can you make this more efficient? If you read the documentation for ifstream you'll see a function named tellg and another one names seekg. These functions allow you to skip ahead to different sections of a file.
Using this you could skip to the end of the file, and roll back a little to count the lines. Keep skipping back a bit and counting lines until you have enough, then print that.
This wouldn't work on std::cin but would work on files. Give it a try.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.