Skip to content

Instantly share code, notes, and snippets.

@klmr
Created September 1, 2012 21:08
Show Gist options
  • Save klmr/3587388 to your computer and use it in GitHub Desktop.
Save klmr/3587388 to your computer and use it in GitHub Desktop.
Display tabular data vertically
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <boost/program_options.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
namespace po = boost::program_options;
namespace ad = boost::adaptors;
int usage(char const* name, po::options_description const& opts) {
std::cout << name << " [options] filename\n";
std::cout << name << " [options] < filename\n\n";
std::cout << "Display tabular data vertically "
"(after an idea by Pierre Lindenbaum).\n\n";
std::cout << opts;
return EXIT_SUCCESS;
}
std::string::size_type length(std::string const& s) {
return s.length();
}
std::vector<std::string> tokenize(std::string const& line, char delimiter) {
std::vector<std::string> tokens;
std::istringstream istr(line);
std::string token;
while (getline(istr, token, delimiter))
tokens.emplace_back(token);
return tokens;
}
std::vector<std::string> tokenize_line(std::istream& in, char delimiter) {
std::string line;
if (not getline(in, line))
return { };
return tokenize(line, delimiter);
}
int main(int argc, char** argv) try {
std::ios_base::sync_with_stdio(false);
po::options_description args("Options");
char delimiter;
args.add_options()
("help,h", "Show this help message")
("delim,d",
po::value<char>(&delimiter)->default_value('\t', "TAB"),
"Delimiter separating the columns")
("no-header,n",
po::bool_switch(),
"Don't interpret first line as header");
po::options_description hidden_args("");
hidden_args.add_options()
("file", po::value<std::string>(), "Input file");
po::positional_options_description positional_args;
positional_args.add("file", -1);
po::options_description all_options("");
all_options.add(args).add(hidden_args);
po::variables_map parsed_args;
store(
po::command_line_parser(argc, argv)
.options(all_options)
.positional(positional_args)
.run(),
parsed_args);
notify(parsed_args);
if (parsed_args.count("help"))
return usage(argv[0], args);
bool const use_header = not parsed_args["no-header"].as<bool>();
bool const has_filename = parsed_args.count("file");
std::ifstream file_in;
if (has_filename)
file_in.open(parsed_args["file"].as<std::string>().c_str());
auto& in = has_filename ? file_in : std::cin;
auto const header =
use_header ? tokenize_line(in, delimiter)
: std::vector<std::string> { };
if (use_header and header.empty())
throw std::runtime_error("Unable to read header.");
// Assume < 100 columns which seems reasonable.
std::size_t const index_width =
header.empty() ? 0 :
header.size() < 10 ? 2 : 3;
std::size_t header_width = use_header
? 1 + *boost::max_element(header | ad::transformed(length))
: 0;
std::size_t lineno = use_header ? 2 : 1;
std::string line;
while (getline(in, line)) {
std::cout << ">>> " << lineno << "\n";
auto const cols = tokenize(line, delimiter);
auto const col_count = use_header ? header.size() : cols.size();
for (std::size_t i = 0; i < col_count; ++i) {
if (use_header)
std::cout << "$"
<< std::left << std::setw(index_width) << (i + 1)
<< std::left << std::setw(header_width) << header[i];
if (i < cols.size())
std::cout << cols[i] << "\n";
else
std::cout << "\n";
}
std::cout << "<<< " << lineno << "\n\n";
++lineno;
}
}
catch (po::error const& e) {
std::cerr << e.what() << "\n";
std::cerr << "Use --help to get help.\n";
return EXIT_FAILURE;
}
catch (std::exception const& ex) {
std::cerr << ex.what() << "\n";
return EXIT_FAILURE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment