tags:

views:

579

answers:

4

I have a C program that outputs two columns, utterly misaligned. The reason for the misalignment is lengths of words in the first column are very different.

I have an output file opened in vi. How do I quickly align these two columns? I am fine with using awk, perl, sed, and not just vi (7.2) toolset. Also, can we have a generic solution for files with more than two columns?

Here is sample file

column1               column2
-------               -------
sdfsdfsddfsdfsdfsdfsd         343r5
dfgdfgdf             234
gdfgdfgdfgdfgf            645
+6  A: 

Presumably you are using printf to output the columns in the first place. You can use extra modifiers in your format string to make sure things get aligned.

  • To print a column of a specific width (right-justified), add the width before the formatting flag, e.g., "%10s" will print a column of width 10. If your string is longer than 10 characters, the column will be longer than you want, so choose a maximum value. If the string is shorter, it will be padded with spaces.
  • To left-justify a column, put a - sign in front, e.g., "%-10s". I like to left-justify strings and right-justify numbers, personally.
  • If you are printing addresses, you can change the fill characters from spaces to zeroes with a leading zero: "%010x".

To give a more in depth example:

printf("%-30s %8s %8s\n", "Name", "Address", "Size");
for (i = 0; i < length; ++i) {
    printf("%-30s %08x %8d\n", names[i], addresses[i], sizes[i]);

This would print three columns like this:

Name                            Address     Size
foo                            01234567      346
bar                            9abcdef0     1024
something-with-a-longer-name   0000abcd     2048
Jay Conrod
+2  A: 

Here's a awk solution : c_prog | awk '{ printf("%- 40s %- 40s\n", $1, $2); }'

RC
+2  A: 

I wrote a small program that solves this problem using Perl. It also works for multiple columns.

#!/usr/bin/perl
use strict;
use warnings;
my $sep = 2;

sub max {
    my ($a,$b) = @_;
    return $a > $b ? $a : $b;
}

my @rows;
my $cols;
my $max = 0;

while (<>) {
    next if m/^\s*$/;
    my (@cols) = split m'\s+';

    for (@cols) {
        $max = max($max, length);
    }

    $cols = @cols;
    push @rows, \@cols;
}

for (@rows) {
    my $str = join '', (('%-' . ($max+$sep) . 's') x $cols);
    $str .= "\n";
    printf $str, @$_;
}
Peter Stuifzand
+1  A: 

If you want to do the processing in Vim (as opposed to fixing the generator), install the superb align plugin and run the following:

ggVG
\tsp

The first command breaks down to gg (go to the start of the file), V (enter visual line mode), G (go to the end of the file). As a combination, it visually-selects the entire file. \tsp is an Align map that aligns on white-space.

If you prefer to do things at the : command line, you can use an alternative separator (e.g. ###) and use the command-line Align:

:%s/\s\+/###/g
:%Align ###
:%s/### //g

It's longer, but you may find it more logical/memorable.

Al