94

I have a sort of column display, but the end two column's seem to not be aligning correctly. This is the code I have at the moment:

Console.WriteLine("Customer name    " 
    + "sales          " 
    + "fee to be paid    " 
    + "70% value       " 
    + "30% value");
for (int DisplayPos = 0; DisplayPos < LineNum; DisplayPos = DisplayPos + 1)
{
    seventy_percent_value = ((fee_payable[DisplayPos] / 10.0) * 7);
    thirty_percent_value = ((fee_payable[DisplayPos] / 10.0) * 3);          
    Console.WriteLine(customer[DisplayPos] + "         " 
        + sales_figures[DisplayPos] + "               " 
        + fee_payable[DisplayPos] + "           " 
        + seventy_percent_value + "           " 
        + thirty_percent_value);
}
GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Stephen Smith
  • 953
  • 1
  • 7
  • 6
  • 1
    [See here](http://stackoverflow.com/questions/10303319/aligning-text-output-by-the-console) for a more generalized version of this question. The answers there are worth checking out too. – Adam Glauser Oct 08 '14 at 14:55

9 Answers9

349

Try this

Console.WriteLine("{0,10}{1,10}{2,10}{3,10}{4,10}",
  customer[DisplayPos],
  sales_figures[DisplayPos],
  fee_payable[DisplayPos], 
  seventy_percent_value,
  thirty_percent_value);

where the first number inside the curly brackets is the index and the second is the alignment. The sign of the second number indicates if the string should be left or right aligned. Use negative numbers for left alignment.

Or look at http://msdn.microsoft.com/en-us/library/aa331875(v=vs.71).aspx

David Sherret
  • 101,669
  • 28
  • 188
  • 178
royas
  • 4,846
  • 2
  • 16
  • 12
  • 21
    actually you do not need to pass the string.format method. In other words it will be the same thing doing: `Console.WriteLine("{0,10}{1,10}{2,10}{3,10}{4,10}", customer[DisplayPos], sales_figures[DisplayPos], fee_payable[DisplayPos], seventy_percent_value, thirty_percent_value);` – Tono Nam Sep 29 '12 at 23:57
74

Just to add to roya's answer. In c# 6.0 you can now use string interpolation:

Console.WriteLine($"{customer[DisplayPos],10}" +
                  $"{salesFigures[DisplayPos],10}" +
                  $"{feePayable[DisplayPos],10}" +
                  $"{seventyPercentValue,10}" +
                  $"{thirtyPercentValue,10}");

This can actually be one line without all the extra dollars, I just think it makes it a bit easier to read like this.

And you could also use a static import on System.Console, allowing you to do this:

using static System.Console;

WriteLine(/* write stuff */);
Daniel Hakimi
  • 1,153
  • 9
  • 16
15

Instead of trying to manually align the text into columns with arbitrary strings of spaces, you should embed actual tabs (the \t escape sequence) into each output string:

Console.WriteLine("Customer name" + "\t"
    + "sales" + "\t" 
    + "fee to be paid" + "\t" 
    + "70% value" + "\t" 
    + "30% value");
for (int DisplayPos = 0; DisplayPos < LineNum; DisplayPos++)
{
    seventy_percent_value = ((fee_payable[DisplayPos] / 10.0) * 7);
    thirty_percent_value = ((fee_payable[DisplayPos] / 10.0) * 3);          
    Console.WriteLine(customer[DisplayPos] + "\t" 
        + sales_figures[DisplayPos] + "\t" 
        + fee_payable + "\t\t"
        + seventy_percent_value + "\t\t" 
        + thirty_percent_value);
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 41
    Tabs will only work well if your data is all of a similar length. If you have data that is of varying length, you should use royas answer with format strings. – Tim Feb 27 '12 at 16:25
  • 3
    Yes, his is a much better answer. I upvoted it as soon as I saw it, but left mine just in case the simpler approach would be more effective. I'm not sure why mine was accepted, though... :-) – Cody Gray - on strike Feb 27 '12 at 17:01
  • 1
    And don't forget about string.PadRight() and string.PadLeft() – nhershy Apr 09 '18 at 02:37
  • 1
    royas answer is not helpful if you are trying to align text with a variable font width. I think you need to use a combination of padding with tabs like in [this answer](https://stackoverflow.com/a/36110064/9362652). – sean Oct 23 '18 at 17:01
8

I know, very old thread but the proposed solution was not fully automatic when there are longer strings around.

I therefore created a small helper method which does it fully automatic. Just pass in a list of string array where each array represents a line and each element in the array, well an element of the line.

The method can be used like this:

var lines = new List<string[]>();
lines.Add(new[] { "What", "Before", "After"});
lines.Add(new[] { "Name:", name1, name2});
lines.Add(new[] { "City:", city1, city2});
lines.Add(new[] { "Zip:", zip1, zip2});
lines.Add(new[] { "Street:", street1, street2});
var output = ConsoleUtility.PadElementsInLines(lines, 3);

The helper method is as follows:

public static class ConsoleUtility
{
    /// <summary>
    /// Converts a List of string arrays to a string where each element in each line is correctly padded.
    /// Make sure that each array contains the same amount of elements!
    /// - Example without:
    /// Title Name Street
    /// Mr. Roman Sesamstreet
    /// Mrs. Claudia Abbey Road
    /// - Example with:
    /// Title   Name      Street
    /// Mr.     Roman     Sesamstreet
    /// Mrs.    Claudia   Abbey Road
    /// <param name="lines">List lines, where each line is an array of elements for that line.</param>
    /// <param name="padding">Additional padding between each element (default = 1)</param>
    /// </summary>
    public static string PadElementsInLines(List<string[]> lines, int padding = 1)
    {
        // Calculate maximum numbers for each element accross all lines
        var numElements = lines[0].Length;
        var maxValues = new int[numElements];
        for (int i = 0; i < numElements; i++)
        {
            maxValues[i] = lines.Max(x => x[i].Length) + padding;
        }
        var sb = new StringBuilder();
        // Build the output
        bool isFirst = true;
        foreach (var line in lines)
        {
            if (!isFirst)
            {
                sb.AppendLine();
            }
            isFirst = false;
            for (int i = 0; i < line.Length; i++)
            {
                var value = line[i];
                // Append the value with padding of the maximum length of any value for this element
                sb.Append(value.PadRight(maxValues[i]));
            }
        }
        return sb.ToString();
    }
}

Hope this helps someone. The source is from a post in my blog here: http://dev.flauschig.ch/wordpress/?p=387

Roemer
  • 2,012
  • 1
  • 24
  • 26
  • This is the most accurate answer. Nonetheless I have improved your method for that last line that not always has all the lines declared: http://pastebin.com/CVkavHgy – David Diez Jan 23 '15 at 10:52
  • If you use Console.Write to print "output" you can get rid of that ```isFirst``` part. Just do the ```sb.AppendLine();``` at the end of the foreach. – tomwaitforitmy Aug 10 '19 at 10:30
3

There're several NuGet packages which can help with formatting. In some cases the capabilities of string.Format are enough, but you may want to auto-size columns based on content, at least.

ConsoleTableExt

ConsoleTableExt is a simple library which allows formatting tables, including tables without grid lines. (A more popular package ConsoleTables doesn't seem to support borderless tables.) Here's an example of formatting a list of objects with columns sized based on their content:

ConsoleTableBuilder
    .From(orders
        .Select(o => new object[] {
            o.CustomerName,
            o.Sales,
            o.Fee,
            o.Value70,
            o.Value30
        })
        .ToList())
    .WithColumn(
        "Customer",
        "Sales",
        "Fee",
        "70% value",
        "30% value")
    .WithFormat(ConsoleTableBuilderFormat.Minimal)
    .WithOptions(new ConsoleTableBuilderOption { DividerString = "" })
    .ExportAndWriteLine();

CsConsoleFormat

If you need more features than that, any console formatting can be achieved with CsConsoleFormat.† For example, here's formatting of a list of objects as a grid with fixed column width of 10, like in the other answers using string.Format:

ConsoleRenderer.RenderDocument(
    new Document { Color = ConsoleColor.Gray }
        .AddChildren(
            new Grid { Stroke = LineThickness.None }
                .AddColumns(10, 10, 10, 10, 10)
                .AddChildren(
                    new Div("Customer"),
                    new Div("Sales"),
                    new Div("Fee"),
                    new Div("70% value"),
                    new Div("30% value"),
                    orders.Select(o => new object[] {
                        new Div().AddChildren(o.CustomerName),
                        new Div().AddChildren(o.Sales),
                        new Div().AddChildren(o.Fee),
                        new Div().AddChildren(o.Value70),
                        new Div().AddChildren(o.Value30)
                    })
                )
        ));

It may look more complicated than pure string.Format, but now it can be customized. For example:

  • If you want to auto-size columns based on content, replace AddColumns(10, 10, 10, 10, 10) with AddColumns(-1, -1, -1, -1, -1) (-1 is a shortcut to GridLength.Auto, you have more sizing options, including percentage of console window's width).

  • If you want to align number columns to the right, add { Align = Right } to a cell's initializer.

  • If you want to color a column, add { Color = Yellow } to a cell's initializer.

  • You can change border styles and more.

† CsConsoleFormat was developed by me.

Athari
  • 33,702
  • 16
  • 105
  • 146
2

You could use tabs instead of spaces between columns, and/or set maximum size for a column in format strings ...

LaGrandMere
  • 10,265
  • 1
  • 33
  • 41
2

Do some padding, i.e.

          public static void prn(string fname, string fvalue)
            {
                string outstring = fname.PadRight(20)  +"\t\t  " + fvalue;
                Console.WriteLine(outstring);

            }

This worked well, at least for me.

2

The alignment can be combined with string interpolation by putting it in front of the ':' format character.

Console.WriteLine($"{name,40} {MaterialArea,10:N2}m² {MaterialWeightInLbs,10:N0}lbs {Cost,10:C2}");
Bryan Legend
  • 6,790
  • 1
  • 59
  • 60
1

I really like those libraries mentioned here but I had an idea that could be simpler than just padding or doing tons of string manipulations,

You could just manually set your cursor using the maximum string length of your data. Here's some code to get the idea (not tested):

var column1[] = {"test", "longer test", "etc"}
var column2[] = {"data", "more data", "etc"}
var offset = strings.OrderByDescending(s => s.Length).First().Length;
for (var i = 0; i < column.Length; i++) {
    Console.Write(column[i]);
    Console.CursorLeft = offset + 1;
    Console.WriteLine(column2[i]);
}

you could easily extrapolate if you have more rows.

Sauleil
  • 2,573
  • 1
  • 24
  • 27