Task: Align columns



C#: using System;
class ColumnAlignerProgram
{
    delegate string Justification(string s, int width);

    static string[] AlignColumns(string[] lines, Justification justification)
    {
        const char Separator = '$';
        
        string[][] table = new string[lines.Length][];
        int columns = 0;
        for (int i = 0; i < lines.Length; i++)
        {
            string[] row = lines[i].TrimEnd(Separator).Split(Separator);
            if (columns < row.Length) columns = row.Length;
            table[i] = row;
        }
        
        string[][] formattedTable = new string[table.Length][];
        for (int i = 0; i < formattedTable.Length; i++)
        {
            formattedTable[i] = new string[columns];
        }
        for (int j = 0; j < columns; j++)
        {
            
            int columnWidth = 0;
            for (int i = 0; i < table.Length; i++)
            {
                if (j < table[i].Length && columnWidth < table[i][j].Length)
                    columnWidth = table[i][j].Length;
            }
            
            for (int i = 0; i < formattedTable.Length; i++)
            {
                if (j < table[i].Length)
                    formattedTable[i][j] = justification(table[i][j], columnWidth);
                else 
                    formattedTable[i][j] = new String(' ', columnWidth);
            }
        }
        
        string[] result = new string[formattedTable.Length];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = String.Join(" ", formattedTable[i]);
        }
        return result;
    }

    static string JustifyLeft(string s, int width) { return s.PadRight(width); }
    static string JustifyRight(string s, int width) { return s.PadLeft(width); }
    static string JustifyCenter(string s, int width) 
    { 
        return s.PadLeft((width + s.Length) / 2).PadRight(width); 
    }

    static void Main()
    {
        string[] 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.",
        };

        foreach (string line in AlignColumns(input, JustifyCenter))
        {
            Console.WriteLine(line);
        }
    }
}








Fortran:       SUBROUTINE RAKE(IN,M,X,WAY)	
Collates column widths so that each column is wide enough for its widest member.
       INTEGER IN		
       INTEGER M		
       CHARACTER*1 X		
       INTEGER WAY		
       INTEGER W(M + 1)		
       INTEGER C(0:M + 1)	
       CHARACTER*(M) ACARD	
       CHARACTER*(28 + 4*M) FORMAT	
       INTEGER I		
       INTEGER L,LF		
       INTEGER NF,MF		
       CHARACTER*6 WAYNESS(-1:+1)	
       PARAMETER (WAYNESS = (/"Left","Centre","Right"/))	
       INTEGER LINPR	
       COMMON LINPR	
        W = 0		
        MF = 0		
        C(0) = 0	
        WRITE (LINPR,*)	
        WRITE (LINPR,*) "Align ",WAYNESS(MIN(MAX(WAY,-1),+1))	

Chase through the file assessing the lengths of each field.
   10   READ (IN,11,END = 20) L,ACARD(1:L)	
   11   FORMAT (Q,A)				
        CALL LIZZIEBORDEN	
        W(1:NF) = MAX(W(1:NF),C(1:NF) - C(0:NF - 1) - 1)	
        MF = MAX(MF,NF)		
        GO TO 10		

Concoct a FORMAT based on the maximum size of each field. Plus one.
   20   REWIND(IN)		
        WRITE (FORMAT,21) W(1:MF) + 1	
   21   FORMAT ("(",<MF>("A",I0,","))	
        LF = INDEX(FORMAT,", ")		
        IF (LF.LE.0) STOP "Format trouble
        FORMAT(LF:LF) = ")"			
        WRITE (LINPR,*) "Format",FORMAT(1:LF)	

Chug afresh, this time knowing the maximum length of each field.
   30   READ (IN,11,END = 40) L,ACARD(1:L)	
        CALL LIZZIEBORDEN		
        SELECT CASE(WAY)	
         CASE(-1)		
          WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1)//	
     1     REPEAT(" ",W(I) - C(I) + C(I - 1) + 1),I = 1,NF)	
         CASE( 0)		
          WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1)//	
     1     REPEAT(" ",(W(I) - C(I) + C(I - 1) + 1)/2),I = 1,NF)	
         CASE(+1)		
          WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1),I = 1,NF)	
         CASE DEFAULT		
         WRITE (LINPR,*) "Huh? WAY=",WAY	
         STOP "Unanticipated value for WAY
        END SELECT		
        GO TO 30		
Closedown
   40   REWIND(IN)		
       CONTAINS	
        SUBROUTINE LIZZIEBORDEN	
          NF = 0		
          DO I = 1,L		
            IF (ICHAR(ACARD(I:I)).EQ.ICHAR(X)) THEN	
              NF = NF + 1		
              C(NF) = I		
            END IF		
          END DO		
          NF = NF + 1		
          C(NF) = L + 1		
        END SUBROUTINE LIZZIEBORDEN	
      END SUBROUTINE RAKE	

      INTEGER L,M,N	
      INTEGER LINPR,IN	
      COMMON LINPR	
      LINPR = 6		
      IN = 10		
      OPEN (IN,FILE="Rake.txt",STATUS="OLD",ACTION="READ")	
      N = 0		
      M = 0		

    1 READ (IN,2,END = 10) L	
    2 FORMAT (Q)	
      N = N + 1		
      M = MAX(M,L)	
      GO TO 1		

   10 REWIND (IN)	
      WRITE (LINPR,*) N,"Recs, longest rec. length is ",M
      CALL RAKE(IN,M,"$",-1)	
      CALL RAKE(IN,M,"$", 0)	
      CALL RAKE(IN,M,"$",+1)	
      END