Created
September 1, 2012 21:08
-
-
Save klmr/3587388 to your computer and use it in GitHub Desktop.
Display tabular data vertically
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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