Cách thực hiện: Cách tốt nhất để vẽ bảng trong ứng dụng bảng điều khiển (C #)


103

Tôi có một câu hỏi thú vị. Hãy tưởng tượng tôi có rất nhiều dữ liệu thay đổi trong khoảng thời gian rất nhanh. Tôi muốn hiển thị dữ liệu đó dưới dạng bảng trong ứng dụng bảng điều khiển. f.ex:

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

Làm thế nào để giữ cho mọi thứ nhanh chóng và làm thế nào để sửa chữa độ rộng của cột? Tôi biết cách thực hiện điều đó trong java, nhưng tôi không biết cách thực hiện trong C #.


6
điều gì sẽ xảy ra nếu bạn cung cấp giải pháp Java của mình để một người có thể giúp bạn dịch sang C #? Nhưng hãy xem lớp String với Length / PadLeft / PadRight / ...
Scoregraphic

Câu trả lời:


66

Bạn có thể làm điều gì đó như sau:

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

Sử dụng String.Format với các giá trị căn chỉnh.

Ví dụ:

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

Để tạo một hàng được định dạng.


Điều đó sẽ không in cùng một giá trị bốn lần?
Brian Rasmussen

1
Vâng, bạn nên sửa: String.Format ("| {0,10} | {1,10} | {2,10} | {3,10} |", arg0, arg1, arg2, arg3);
Lukas Šalkauskas

1
Nếu bạn không cần cạnh bàn và góc, điều này hoạt động tốt. Đừng quên sử dụng PadRight (x) hoặc PadLeft (x) để giúp bạn điều chỉnh khoảng cách. Đối với những gì tôi cần, tôi chỉ cần một cái gì đó dễ đọc, và sau khi xóa | s, điều này đã thành công.
CokoBWare

3
Sử dụng -cho nội dung căn trái, ví dụ:{0,-5}
Mehdi Dehghani

Làm cách nào để sử dụng điều này để căn chỉnh các giá trị trong đó một đối số làm tràn mép của khoảng trống có sẵn trong một hàng hoặc chứa một ký tự dòng mới?
user9811991

36

Chỉnh sửa: nhờ @superlogical, giờ đây bạn có thể tìm và cải thiện đoạn mã sau trong github !


Tôi đã viết lớp học này dựa trên một số ý tưởng ở đây. Chiều rộng cột là tối ưu, nó có thể xử lý các mảng đối tượng với API đơn giản này:

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   |
  */
}

Đây là lớp học:

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

Biên tập: Tôi đã thêm một cải tiến nhỏ - nếu bạn muốn tiêu đề cột là tên thuộc tính, hãy thêm phương thức sau vào TableParser(lưu ý rằng nó sẽ chậm hơn một chút do phản chiếu):

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

Phương thức GetProperty ở đâu?
siêu học

1
@superlogical, nó đã được thêm ở cuối câu trả lời. Cảm ơn đã chú ý.
HuBeZa

1
Tôi thích kiểu phương pháp mở rộng khép kín so với các câu trả lời khác ở đây. Cảm ơn!
James Haug

1
tuyệt vời tôi sử dụng nó và nó hoạt động hoàn hảo, Chỉ cần thay đổi nội dung này arrValues[rowIndex, colIndex] = valueSelectors[colIndex].Invoke(values[rowIndex - 1]).ToString();đến var val = valueSelectors[colIndex].Invoke(values[rowIndex - 1]); arrValues[rowIndex, colIndex] = val == null ? "null" : val.ToString();theo cách này nó cho thấy null.
H_H

Điều này hoạt động tốt, cho đến khi bạn bắt đầu làm việc với các ký tự không phải unicode. Sau đó, chiều rộng sẽ rối tung lên.
Thom

30

Có một số thư viện mã nguồn mở cho phép định dạng bảng điều khiển, từ đơn giản (như các mẫu mã trong câu trả lời ở đây) đến nâng cao hơn.

ConsoleTable

Đánh giá theo thống kê NuGet, thư viện phổ biến nhất để định dạng bảng là ConsoleTable . Các bảng được xây dựng như thế này (từ tệp readme):

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

Các bảng có thể được định dạng bằng một trong các kiểu được xác định trước. Nó sẽ trông như thế này:

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

Thư viện này yêu cầu các ô một dòng không có định dạng.

Có một số thư viện dựa trên ConsoleTable với các bộ tính năng mở rộng hơn một chút, như nhiều kiểu dòng hơn.

CsConsoleFormat

Nếu bạn cần định dạng phức tạp hơn, bạn có thể sử dụng CsConsoleFormat . † Đây là bảng được tạo từ danh sách quy trình (từ một dự án mẫu):

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")),
        })
    )

Kết quả cuối cùng sẽ như thế này:

Nó hỗ trợ bất kỳ loại dòng bảng nào (một số dòng được bao gồm và có thể tùy chỉnh), các ô nhiều dòng với sự bao bọc từ, màu sắc, các cột phát triển dựa trên nội dung hoặc tỷ lệ phần trăm, căn chỉnh văn bản, v.v.

† CsConsoleFormat do tôi phát triển.


Tôi biết nó khá cũ, nhưng làm cách nào để tạo một ô nhiều dòng? Tôi không tìm thấy nó ở bất cứ đâu
Morta1

1
@ Morta1 Cả câu trả lời này và thư viện CsConsoleFormat đều không cũ. Nếu bạn đang sử dụng CsConsoleFormat, bạn có thể chèn các ngắt dòng theo cách thủ công ("\ n" trong các chuỗi bên trong ô) và nó sẽ được xử lý chính xác hoặc để thư viện tự động quấn văn bản (đó là mặc định, vì vậy bạn không thêm TextWrap = TextWrapping.NoWrapkhông giống như trong ví dụ trên).
Athari

Cảm ơn câu trả lời nhanh chóng, có cách nào để làm điều đó với ConsoleTable không?
Morta1

1
@ Morta1 Không. Tôi không nghĩ rằng bất kỳ thư viện đơn giản nào hỗ trợ điều đó.
Athari

1
@HarveyDarvey Đại loại là new Cell(text) { Color = text == "true" ? Green : Red }. Nếu bạn có nhiều bảng có quy tắc định dạng tương tự, bạn có thể đặt mã đó vào một số hàm, có thể là cho ô, hàng hoặc toàn bộ bảng.
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
Để linh hoạt hơn và sử dụng lại mã tốt hơn: 1. thay đổi kiểu tham số từ StreamWriterthành TextWriter. 2. Thay thế PrintToConsolemã bằng: PrintToStream(arrValues, Console.Out);3. ??? 4. LỢI NHUẬN!
HuBeZa

15

Tôi muốn các cột có chiều rộng thay đổi và tôi không đặc biệt quan tâm đến các ký tự hộp. Ngoài ra, tôi cần in thêm một số thông tin cho mỗi hàng.

Vì vậy, trong trường hợp có ai khác cần điều đó, tôi sẽ tiết kiệm cho bạn vài phút:

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

Đầu ra:

Khi tên ID
---- - ----
2/4/2013 8:37:44 CH 1 Tên1
2/4/2013 8:37:44 CH 1 Tên 2

Khi tên ID
---- - ----
2/4/2013 8:37:44 CH 1 Tên1
nhiều thứ hơn trên mỗi dòng
2/4/2013 8:37:44 CH 1 Tên 2
nhiều thứ hơn trên mỗi dòng

9

Đây là một cải tiến cho câu trả lời trước đó. Nó bổ sung hỗ trợ cho các giá trị có độ dài và hàng khác nhau với số lượng ô khác nhau. Ví dụ:

┌──────────┬─────────┬──────────────────────────┬────────────────┬─────┬───────┐
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
└──────────┴─────────┴──────────────────────────┴────────────────┘

Đây là mã:

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
Vì nó xuất hiện sau một số thử nghiệm, mã của bạn không xử lý tốt trường hợp của một cột duy nhất.
bazzilic

6

Tôi có một dự án trên GitHub mà bạn có thể sử dụng

https://github.com/BrunoVT1992/ConsoleTable

Bạn có thể sử dụng nó như thế này:

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

Nó sẽ cho kết quả này:

nhập mô tả hình ảnh ở đây


1
Có những gói khác không phải của bạn cho điều này, nhưng tôi thích gói của bạn hơn vì đầu ra đẹp hơn với các ký tự đặc biệt cho các đường nối. API đơn giản và không giả định quá nhiều (chẳng hạn như yêu cầu tiêu đề), điều này rất hay. Bạn có cân nhắc thêm nó trên Nuget không? Dù sao cũng cảm ơn!
Biểu mẫu

Đẹp @BrunoVT. Cảm ơn! Và tôi cũng muốn một Nuget.
Gerhard Schreurs

5

Sử dụng thư viện MarkDownLog (bạn có thể tìm thấy nó trên NuGet)

bạn chỉ cần sử dụng phần mở rộng ToMarkdownTable () cho bất kỳ bộ sưu tập nào, nó thực hiện tất cả các định dạng cho bạn.

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

Đầu ra trông giống như sau:

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

5

Trong trường hợp nếu điều này giúp ích cho ai đó, đây là một lớp học đơn giản tôi đã viết cho nhu cầu của tôi. Bạn có thể thay đổi dễ dàng để phù hợp với nhu cầu của mình.

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("+");
        }
    }
}

Sử dụng mẫu:

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

Đầu ra:

+-----+----------+----------+
| 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

Nó dễ dàng hơn trong VisualBasic.net!

Nếu bạn muốn người dùng có thể nhập dữ liệu vào bảng theo cách thủ công:

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

Nó sẽ giống như thế này:

Nó sẽ giống như thế này (Nhấp vào Tôi).


Tôi nghĩ rằng điều này hữu ích vì tất cả những gì tôi muốn là căn chỉnh cột đơn giản trong C # và Console.WriteLine () hoạt động tương tự trong cả hai ngôn ngữ.
pwrgreg007
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.