如何:在控制台应用程序(C#)中绘制表格的最佳方法


103

我有一个有趣的问题。想象一下,我有很多数据以非常快的时间间隔进行更改。我想将该数据显示为控制台应用程序中的表格。外汇:

-------------------------------------------------------------------------
|    Column 1     |    Column 2     |    Column 3     |    Column 4     |
-------------------------------------------------------------------------
|                 |                 |                 |                 |
|                 |                 |                 |                 |
|                 |                 |                 |                 |
-------------------------------------------------------------------------

如何保持速度快以及如何固定列宽?我知道如何在Java中执行此操作,但我不知道如何在C#中执行此操作。


6
如果您提供Java解决方案,以便可以帮助您转换为C#,该怎么办?但是看看带有Length / PadLeft / PadRight / ...的String类吧
Scoregraphic

Answers:


66

您可以执行以下操作:

static int tableWidth = 73;

static void Main(string[] args)
{
    Console.Clear();
    PrintLine();
    PrintRow("Column 1", "Column 2", "Column 3", "Column 4");
    PrintLine();
    PrintRow("", "", "", "");
    PrintRow("", "", "", "");
    PrintLine();
    Console.ReadLine();
}

static void PrintLine()
{
    Console.WriteLine(new string('-', tableWidth));
}

static void PrintRow(params string[] columns)
{
    int width = (tableWidth - columns.Length) / columns.Length;
    string row = "|";

    foreach (string column in columns)
    {
        row += AlignCentre(column, width) + "|";
    }

    Console.WriteLine(row);
}

static string AlignCentre(string text, int width)
{
    text = text.Length > width ? text.Substring(0, width - 3) + "..." : text;

    if (string.IsNullOrEmpty(text))
    {
        return new string(' ', width);
    }
    else
    {
        return text.PadRight(width - (width - text.Length) / 2).PadLeft(width);
    }
}

136

String.Format与对齐值一起使用。

例如:

String.Format("|{0,5}|{1,5}|{2,5}|{3,5}|", arg0, arg1, arg2, arg3);

创建一个格式化的行。


那不会打印相同的值四次吗?
布赖恩·拉斯穆森

1
是的,您应该修复:String.Format(“ | {0,10} | {1,10} | {2,10} | {3,10} |”,arg0,arg1,arg2,arg3);
卢卡斯·史考斯卡斯(LukasŠalkauskas),2009年

1
如果您不需要桌子的边缘和角落,这很好用。不要忘记使用PadRight(x)或PadLeft(x)来帮助您调整间距。对于我所需要的,我只需要一些易于阅读的内容,并且在除去| s之后就可以了。
CokoBWare 2015年

3
用途-为左对齐的内容,如:{0,-5}
迈赫迪德赫加尼

当一个参数溢出一行中的可用空间边缘或包含一个换行符时,我将如何使用它来对齐值?
user9811991 '19

36

编辑:感谢@superologic,您现在可以在github中查找和改进以下代码!


我根据一些想法在这里写了这个课。列的宽度是最佳的,它可以使用以下简单的API处理对象数组:

static void Main(string[] args)
{
  IEnumerable<Tuple<int, string, string>> authors =
    new[]
    {
      Tuple.Create(1, "Isaac", "Asimov"),
      Tuple.Create(2, "Robert", "Heinlein"),
      Tuple.Create(3, "Frank", "Herbert"),
      Tuple.Create(4, "Aldous", "Huxley"),
    };

  Console.WriteLine(authors.ToStringTable(
    new[] {"Id", "First Name", "Surname"},
    a => a.Item1, a => a.Item2, a => a.Item3));

  /* Result:        
  | Id | First Name | Surname  |
  |----------------------------|
  | 1  | Isaac      | Asimov   |
  | 2  | Robert     | Heinlein |
  | 3  | Frank      | Herbert  |
  | 4  | Aldous     | Huxley   |
  */
}

这是课程:

public static class TableParser
{
  public static string ToStringTable<T>(
    this IEnumerable<T> values,
    string[] columnHeaders,
    params Func<T, object>[] valueSelectors)
  {
    return ToStringTable(values.ToArray(), columnHeaders, valueSelectors);
  }

  public static string ToStringTable<T>(
    this T[] values,
    string[] columnHeaders,
    params Func<T, object>[] valueSelectors)
  {
    Debug.Assert(columnHeaders.Length == valueSelectors.Length);

    var arrValues = new string[values.Length + 1, valueSelectors.Length];

    // Fill headers
    for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++)
    {
      arrValues[0, colIndex] = columnHeaders[colIndex];
    }

    // Fill table rows
    for (int rowIndex = 1; rowIndex < arrValues.GetLength(0); rowIndex++)
    {
      for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++)
      {
        arrValues[rowIndex, colIndex] = valueSelectors[colIndex]
          .Invoke(values[rowIndex - 1]).ToString();
      }
    }

    return ToStringTable(arrValues);
  }

  public static string ToStringTable(this string[,] arrValues)
  {
    int[] maxColumnsWidth = GetMaxColumnsWidth(arrValues);
    var headerSpliter = new string('-', maxColumnsWidth.Sum(i => i + 3) - 1);

    var sb = new StringBuilder();
    for (int rowIndex = 0; rowIndex < arrValues.GetLength(0); rowIndex++)
    {
      for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++)
      {
        // Print cell
        string cell = arrValues[rowIndex, colIndex];
        cell = cell.PadRight(maxColumnsWidth[colIndex]);
        sb.Append(" | ");
        sb.Append(cell);
      }

      // Print end of line
      sb.Append(" | ");
      sb.AppendLine();

      // Print splitter
      if (rowIndex == 0)
      {
        sb.AppendFormat(" |{0}| ", headerSpliter);
        sb.AppendLine();
      }
    }

    return sb.ToString();
  }

  private static int[] GetMaxColumnsWidth(string[,] arrValues)
  {
    var maxColumnsWidth = new int[arrValues.GetLength(1)];
    for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++)
    {
      for (int rowIndex = 0; rowIndex < arrValues.GetLength(0); rowIndex++)
      {
        int newLength = arrValues[rowIndex, colIndex].Length;
        int oldLength = maxColumnsWidth[colIndex];

        if (newLength > oldLength)
        {
          maxColumnsWidth[colIndex] = newLength;
        }
      }
    }

    return maxColumnsWidth;
  }
}

编辑:我添加了一个小改进-如果您希望列标题为属性名称,请添加以下方法TableParser(请注意,由于反射,它会慢一些):

public static string ToStringTable<T>(
    this IEnumerable<T> values,
    params Expression<Func<T, object>>[] valueSelectors)
{
  var headers = valueSelectors.Select(func => GetProperty(func).Name).ToArray();
  var selectors = valueSelectors.Select(exp => exp.Compile()).ToArray();
  return ToStringTable(values, headers, selectors);
}

private static PropertyInfo GetProperty<T>(Expression<Func<T, object>> expresstion)
{
  if (expresstion.Body is UnaryExpression)
  {
    if ((expresstion.Body as UnaryExpression).Operand is MemberExpression)
    {
      return ((expresstion.Body as UnaryExpression).Operand as MemberExpression).Member as PropertyInfo;
    }
  }

  if ((expresstion.Body is MemberExpression))
  {
    return (expresstion.Body as MemberExpression).Member as PropertyInfo;
  }
  return null;
}

GetProperty方法在哪里?
超级逻辑2014年

1
@superologic,它已添加在答案的底部。感谢您的关注。
HuBeZa 2014年

1
与这里的其他答案相比,我喜欢独立的扩展方法样式。谢谢!
詹姆斯·豪格

1
太棒了,我使用了它,并且效果很好,只需将其编辑arrValues[rowIndex, colIndex] = valueSelectors[colIndex].Invoke(values[rowIndex - 1]).ToString();var val = valueSelectors[colIndex].Invoke(values[rowIndex - 1]); arrValues[rowIndex, colIndex] = val == null ? "null" : val.ToString();以这种方式显示为null。
H_H

在您开始使用非Unicode字符之前,此方法非常有用。然后宽度变得一团糟。
汤姆

30

有几种开放源代码库允许控制台表格格式化,范围从简单(如此处答案中的代码示例)到更高级。

控制台表

从NuGet统计数据来看,用于格式化表格的最流行的库是ConsoleTable。表的构造如下(来自自述文件):

var table = new ConsoleTable("one", "two", "three");
table.AddRow(1, 2, 3)
     .AddRow("this line should be longer", "yes it is", "oh");

可以使用预定义样式之一来格式化表格。它看起来像这样:

--------------------------------------------------
| one                        | two       | three |
--------------------------------------------------
| 1                          | 2         | 3     |
--------------------------------------------------
| this line should be longer | yes it is | oh    |
--------------------------------------------------

该库要求没有格式的单行单元格。

有一些基于ConsoleTable的库,它们具有稍微扩展的功能集,例如更多的线型。

CsConsoleFormat

如果需要更复杂的格式,则可以使用CsConsoleFormat。†这是从过程列表(从示例项目)生成的表:

new Grid { Stroke = StrokeHeader, StrokeColor = DarkGray }
    .AddColumns(
        new Column { Width = GridLength.Auto },
        new Column { Width = GridLength.Auto, MaxWidth = 20 },
        new Column { Width = GridLength.Star(1) },
        new Column { Width = GridLength.Auto }
    )
    .AddChildren(
        new Cell { Stroke = StrokeHeader, Color = White }
            .AddChildren("Id"),
        new Cell { Stroke = StrokeHeader, Color = White }
            .AddChildren("Name"),
        new Cell { Stroke = StrokeHeader, Color = White }
            .AddChildren("Main Window Title"),
        new Cell { Stroke = StrokeHeader, Color = White }
            .AddChildren("Private Memory"),
        processes.Select(process => new[] {
            new Cell { Stroke = StrokeRight }
                .AddChildren(process.Id),
            new Cell { Stroke = StrokeRight, Color = Yellow, TextWrap = TextWrapping.NoWrap }
                .AddChildren(process.ProcessName),
            new Cell { Stroke = StrokeRight, Color = White, TextWrap = TextWrapping.NoWrap }
                .AddChildren(process.MainWindowTitle),
            new Cell { Stroke = LineThickness.None, Align = HorizontalAlignment.Right }
                .AddChildren(process.PrivateMemorySize64.ToString("n0")),
        })
    )

最终结果将如下所示:

它支持任何类型的表格行(包括几行和可自定义),带自动换行的多行单元格,颜色,根据内容或百分比增长的列,文本对齐方式等。

†CsConsoleFormat由我开发。


我知道它已经很老了,但是如何创建多行单元呢?我在任何地方都找不到它
Morta1,2018年

1
@ Morta1这个答案和CsConsoleFormat库都不旧。如果您使用的是CsConsoleFormat,则可以手动插入换行符(单元格中的字符串中的“ \ n”),并且可以正确处理,也可以让库自动换行(这是默认设置,因此您不必添加TextWrap = TextWrapping.NoWrap与上面的示例不同)。
Athari

感谢您的快速回答,有没有办法用ConsoleTable做到这一点?
Morta1

1
@ Morta1不,我认为没有任何简单的库支持该功能。
Athari

1
@HarveyDarvey之类的new Cell(text) { Color = text == "true" ? Green : Red }。如果您有许多具有相似格式规则的表,则可以将该代码放入某个函数中,无论是用于单元格,行还是整个表。
Athari

23
class ArrayPrinter
    {
    #region Declarations

    static bool isLeftAligned = false;
    const string cellLeftTop = "┌";
    const string cellRightTop = "┐";
    const string cellLeftBottom = "└";
    const string cellRightBottom = "┘";
    const string cellHorizontalJointTop = "┬";
    const string cellHorizontalJointbottom = "┴";
    const string cellVerticalJointLeft = "├";
    const string cellTJoint = "┼";
    const string cellVerticalJointRight = "┤";
    const string cellHorizontalLine = "─";
    const string cellVerticalLine = "│";

    #endregion

    #region Private Methods

    private static int GetMaxCellWidth(string[,] arrValues)
    {
        int maxWidth = 1;

        for (int i = 0; i < arrValues.GetLength(0); i++)
        {
            for (int j = 0; j < arrValues.GetLength(1); j++)
            {
                int length = arrValues[i, j].Length;
                if (length > maxWidth)
                {
                    maxWidth = length;
                }
            }
        }

        return maxWidth;
    }

    private static string GetDataInTableFormat(string[,] arrValues)
    {
        string formattedString = string.Empty;

        if (arrValues == null)
            return formattedString;

        int dimension1Length = arrValues.GetLength(0);
        int dimension2Length = arrValues.GetLength(1);

        int maxCellWidth = GetMaxCellWidth(arrValues);
        int indentLength = (dimension2Length * maxCellWidth) + (dimension2Length - 1);
        //printing top line;
        formattedString = string.Format("{0}{1}{2}{3}", cellLeftTop, Indent(indentLength), cellRightTop, System.Environment.NewLine);

        for (int i = 0; i < dimension1Length; i++)
        {
            string lineWithValues = cellVerticalLine;
            string line = cellVerticalJointLeft;
            for (int j = 0; j < dimension2Length; j++)
            {
                string value = (isLeftAligned) ? arrValues[i, j].PadRight(maxCellWidth, ' ') : arrValues[i, j].PadLeft(maxCellWidth, ' ');
                lineWithValues += string.Format("{0}{1}", value, cellVerticalLine);
                line += Indent(maxCellWidth);
                if (j < (dimension2Length - 1))
                {
                    line += cellTJoint;
                }
            }
            line += cellVerticalJointRight;
            formattedString += string.Format("{0}{1}", lineWithValues, System.Environment.NewLine);
            if (i < (dimension1Length - 1))
            {
                formattedString += string.Format("{0}{1}", line, System.Environment.NewLine);
            }
        }

        //printing bottom line
        formattedString += string.Format("{0}{1}{2}{3}", cellLeftBottom, Indent(indentLength), cellRightBottom, System.Environment.NewLine);
        return formattedString;
    }

    private static string Indent(int count)
    {
        return string.Empty.PadLeft(count, '─');                 
    }

    #endregion

    #region Public Methods

    public static void PrintToStream(string[,] arrValues, StreamWriter writer)
    {
        if (arrValues == null)
            return;

        if (writer == null)
            return;

        writer.Write(GetDataInTableFormat(arrValues));
    }

    public static void PrintToConsole(string[,] arrValues)
    {
        if (arrValues == null)
            return;

        Console.WriteLine(GetDataInTableFormat(arrValues));
    }

    #endregion

    static void Main(string[] args)
    {           
        int value = 997;
        string[,] arrValues = new string[5, 5];
        for (int i = 0; i < arrValues.GetLength(0); i++)
        {
            for (int j = 0; j < arrValues.GetLength(1); j++)
            {
                value++;
                arrValues[i, j] = value.ToString();
            }
        }
        ArrayPrinter.PrintToConsole(arrValues);
        Console.ReadLine();
    }
}

1
为了获得更大的灵活性和更好的代码重用性:1.将参数类型从更改StreamWriterTextWriter。2.将PrintToConsole代码替换为:PrintToStream(arrValues, Console.Out);3. ??? 4.利润!
HuBeZa

15

我想要可变宽度的列,而我并不特别在乎方框字符。另外,我需要为每行打印一些额外的信息。

因此,万一其他人需要,我可以为您节省几分钟:

public class TestTableBuilder
{

    public interface ITextRow
    {
        String Output();
        void Output(StringBuilder sb);
        Object Tag { get; set; }
    }

    public class TableBuilder : IEnumerable<ITextRow>
    {
        protected class TextRow : List<String>, ITextRow
        {
            protected TableBuilder owner = null;
            public TextRow(TableBuilder Owner)
            {
                owner = Owner;
                if (owner == null) throw new ArgumentException("Owner");
            }
            public String Output()
            {
                StringBuilder sb = new StringBuilder();
                Output(sb);
                return sb.ToString();
            }
            public void Output(StringBuilder sb)
            {
                sb.AppendFormat(owner.FormatString, this.ToArray());
            }
            public Object Tag { get; set; }
        }

        public String Separator { get; set; }

        protected List<ITextRow> rows = new List<ITextRow>();
        protected List<int> colLength = new List<int>();

        public TableBuilder()
        {
            Separator = "  ";
        }

        public TableBuilder(String separator)
            : this()
        {
            Separator = separator;
        }

        public ITextRow AddRow(params object[] cols)
        {
            TextRow row = new TextRow(this);
            foreach (object o in cols)
            {
                String str = o.ToString().Trim();
                row.Add(str);
                if (colLength.Count >= row.Count)
                {
                    int curLength = colLength[row.Count - 1];
                    if (str.Length > curLength) colLength[row.Count - 1] = str.Length;
                }
                else
                {
                    colLength.Add(str.Length);
                }
            }
            rows.Add(row);
            return row;
        }

        protected String _fmtString = null;
        public String FormatString
        {
            get
            {
                if (_fmtString == null)
                {
                    String format = "";
                    int i = 0;
                    foreach (int len in colLength)
                    {
                        format += String.Format("{{{0},-{1}}}{2}", i++, len, Separator);
                    }
                    format += "\r\n";
                    _fmtString = format;
                }
                return _fmtString;
            }
        }

        public String Output()
        {
            StringBuilder sb = new StringBuilder();
            foreach (TextRow row in rows)
            {
                row.Output(sb);
            }
            return sb.ToString();
        }

        #region IEnumerable Members

        public IEnumerator<ITextRow> GetEnumerator()
        {
            return rows.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return rows.GetEnumerator();
        }

        #endregion
    }



    static void Main(String[] args)
    {
        TableBuilder tb = new TableBuilder();
        tb.AddRow("When", "ID", "Name");
        tb.AddRow("----", "--", "----");

        tb.AddRow(DateTime.Now, "1", "Name1");
        tb.AddRow(DateTime.Now, "1", "Name2");

        Console.Write(tb.Output());
        Console.WriteLine();

        // or

        StringBuilder sb = new StringBuilder();
        int i = 0;
        foreach (ITextRow tr in tb)
        {
            tr.Output(sb);
            if (i++ > 1) sb.AppendLine("more stuff per line");
        }
        Console.Write(sb.ToString());
    }
}

输出:

当ID名称
---------
2013/2/4下午8:37:44 1 Name1
2013/2/4下午8:37:44 1 Name2

当ID名称
---------
2013/2/4下午8:37:44 1 Name1
每行更多的东西
2013/2/4下午8:37:44 1 Name2
每行更多的东西

9

这是对先前答案的改进。它增加了对具有可变长度的值和具有可变单元格数量的行的支持。例如:

┌──────────┬─────────┬──────────────────────────┬────────────────┬─────┬───────┐
Identifier     Type               Description  CPU Credit UseHoursBalance
├──────────┼─────────┼──────────────────────────┼────────────────┼─────┼───────┘
 i-1234154 t2.small       This is an example.│         3263.75  360
├──────────┼─────────┼──────────────────────────┼────────────────┼─────┘
 i-1231412 t2.small  This is another example.│         3089.93
└──────────┴─────────┴──────────────────────────┴────────────────┘

这是代码:

public class ArrayPrinter
{
    const string TOP_LEFT_JOINT = "┌";
    const string TOP_RIGHT_JOINT = "┐";
    const string BOTTOM_LEFT_JOINT = "└";
    const string BOTTOM_RIGHT_JOINT = "┘";
    const string TOP_JOINT = "┬";
    const string BOTTOM_JOINT = "┴";
    const string LEFT_JOINT = "├";
    const string JOINT = "┼";
    const string RIGHT_JOINT = "┤";
    const char HORIZONTAL_LINE = '─';
    const char PADDING = ' ';
    const string VERTICAL_LINE = "│";

    private static int[] GetMaxCellWidths(List<string[]> table)
    {
        int maximumCells = 0;
        foreach (Array row in table)
        {
            if (row.Length > maximumCells)
                maximumCells = row.Length;
        }

        int[] maximumCellWidths = new int[maximumCells];
        for (int i = 0; i < maximumCellWidths.Length; i++)
            maximumCellWidths[i] = 0;

        foreach (Array row in table)
        {
            for (int i = 0; i < row.Length; i++)
            {
                if (row.GetValue(i).ToString().Length > maximumCellWidths[i])
                    maximumCellWidths[i] = row.GetValue(i).ToString().Length;
            }
        }

        return maximumCellWidths;
    }

    public static string GetDataInTableFormat(List<string[]> table)
    {
        StringBuilder formattedTable = new StringBuilder();
        Array nextRow = table.FirstOrDefault();
        Array previousRow = table.FirstOrDefault();

        if (table == null || nextRow == null)
            return String.Empty;

        // FIRST LINE:
        int[] maximumCellWidths = GetMaxCellWidths(table);
        for (int i = 0; i < nextRow.Length; i++)
        {
            if (i == 0 && i == nextRow.Length - 1)
                formattedTable.Append(String.Format("{0}{1}{2}", TOP_LEFT_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), TOP_RIGHT_JOINT));
            else if (i == 0)
                formattedTable.Append(String.Format("{0}{1}", TOP_LEFT_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
            else if (i == nextRow.Length - 1)
                formattedTable.AppendLine(String.Format("{0}{1}{2}", TOP_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), TOP_RIGHT_JOINT));
            else
                formattedTable.Append(String.Format("{0}{1}", TOP_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
        }

        int rowIndex = 0;
        int lastRowIndex = table.Count - 1;
        foreach (Array thisRow in table)
        {
            // LINE WITH VALUES:
            int cellIndex = 0;
            int lastCellIndex = thisRow.Length - 1;
            foreach (object thisCell in thisRow)
            {
                string thisValue = thisCell.ToString().PadLeft(maximumCellWidths[cellIndex], PADDING);

                if (cellIndex == 0 && cellIndex == lastCellIndex)
                    formattedTable.AppendLine(String.Format("{0}{1}{2}", VERTICAL_LINE, thisValue, VERTICAL_LINE));
                else if (cellIndex == 0)
                    formattedTable.Append(String.Format("{0}{1}", VERTICAL_LINE, thisValue));
                else if (cellIndex == lastCellIndex)
                    formattedTable.AppendLine(String.Format("{0}{1}{2}", VERTICAL_LINE, thisValue, VERTICAL_LINE));
                else
                    formattedTable.Append(String.Format("{0}{1}", VERTICAL_LINE, thisValue));

                cellIndex++;
            }

            previousRow = thisRow;

            // SEPARATING LINE:
            if (rowIndex != lastRowIndex)
            {
                nextRow = table[rowIndex + 1];

                int maximumCells = Math.Max(previousRow.Length, nextRow.Length);
                for (int i = 0; i < maximumCells; i++)
                {
                    if (i == 0 && i == maximumCells - 1)
                    {
                        formattedTable.Append(String.Format("{0}{1}{2}", LEFT_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), RIGHT_JOINT));
                    }
                    else if (i == 0)
                    {
                        formattedTable.Append(String.Format("{0}{1}", LEFT_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
                    }
                    else if (i == maximumCells - 1)
                    {
                        if (i > previousRow.Length)
                            formattedTable.AppendLine(String.Format("{0}{1}{2}", TOP_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), TOP_RIGHT_JOINT));
                        else if (i > nextRow.Length)
                            formattedTable.AppendLine(String.Format("{0}{1}{2}", BOTTOM_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), BOTTOM_RIGHT_JOINT));
                        else if (i > previousRow.Length - 1)
                            formattedTable.AppendLine(String.Format("{0}{1}{2}", JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), TOP_RIGHT_JOINT));
                        else if (i > nextRow.Length - 1)
                            formattedTable.AppendLine(String.Format("{0}{1}{2}", JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), BOTTOM_RIGHT_JOINT));
                        else
                            formattedTable.AppendLine(String.Format("{0}{1}{2}", JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), RIGHT_JOINT));
                    }
                    else
                    {
                        if (i > previousRow.Length)
                            formattedTable.Append(String.Format("{0}{1}", TOP_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
                        else if (i > nextRow.Length)
                            formattedTable.Append(String.Format("{0}{1}", BOTTOM_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
                        else
                            formattedTable.Append(String.Format("{0}{1}", JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
                    }
                }
            }

            rowIndex++;
        }

        // LAST LINE:
        for (int i = 0; i < previousRow.Length; i++)
        {
            if (i == 0)
                formattedTable.Append(String.Format("{0}{1}", BOTTOM_LEFT_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
            else if (i == previousRow.Length - 1)
                formattedTable.AppendLine(String.Format("{0}{1}{2}", BOTTOM_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE), BOTTOM_RIGHT_JOINT));
            else
                formattedTable.Append(String.Format("{0}{1}", BOTTOM_JOINT, String.Empty.PadLeft(maximumCellWidths[i], HORIZONTAL_LINE)));
        }

        return formattedTable.ToString();
    }
}

1
在经过一些测试后,您的代码无法很好地处理单个列。
巴西残奥会2015年

6

我在GitHub上有一个项目可以使用

https://github.com/BrunoVT1992/ConsoleTable

您可以像这样使用它:

var table = new Table();

table.SetHeaders("Name", "Date", "Number");

for (int i = 0; i <= 10; i++)
{
    if (i % 2 == 0)
        table.AddRow($"name {i}", DateTime.Now.AddDays(-i).ToLongDateString(), i.ToString());
    else
        table.AddRow($"long name {i}", DateTime.Now.AddDays(-i).ToLongDateString(), (i * 5000).ToString());
}

Console.WriteLine(table.ToString());

它将给出以下结果:

在此处输入图片说明


1
除此以外,还存在其他软件包,但是我更喜欢您的软件包,因为对于线结,带有特殊字符的输出更好。该API很简单,并且不需要承担太多(例如要求标头),这很好。您是否考虑在Nuget上添加它?不管怎样,谢谢!
表格

尼斯@BrunoVT。谢谢!我也想要一个Nuget。
Gerhard Schreurs

5

使用MarkDownLog库(您可以在NuGet上找到它)

您可以简单地将扩展名ToMarkdownTable()用于任何集合,它会为您完成所有格式化。

 Console.WriteLine(
    yourCollection.Select(s => new
                    {
                        column1 = s.col1,
                        column2 = s.col2,
                        column3 = s.col3,
                        StaticColumn = "X"
                    })
                    .ToMarkdownTable());

输出看起来像这样:

Column1  | Column2   | Column3   | StaticColumn   
--------:| ---------:| ---------:| --------------
         |           |           | X              

5

如果这可以帮助某人,这是我为满足需要而编写的一个简单类。您可以轻松更改它以满足您的需求。

using System.Collections.Generic;
using System.Linq;

namespace Utilities
{
    public class TablePrinter
    {
        private readonly string[] titles;
        private readonly List<int> lengths;
        private readonly List<string[]> rows = new List<string[]>();

        public TablePrinter(params string[] titles)
        {
            this.titles = titles;
            lengths = titles.Select(t => t.Length).ToList();
        }

        public void AddRow(params object[] row)
        {
            if (row.Length != titles.Length)
            {
                throw new System.Exception($"Added row length [{row.Length}] is not equal to title row length [{titles.Length}]");
            }
            rows.Add(row.Select(o => o.ToString()).ToArray());
            for (int i = 0; i < titles.Length; i++)
            {
                if (rows.Last()[i].Length > lengths[i])
                {
                    lengths[i] = rows.Last()[i].Length;
                }
            }
        }

        public void Print()
        {
            lengths.ForEach(l => System.Console.Write("+-" + new string('-', l) + '-'));
            System.Console.WriteLine("+");

            string line = "";
            for (int i = 0; i < titles.Length; i++)
            {
                line += "| " + titles[i].PadRight(lengths[i]) + ' ';
            }
            System.Console.WriteLine(line + "|");

            lengths.ForEach(l => System.Console.Write("+-" + new string('-', l) + '-'));
            System.Console.WriteLine("+");

            foreach (var row in rows)
            {
                line = "";
                for (int i = 0; i < row.Length; i++)
                {
                    if (int.TryParse(row[i], out int n))
                    {
                        line += "| " + row[i].PadLeft(lengths[i]) + ' ';  // numbers are padded to the left
                    }
                    else
                    {
                        line += "| " + row[i].PadRight(lengths[i]) + ' ';
                    }
                }
                System.Console.WriteLine(line + "|");
            }

            lengths.ForEach(l => System.Console.Write("+-" + new string('-', l) + '-'));
            System.Console.WriteLine("+");
        }
    }
}

用法示例:

var t = new TablePrinter("id", "Column A", "Column B");
t.AddRow(1, "Val A1", "Val B1");
t.AddRow(2, "Val A2", "Val B2");
t.AddRow(100, "Val A100", "Val B100");
t.Print();

输出:

+-----+----------+----------+
| id  | Column A | Column B |
+-----+----------+----------+
|   1 | Val A1   | Val B1   |
|   2 | Val A2   | Val B2   |
| 100 | Val A100 | Val B100 |
+-----+----------+----------+

4
public static void ToPrintConsole(this DataTable dataTable)
    {
        // Print top line
        Console.WriteLine(new string('-', 75));

        // Print col headers
        var colHeaders = dataTable.Columns.Cast<DataColumn>().Select(arg => arg.ColumnName);
        foreach (String s in colHeaders)
        {
            Console.Write("| {0,-20}", s);
        }
        Console.WriteLine();

        // Print line below col headers
        Console.WriteLine(new string('-', 75));

        // Print rows
        foreach (DataRow row in dataTable.Rows)
        {
            foreach (Object o in row.ItemArray)
            {
                Console.Write("| {0,-20}", o.ToString());
            }
            Console.WriteLine();
        }

        // Print bottom line
        Console.WriteLine(new string('-', 75));
    }

-6

在VisualBasic.net中更容易!

如果希望用户能够手动将数据输入到表中:

Console.Write("Enter Data For Column 1: ")
    Dim Data1 As String = Console.ReadLine
    Console.Write("Enter Data For Column 2: ")
    Dim Data2 As String = Console.ReadLine

    Console.WriteLine("{0,-20} {1,-10} {2,-10}", "{Data Type}", "{Column 1}", "{Column 2}")
    Console.WriteLine("{0,-20} {1,-10} {2,-10}", "Data Entered:", Data1, Data2)

    Console.WriteLine("ENTER To Exit: ")
    Console.ReadLine()

它看起来应该像这样:

它应该看起来像这样(单击我)。


我认为这很有用,因为我想要的只是在C#中进行简单的列对齐,而Console.WriteLine()在这两种语言中的工作方式相似。
pwrgreg007
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.