Align columns: Difference between revisions
→{{header|D}}: add implementation |
→{{header|D}}: Actually, it seems to be incomplete |
||
Line 552: | Line 552: | ||
=={{header|D}}== |
=={{header|D}}== |
||
{{incorrect|D|(Actually, it is '''incomplete'''). This seems as if it might generate a correct left justified output , but right and center justification, (and maybe sample output), are missing.}} |
|||
<lang d> |
<lang d> |
||
import std.stdio; |
import std.stdio; |
Revision as of 05:47, 20 August 2009
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
Use the following text to test your programs:
Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.
Note that:
- The example input texts lines may, or may not, have trailing dollar characters.
- All columns should share the same alignment.
- Consecutive space characters produced adjacent to the end of lines are insignificant for the purposes of the task.
- Output text will be viewed in a mono-spaced font.
Ada
<lang ada> with Ada.Characters.Latin_1; use Ada.Characters.Latin_1; with Ada.Text_IO; use Ada.Text_IO; with Strings_Edit; use Strings_Edit;
procedure Column_Aligner is
Text : constant String := "Given$a$text$file$of$many$lines,$where$fields$within$a$line$" & NUL & "are$delineated$by$a$single$'dollar'$character,$write$a$program" & NUL & "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" & NUL & "column$are$separated$by$at$least$one$space." & NUL & "Further,$allow$for$each$word$in$a$column$to$be$either$left$" & NUL & "justified,$right$justified,$or$center$justified$within$its$column." & NUL; File : File_Type; Width : array (1..1_000) of Natural := (others => 0); Line : String (1..200); Column : Positive := 1; Start : Positive := 1; Pointer : Positive;
begin
Create (File, Out_File, "columned.txt"); -- Determining the widths of columns for I in Text'Range loop case Text (I) is when '$' | NUL => Width (Column) := Natural'Max (Width (Column), I - Start + 1); Start := I + 1; if Text (I) = NUL then Column := 1; else Column := Column + 1; end if; when others => null; end case; end loop; -- Formatting for Align in Alignment loop Column := 1; Start := 1; Pointer := 1; for I in Text'Range loop case Text (I) is when '$' | NUL => Put -- Formatted output of a word ( Destination => Line, Pointer => Pointer, Value => Text (Start..I - 1), Field => Width (Column), Justify => Align ); Start := I + 1; if Text (I) = NUL then Put_Line (File, Line (1..Pointer - 1)); Pointer := 1; Column := 1; else Column := Column + 1; end if; when others => null; end case; end loop; end loop; Close (File);
end Column_Aligner; </lang> Formatted file sample:
Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
ALGOL 68
<lang algol> STRING nl = REPR 10; STRING text in list := "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"+nl+
"are$delineated$by$a$single$'dollar'$character,$write$a$program"+nl+ "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"+nl+ "column$are$separated$by$at$least$one$space."+nl+ "Further,$allow$for$each$word$in$a$column$to$be$either$left$"+nl+ "justified,$right$justified,$or$center$justified$within$its$column.";
MODE PAGE = FLEX[0,0]STRING; PAGE page;
PROC flex page = (PAGE in page, INT row, col)PAGE:(
HEAP FLEX[row, col]STRING out page; out page[:1 UPB in page, :2 UPB in page] := in page; FOR r TO row DO FOR c FROM 2 UPB in page + 1 TO col DO out page[r,c]:="" OD OD; FOR r FROM 1 UPB in page + 1 TO row DO FOR c FROM 1 TO col DO out page[r,c]:="" OD OD; out page
);
FILE text in file; associate(text in file, text in list); make term(text in file, "$");
on physical file end(text in file, (REF FILE skip)BOOL: stop iteration); on logical file end(text in file, (REF FILE skip)BOOL: stop iteration); FOR row DO
on line end(text in file, (REF FILE skip)BOOL: stop iteration); FOR col DO STRING tok; getf(text in file, ($gx$,tok)); IF row > 1 UPB page THEN page := flex page(page, row, 2 UPB page) FI; IF col > 2 UPB page THEN page := flex page(page, 1 UPB page, col) FI; page[row,col]:=tok OD; stop iteration: SKIP
OD; stop iteration:
SKIP;
BEGIN
PROC aligner = (PAGE in page, PROC (STRING,INT)STRING aligner)VOID:( PAGE page := in page; [2 UPB page]INT max width; FOR col TO 2 UPB page DO INT max len:=0; FOR row TO UPB page DO IF UPB page[row,col]>max len THEN max len:=UPB page[row,col] FI OD; FOR row TO UPB page DO page[row,col] := aligner(page[row,col], maxlen) OD OD; printf(($n(UPB page)(n(2 UPB page -1)(gx)gl)$,page)) );
PROC left = (STRING in, INT len)STRING: in + " "*(len - UPB in), right = (STRING in, INT len)STRING: " "*(len - UPB in) + in, centre = (STRING in, INT len)STRING: ( INT pad=len-UPB in; pad%2*" "+ in + (pad-pad%2)*" " ); []STRUCT(STRING name, PROC(STRING,INT)STRING align) aligners = (("Left",left), ("Left",right), ("Centre",centre)); FOR index TO UPB aligners DO print((new line, "# ",name OF aligners[index]," Column-aligned output:",new line)); aligner(page, align OF aligners[index]) OD
END</lang>
AWK
<lang awk>BEGIN {
FS="$" lcounter = 1 maxfield = 0 # justistification; pick up one #justify = "left" justify = "center" #justify = "right"
} {
if ( NF > maxfield ) maxfield = NF; for(i=1; i <= NF; i++) { line[lcounter,i] = $i if ( longest[i] == "" ) longest[i] = 0; if ( length($i) > longest[i] ) longest[i] = length($i); } lcounter++
} END {
just = (justify == "left") ? "-" : "" for(i=1; i <= NR; i++) { for(j=1; j <= maxfield; j++) { if ( justify != "center" ) {
template = "%" just longest[j] "s "
} else {
v = int((longest[j] - length(line[i,j]))/2) rt = "%" v+1 "s%%-%ds" template = sprintf(rt, "", longest[j] - v)
} printf(template, line[i,j]) } print "" }
}</lang>
C
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <string.h>
struct linelement {
char **wordlist; int longest; struct linelement *next;
};
typedef struct linelement line_t;
char *trim(char *s, int *len) {
char *b; int l;
while( (*s == ' ') || (*s == '\t') ) s++; b = s; l = strlen(b); s += l; s--; while( (*s == ' ') || (*s == '\t') ) s--; s++; *s = 0; if ( l > *len ) *len = l; return b;
}
char **split(char *l, int c, int *longest) {
int howmany, i; char **arr; char *n;
if ( (n = strchr(l, 10)) != NULL ) *n = 0; if ( (n = strchr(l, 13)) != NULL ) *n = 0;
for(howmany=1, i=0; l[i] != 0 ; i++) if ( (l[i]==c) && ((i+1) < strlen(l))) howmany++; arr = malloc(sizeof(char *) * (howmany+1)); arr[0] = NULL; if ( arr != NULL ) { n = l; for(i=0; i < howmany; i++) { arr[i] = n; n = strchr(n, c); if ( n == NULL ) { break; } *n = 0; n++; arr[i] = trim(arr[i], longest); } } arr[howmany] = NULL; return arr;
}
- define MAXLINELEN 1024
- define FILLCHAR ' '
/* decide the alignment */ enum textalign {
LEFT_ALIGNED, RIGHT_ALIGNED, CENTER_ALIGNED
}; const int alignment = CENTER_ALIGNED;
int main()
{
char buf[MAXLINELEN]; line_t *head, *cur; char *lb; int reallongest, i, len, ipos;
head = malloc(sizeof(line_t)); memset(head, 0, sizeof(line_t));
/* for each line, split it ($-separated words) */ cur = head; while ( fgets(buf, MAXLINELEN, stdin) != NULL ) { lb = malloc(strlen(buf)); strncpy(lb, buf, strlen(buf)); cur->wordlist = split(lb, '$', &(cur->longest)); cur->next = malloc(sizeof(line_t)); memset(cur->next, 0, sizeof(line_t)); cur = cur->next; } cur->next = NULL; /* last node is a end-marker */
/* each line holds the longest word length; find the longest among all lines; this determines the width of all columns */ reallongest = head->longest; cur = head; while( cur->next != NULL ) { if ( cur->longest > reallongest ) reallongest = cur->longest; cur = cur->next; }
reallongest++; buf[reallongest] = 0; /* no bounds check... */
/* print the columns */ cur = head; while( cur->next != NULL ) { for(i=0; cur->wordlist[i] != NULL; i++) { len = strlen(cur->wordlist[i]); switch(alignment) { case LEFT_ALIGNED:
ipos = 0; break;
case RIGHT_ALIGNED:
ipos = reallongest - len; break;
case CENTER_ALIGNED:
ipos = (reallongest - len)/2; break;
} memset(buf, FILLCHAR, reallongest); memcpy(buf+ipos, cur->wordlist[i], len); printf("%s ", buf); } printf("\n"); cur = cur->next; }
}</lang>
C++
The following code fragments are all in one source file for this example (and in this order), but broken up here for clarity.
A reusable template function that handles the tokenizing, and is independent of any work that might wish to be done with the results:
<lang cpp>#include <vector>
- include <string> // for getline etc.
- include <iostream>
- include <sstream> // for istringstream
- include <algorithm> // for max
- include <iomanip> // for setw
- include <fstream> // for ofstream
using namespace std;
template< typename C > void enumerateFields( const string& strInput, char chDelim, C callback ) {
istringstream issFile( strInput ); string strLine; string strField; size_t nColIndex;
while ( getline( issFile, strLine ) ) { istringstream issLine( strLine ); nColIndex = 0;
while ( getline( issLine, strField, chDelim ) ) { callback( nColIndex, strField );
nColIndex++; } }
}</lang>
A function object that fills an array with column widths:
<lang cpp>typedef vector< size_t > ColWidths;
struct MaxColWidthsDeterminer {
explicit MaxColWidthsDeterminer( ColWidths& colWidths ) : m_colWidths( colWidths ) {}
void operator()( size_t nColIndex, const string& strField );
ColWidths& m_colWidths;
};
void MaxColWidthsDeterminer::operator()( size_t nColIndex,
const string& strField )
{
size_t nWidth = strField.length();
if ( nColIndex >= m_colWidths.size() ) m_colWidths.push_back( nWidth ); else m_colWidths[ nColIndex ] = max( m_colWidths[ nColIndex ], nWidth );
}</lang>
A function object that outputs fields formatted in columns:
<lang cpp>struct FormattedLister {
enum Alignment { eLeft, eRight, eCenter };
FormattedLister( const ColWidths& colWidths, ostream& os, Alignment alignment = eLeft ) : m_colWidths( colWidths ), m_os( os ), m_alignment( alignment ), m_nPrevColIndex( 0 ) { m_savedStreamFlags = os.flags(); m_os.setf( ( m_alignment == eRight ) ? ios::right : ios::left, ios::adjustfield ); }
~FormattedLister() { m_os.flags( m_savedStreamFlags ); }
void operator()( size_t nColIndex, const string& strField );
const ColWidths& m_colWidths; ostream& m_os; Alignment m_alignment; size_t m_nPrevColIndex; ios::fmtflags m_savedStreamFlags;
};
void FormattedLister::operator()( size_t nColIndex, const string& strField ) {
if ( nColIndex < m_nPrevColIndex ) m_os << '\n';
if ( m_alignment == eCenter ) { size_t nSpacesBefore = ( m_colWidths[ nColIndex ] - strField.length() ) / 2; size_t nSpacesAfter = m_colWidths[ nColIndex ] - strField.length() - nSpacesBefore + 1;
m_os << string( nSpacesBefore, ' ' ) << strField << string( nSpacesAfter, ' ' ); } else { m_os << setw( static_cast< streamsize >( m_colWidths[ nColIndex ] ) ) << strField << ' '; }
m_nPrevColIndex = nColIndex;
}</lang>
The test program, that makes a pass through the data to determine the column widths, and then three more for outputting in each of the three column alignments:
<lang cpp>int main() {
const string strInput( "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" "are$delineated$by$a$single$'dollar'$character,$write$a$program\n" "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" "column$are$separated$by$at$least$one$space.\n" "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" "justified,$right$justified,$or$center$justified$within$its$column." );
const char chDelim = '$';
ColWidths colWidths;
enumerateFields( strInput, chDelim, MaxColWidthsDeterminer( colWidths ) );
ofstream outFile( "ColumnAligner.txt" ); if ( outFile ) { enumerateFields( strInput, chDelim, FormattedLister( colWidths, outFile ) ); outFile << '\n';
enumerateFields( strInput, chDelim, FormattedLister( colWidths, outFile, FormattedLister::eRight ) ); outFile << '\n';
enumerateFields( strInput, chDelim, FormattedLister( colWidths, outFile, FormattedLister::eCenter ) ); outFile << endl; }
}</lang>
Common Lisp
<lang lisp>(defun nonempty (seq)
(position-if (lambda (x) (declare (ignore x)) t) seq))
(defun split (delim seq) "Splits seq on delim into a list of subsequences. Trailing empty subsequences are removed."
(labels ((f (seq &aux (pos (position delim seq))) (if pos (cons (subseq seq 0 pos) (f (subseq seq (1+ pos)))) (list seq)))) (let* ((list (f seq)) (end (position-if #'nonempty list :from-end t))) (subseq list 0 (1+ end)))))
(defun lengthen (list minlen filler-elem &aux (len (length list))) "Destructively pads list with filler-elem up to minlen."
(if (< len minlen) (nconc list (make-list (- minlen len) :initial-element filler-elem)) list))
(defun align-columns (text &key (align :left) &aux
(fmtmod (case align (:left "@") (:right ":") (:center "@:") (t (error "Invalid alignment.")))) (fields (mapcar (lambda (line) (split #\$ line)) (split #\Newline text))) (mostcols (loop for l in fields maximize (length l))) widest) (setf fields (mapcar (lambda (l) (lengthen l mostcols "")) fields)) (setf widest (loop for col below (length (first fields)) collect (loop for row in fields maximize (length (elt row col))))) (format nil (with-output-to-string (s) (princ "~{~{" s) (dolist (w widest) (format s "~~~d~a<~~a~~>" (1+ w) fmtmod)) (princ "~}~%~}" s)) fields))</lang>
D
<lang d> import std.stdio; import std.string; char[]text = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" "are$delineated$by$a$single$'dollar'$character,$write$a$program\n" "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" "column$are$separated$by$at$least$one$space.\n" "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" "justified,$right$justified,$or$center$justified$within$its$column.";
int main() {
char[][]lines = text.split("\n"); char[][][]words; int[]maxlens; foreach(line;lines) { words ~= line.split("$"); // make sure we have enough slots in maxlens if (words[$-1].length > maxlens.length) { maxlens.length = words[$-1].length; } foreach(i,word;words[$-1]) { if (word.length > maxlens[i]) maxlens[i] = word.length; } } // now that we've done a whole pass and grabbed all the max lengths, start over and pad everything to the right length foreach(line;words) foreach(i,ref word;line) { word ~= repeat(" ",maxlens[i]-word.length); } // now join all the words back into lines and print them foreach(line;words) { writefln("%s",line.join(" ")); } return 0;
} </lang>
E
<lang e> pragma.enable("accumulator")
def left(width, word) {
return word + " " * (width - word.size())
}
def center(width, word) {
def leftCount := (width - word.size()) // 2 return " " * leftCount + word + " " * (width - word.size() - leftCount)
}
def right(width, word) {
return " " * (width - word.size()) + word
}
def alignColumns(align, text) {
def split := accum [] for line in text.split("\n") { _.with(line.split("$")) } var widths := [] for line in split { for i => word in line { widths with= (i, widths.fetch(i, fn{0}).max(word.size())) } } return accum "" for line in split { _ + accum "" for i => word in line { _ + align(widths[i] + 1, word) } + "\n" }
}</lang>
<lang e>? def text := "Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column."; null
? println(alignColumns(left, text)) Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
? println(alignColumns(center, text))
Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left
justified, right justified, or center justified within its column.
? println(alignColumns(right, text))
Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.</lang>
Groovy
Solution: <lang groovy>def alignColumns = { align, rawText ->
def lines = rawText.tokenize('\n') def words = lines.collect { it.tokenize(/\$/) } def maxLineWords = words.collect {it.size()}.max() words = words.collect { line -> line + [] * (maxLineWords - line.size()) } def columnWidths = words.transpose().collect{ column -> column.collect { it.size() }.max() }
def justify = [ Right : { width, string -> string.padLeft(width) }, Left : { width, string -> string.padRight(width) }, Center : { width, string -> string.center(width) } ] def padAll = { pad, colWidths, lineWords -> [colWidths, lineWords].transpose().collect { pad(it) + ' ' } }
words.each { padAll(justify[align], columnWidths, it).each { print it }; println() }
}</lang>
Test Program: <lang groovy>def rawTextInput = Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.
['Left', 'Center', 'Right'].each { align ->
println "${align} Justified:" alignColumns(align, rawTextInput) println()
}</lang>
Output:
Left Justified: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Center Justified: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Right Justified: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
Haskell
<lang haskell> import Data.List import Control.Monad import Control.Arrow
dat = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" ++
"are$delineated$by$a$single$'dollar'$character,$write$a$program\n" ++ "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" ++ "column$are$separated$by$at$least$one$space.\n" ++ "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" ++ "justified,$right$justified,$or$center$justified$within$its$column.\n"
brkdwn = unfoldr (\xs -> if null xs then Nothing else Just (second (drop 1) (span ('$'/=) xs)) ) spas = repeat ' '
format j ls = map (concat. zipWith align colw) rows
where rows = map brkdwn $ lines ls colw = map (maximum. map length) . transpose $ rows align cw w = case j of 'c' -> (take l spas) ++ w ++ (take (succ r) spas) 'r' -> (take dl spas)++w++" " 'l' -> w ++ (take (succ dl) spas) where dl = cw-length w hdl = dl `div` 2 (l,r) | odd dl = (hdl,hdl+1) | otherwise = (hdl,hdl)
</lang> output example:
*Main> mapM_ putStrLn $ format 'c' dat Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
J
Solution, using 'LEFT CENTER RIGHT'=:i.3 to specify justification:
_2 {:\ ": <;._2@:,~&>/ '$';LF;text [ 9!:17 ,~ CENTER [ 9!:7 ' '$~11
Example:
text=: noun define Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column. ) _2 {:\ ": <;._2@:,~&>/'$';LF;text [ 9!:17 ,~ CENTER [ 9!:7 ' '$~11 Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
OCaml
<lang ocaml>#load "str.cma" open Str
let input = "\ Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column."
let () =
let lines = split (regexp_string "\n") input in let fields_l = List.map (split (regexp_string "$")) lines in let fields_l = List.map Array.of_list fields_l in let n = (* number of columns *) List.fold_left (fun n fields -> max n (Array.length fields)) 0 fields_l in let pads = Array.make n 0 in List.iter ( (* calculate the max padding for each column *) Array.iteri (fun i word -> pads.(i) <- max pads.(i) (String.length word)) ) fields_l;
let print f = List.iter (fun fields -> Array.iteri (fun i word -> f word (pads.(i) - (String.length word)) ) fields; print_newline() ) fields_l; in
(* left column-aligned output *) print (fun word pad -> let spaces = String.make pad ' ' in Printf.printf "%s%s " word spaces);
(* right column-aligned output *) print (fun word pad -> let spaces = String.make pad ' ' in Printf.printf "%s%s " spaces word);
(* center column-aligned output *) print (fun word pad -> let pad1 = pad / 2 in let pad2 = pad - pad1 in let sp1 = String.make pad1 ' ' in let sp2 = String.make pad2 ' ' in Printf.printf "%s%s%s " sp1 word sp2);
- </lang>
Perl
<lang Perl>
#/usr/bin/perl -w use strict ; die "Call : perl columnaligner.pl <inputfile> <printorientation>!\n" unless @ARGV == 2 ; #$ARGV[ 0 ] contains example file , $ARGV[1] any of 'left' , 'right' or 'center' die "last argument must be one of center, left or right!\n" unless $ARGV[ 1 ] =~ /center|left|right/ ; sub printLines( $$$ ) ; open ( INFILE , "<" , "$ARGV[ 0 ]" ) or die "Can't open $ARGV[ 0 ]!\n" ; my @lines = <INFILE> ; close ( INFILE ) ; map { chomp $_ } @lines ; my @fieldwidths = ( ) ; map { push @fieldwidths , length( $_ ) } split ( /\$/ , $lines[ 0 ] ) ; foreach my $i ( 1..$#lines ) { my @words = split( /\$/ , $lines[ $i ] ) ; foreach my $j ( 0..$#words ) { if ( $j <= $#fieldwidths ) { if ( length( $words[ $j ] ) > $fieldwidths[ $j ] ) { $fieldwidths[ $j ] = length( $words[ $j ] ) ; } } else { push @fieldwidths, length( $words[ $j ] ) ; } } } map { printLine( $_ , $ARGV[ 1 ] , \@fieldwidths ) } @lines ; ################################################################## #### sub printLine { my $line = shift ; my $orientation = shift ; my $widthref = shift ; my @words = split( /\$/, $line ) ; foreach my $k ( 0..$#words ) { my $printwidth = $widthref->[ $k ] + 1 ; if ( $orientation eq 'center' ) { $printwidth++ ; } if ( $orientation eq 'left' ) { print $words[ $k ] ; for ( my $i = 0 ; $i < $printwidth - length( $words[ $k ] ) ; $i++ ) { print " " ; } } if ( $orientation eq 'right' ) { for ( my $i = 0 ; $i < $printwidth - length( $words[ $k ] ) ; $i++ ) { print " " ; } print $words[ $k ] ; } if ( $orientation eq 'center' ) { my $left = int( ( $printwidth - length( $words[ $k ] ) ) / 2 ) ; my $right = $printwidth - length( $words[ $k ] ) - $left ; if ( $left == 1 ) { print " " ; } if ( $left > 1 ) { for ( my $i = 0 ; $i < $left ; $i++ ) { print " " ; } } print $words[ $k ] ; if ( $right == 1 ) { print " " ; } if ( $right > 1 ) { for ( my $i = 0 ; $i < $right ; $i++ ) { print " " ; } } } } print "\n" ; }
</lang>
Python
<lang python>from StringIO import StringIO
textinfile = Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.
j2justifier = dict(L=str.ljust, R=str.rjust, C=str.center)
def aligner(infile, justification = 'L'):
\ Justify columns of textual tabular input where the row separator is the newline and the field separator is a 'dollar' character. justification can be L, R, or C; (Left, Right, or Centered). Return the justified output as a string assert justification in 'LRC', "justification can be L, R, or C; (Left, Right, or Centered)." justifier = j2justifier[justification] fieldsbyrow= [line.strip().split('$') for line in infile] # pad to same number of fields per row maxfields = max(len(row) for row in fieldsbyrow) fieldsbyrow = [fields + []*(maxfields - len(fields)) for fields in fieldsbyrow] # rotate fieldsbycolumn = zip(*fieldsbyrow) # calculate max fieldwidth per column colwidths = [max(len(field) for field in column) for column in fieldsbycolumn] # pad fields in columns to colwidth with spaces fieldsbycolumn = [ [justifier(field, width) for field in column] for width, column in zip(colwidths, fieldsbycolumn) ] # rotate again fieldsbyrow = zip(*fieldsbycolumn) return "\n".join( " ".join(row) for row in fieldsbyrow)
for align in 'Left Right Center'.split():
infile = StringIO(textinfile) print "\n# %s Column-aligned output:" % align print aligner(infile, align[0])</lang>
Example output:
# Left Column-aligned output: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. # Right Column-aligned output: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. # Center Column-aligned output: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
R
<lang R>
- Read in text
lines <- readLines(tc <- textConnection("Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.")); close(tc)
- Split words by the dollar
words <- strsplit(lines, "\\$")
- Reformat
maxlen <- max(sapply(words, length)) words <- lapply(words, function(x) {length(x) <- maxlen; x}) block <- matrix(unlist(words), byrow=TRUE, ncol=maxlen) block[is.na(block)] <- "" leftjust <- format(block) rightjust <- format(block, justify="right") centrejust <- format(block, justify="centre")
noquote(leftjust) noquote(rightjust) noquote(centrejust) </lang> Right justified output shown.
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [1,] Given a text file of many lines, where fields within a line [2,] are delineated by a single 'dollar' character, write a program [3,] that aligns each column of fields by ensuring that words in each [4,] column are separated by at least one space. [5,] Further, allow for each word in a column to be either left [6,] justified, right justified, or center justified within its column.
Ruby
<lang ruby>require 'stringio'
textinfile = <<END Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column. END
J2justifier = {'L' => String.instance_method(:ljust),
'R' => String.instance_method(:rjust), 'C' => String.instance_method(:center)}
=begin Justify columns of textual tabular input where the record separator is the newline and the field separator is a 'dollar' character. justification can be L, R, or C; (Left, Right, or Centered).
Return the justified output as a string =end def aligner(infile, justification = 'L')
justifier = J2justifier[justification] fieldsbyrow = infile.map {|line| line.strip.split('$')} # pad to same number of fields per record maxfields = fieldsbyrow.map {|row| row.length}.max fieldsbyrow.map! {|row| row + []*(maxfields - row.length) } # calculate max fieldwidth per column colwidths = fieldsbyrow.transpose.map {|column| column.map {|field| field.length}.max } # pad fields in columns to colwidth with spaces fieldsbyrow.map! {|row| row.zip(colwidths).map {|field, width| justifier.bind(field)[width] } } fieldsbyrow.map {|row| row.join(" ")}.join("\n")
end
for align in %w{Left Right Center}
infile = StringIO.new(textinfile) puts "\n# %s Column-aligned output:" % align puts aligner(infile, align[0..0])
end</lang>
Example output:
# Left Column-aligned output: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. # Right Column-aligned output: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. # Center Column-aligned output: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
Tcl
<lang tcl>package require Tcl 8.5
set text {Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.}
array set max {} foreach line [split $text \n] {
set col 0 set thisline [split $line \$] lappend words $thisline foreach word $thisline { set max([incr col]) [expr {[info exists max($col)] ? max($max($col), [string length $word]) : [string length $word] }] }
}
proc justify {word position width} {
switch -exact -- $position { left { return [format "%-*s" $width $word] } center { set lpadw [expr {($width - [string length $word])/2}] return [format "%s%-*s" [string repeat " " $lpadw] [incr width -$lpadw] $word] } right { return [format "%*s" $width $word] } }
}
foreach position {left center right} {
foreach thisline $words { set col 0 set line "" foreach word $thisline { append line [justify $word $position $max([incr col])] " " } puts [string trimright $line] } puts ""
}</lang> Output:
Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
Ursala
The algorithm is to lex the text to a list of lists of strings assuming $ as a separator, then pad the lists out to the length of the maximum length list, transpose, do the same with each column, and transpose again. For left justification, nothing further but concatenation is needed. For right justification, each word's string of trailing blanks is moved to the beginning, and for center justification, the trailing blanks are divided equally between the beginning and end of each word. <lang Ursala>
- import std
text =
-[Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.]-
pad = sep`$*; @FS ~&rSSSK7+ (zipp` ^*D\~& leql$^)*rSSK7+ zipp0^*D/leql$^ ~&
just_left = mat` *+ pad just_right = mat` *+ pad; ==` ~-rlT** just_center = mat` *+ pad; ==` ~-rK30PlrK31PTT**
- show+
main = mat0 <.just_left,just_center,just_right> text </lang> output:
Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
Vedit macro language
This implementation converts the file currently being edited. The file can then be saved with different filename if required.
RS(10, "$") // Field separator #11 = 1 // Align: 1 = left, 2 = center, 3 = right // Reset column widths. Max 50 columns for (#1=40; #1<90; #1++) { #@1 = 0 } // Find max width of each column BOF Repeat(ALL) { for (#1=40; #1<90; #1++) { Match(@10, ADVANCE) // skip field separator if any #2 = Cur_Pos Search("|{|@(10),|N}", NOERR) // field separator or end of line #3 = Cur_Pos - #2 // width of text if (#3 > #@1) { #@1 = #3 } if (At_EOL) { Break } } Line(1, ERRBREAK) } // Convert lines BOF Repeat(ALL) { for (#1=40; #1<90; #1++) { #2 = Cur_Pos Search("|{|@(10),|N}", NOERR) if (At_EOL==0) { Del_Char(Chars_Matched) } #3 = #@1 - Cur_Pos + #2 // number of spaces to insert #4 = 0 if (#11 == 2) { #4 = #3/2; #3 -= #4 } // Center if (#11 == 3) { #4 = #3; #3 = 0 } // Right justify Set_Marker(1, Cur_Pos) Goto_Pos(#2) Ins_Char(' ', COUNT, #4) // add spaces before the word Goto_Pos(Marker(1)) Ins_Char(' ', COUNT, #3+1) // add spaces after the word if (At_EOL) { Break } } Line(1, ERRBREAK) }
Example output:
-- Left: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. -- Center: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column. -- Right: Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.