如何使用Console.WriteLine将文本对齐到列中?

95

我有一种列显示,但最后两列似乎没有对齐。这是我目前的代码:

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);
}

1
请查看此处的更一般化版本的问题,该问题与编程有关。那里的答案也值得一看。 - Adam Glauser
9个回答

350

试一试这个

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);

大括号内的第一个数字是索引,第二个数字是对齐方式。第二个数字的符号表示字符串应该左对齐还是右对齐。使用负数进行左对齐。

或者查看http://msdn.microsoft.com/en-us/library/aa331875(v=vs.71).aspx


22
实际上,您不需要使用string.format方法。换句话说,下面这段代码与使用string.format方法是等价的: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

74

顺便补充一下roya的回答。在C#6.0中,您现在可以使用字符串插值:

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

这实际上可以一行完成而不需要所有额外的美元符号,我只是觉得这样写更容易阅读。

另外,您还可以在 System.Console 上使用静态导入,使您可以这样做:

using static System.Console;

WriteLine(/* write stuff */);

7
有关“alignment”的文档。非常有帮助! - Frison Alexander
6
要左对齐,只需使用负数即可,就像这样:$"{thirtyPercentValue,-10}" - stomtech

15

与其尝试使用任意空格字符串手动对齐文本,您应该将实际制表符(\t 转义序列)嵌入到每个输出字符串中:

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);
}

41
只有当您的数据长度相似时,选项卡才能正常工作。如果您的数据长度不同,您应该使用royas的答案并使用格式化字符串。 - Tim
3
是的,他的回答更好。我一看到就点了赞,但是为了万无一失,我也保留了我的回答,以防更简单的方法更有效。不过,我不确定为什么我的被采纳了... :-) - Cody Gray
1
不要忘记使用 string.PadRight() 和 string.PadLeft()。 - nhershy
1
如果您正在尝试将文本与可变字体宽度对齐,Royas的答案可能并不有用。我认为您需要像这个答案中所示,使用填充和制表符的组合。 - sean

8

我知道,这是一个非常老的线程,但是提出的解决方案在周围有更长的字符串时并不是完全自动化的。

因此,我创建了一个小的帮助方法,完全自动化。只需传入一个字符串数组的列表,其中每个数组表示一行,数组中的每个元素则是该行的元素。

该方法可按以下方式使用:

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);

辅助方法如下:
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();
    }
}

希望这能帮助到某些人。来源是我博客中的一篇文章,链接在这里:http://dev.flauschig.ch/wordpress/?p=387

这是最准确的答案。尽管如此,我已经改进了您的方法,对于那些最后一行并不总是声明所有行的情况:http://pastebin.com/CVkavHgy - David Diez
如果你使用Console.Write打印“output”,则可以摆脱isFirst部分。只需要在foreach的末尾执行sb.AppendLine();即可。 - tomwaitforitmy

3

有几个NuGet包可以帮助格式化。在某些情况下,string.Format的功能已足够,但您可能希望根据内容自动调整列的大小。

ConsoleTableExt

ConsoleTableExt是一个简单的库,允许格式化表格,包括没有网格线的表格。(一个更受欢迎的包ConsoleTables似乎不支持无边框的表格。)以下是一个使用基于内容大小调整列宽的对象列表的示例:

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

如果你需要更多的功能,可以使用CsConsoleFormat来实现任何控制台格式化。†例如,以下代码将一个对象列表按照类似于使用string.Format时固定列宽为10的网格格式进行格式化:

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)
                    })
                )
        ));

它看起来比纯的string.Format更复杂,但现在它可以定制化。例如:
  • 如果您想要根据内容自动调整列的大小,请将AddColumns(10, 10, 10, 10, 10)替换为AddColumns(-1, -1, -1, -1, -1)-1GridLength.Auto的快捷方式,您有更多的大小选项,包括控制台窗口宽度的百分比)。

  • 如果您想要将数字列右对齐,请向单元格的初始化程序添加{ Align = Right }

  • 如果您想要着色某一列,请向单元格的初始化程序添加{ Color = Yellow }

  • 您可以更改边框样式等。

† CsConsoleFormat是由我开发的。


2
对齐方式可以与字符串插值相结合,方法是将其放在‘:’格式字符之前。
Console.WriteLine($"{name,40} {MaterialArea,10:N2}m² {MaterialWeightInLbs,10:N0}lbs {Cost,10:C2}");

2
您可以在格式字符串中使用制表符代替列之间的空格,并/或者设置列的最大大小...

2

进行一些填充,即

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

            }


这对我来说很有效。

1
我很喜欢这里提到的库,但我有一个比填充或进行大量字符串操作更简单的想法,您只需使用数据的最大字符串长度手动设置光标即可。以下是一些示例代码(未经测试)来帮助您理解:
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]);
}

如果您有更多的行,您可以轻松地推断出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接