วิธีการ: วิธีที่ดีที่สุดในการวาดตารางในแอปคอนโซล (C #)


104

ฉันมีคำถามที่น่าสนใจ ลองนึกภาพว่าฉันมีข้อมูลจำนวนมากเปลี่ยนแปลงในช่วงเวลาที่รวดเร็วมาก ฉันต้องการแสดงข้อมูลนั้นเป็นตารางในแอปคอนโซล ฉ. ex:

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

วิธีทำให้สิ่งต่างๆรวดเร็วและจะแก้ไขความกว้างของคอลัมน์ได้อย่างไร? ฉันรู้วิธีทำใน java แต่ฉันทำไม่ได้ใน C #


6
จะเกิดอะไรขึ้นถ้าคุณให้โซลูชัน Java ของคุณเพื่อช่วยในการแปลเป็น C #? แต่ลองดูคลาส String ด้วย Length / PadLeft / PadRight / ...
Scoregraphic

คำตอบ:


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

137

ใช้String.Formatกับค่าการจัดตำแหน่ง

ตัวอย่างเช่น:

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

เพื่อสร้างแถวที่จัดรูปแบบหนึ่งแถว


จะไม่พิมพ์ค่าเดียวกันสี่ครั้ง?
Brian Rasmussen

1
ใช่คุณควรแก้ไข: String.Format ("| {0,10} | {1,10} | {2,10} | {3,10} |", arg0, arg1, arg2, arg3);
Lukas Šalkauskas

1
หากคุณไม่ต้องการขอบโต๊ะและมุมก็ใช้ได้ดี อย่าลืมใช้ PadRight (x) หรือ PadLeft (x) เพื่อช่วยเว้นระยะห่าง สำหรับสิ่งที่ฉันต้องการฉันแค่ต้องการสิ่งที่อ่านง่ายและหลังจากลบ | s แล้วสิ่งนี้ก็ได้เคล็ดลับ
CokoBWare

3
ใช้-สำหรับเนื้อหาจัดชิดซ้ายเช่น{0,-5}
Mehdi Dehghani

ฉันจะใช้สิ่งนี้เพื่อจัดแนวค่าที่อาร์กิวเมนต์หนึ่งล้นขอบของพื้นที่ว่างในแถวหรือมีอักขระบรรทัดใหม่ได้อย่างไร
user9811991

35

แก้ไข:ขอบคุณ @superlogical ตอนนี้คุณสามารถค้นหาและปรับปรุงโค้ดต่อไปนี้ใน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 อยู่ที่ไหน
superlogical

1
@superlogical ได้เพิ่มไว้ที่ด้านล่างของคำตอบ ขอบคุณที่แจ้งให้ทราบ
HuBeZa

1
ฉันชอบรูปแบบวิธีการขยายที่มีอยู่ในตัวเมื่อเทียบกับคำตอบอื่น ๆ ที่นี่ ขอบคุณ!
James Haug

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

มีไลบรารีโอเพนซอร์สหลายตัวที่อนุญาตให้จัดรูปแบบตารางคอนโซลตั้งแต่แบบง่าย (เช่นตัวอย่างโค้ดในคำตอบที่นี่) ไปจนถึงขั้นสูง

ConsoleTable

ตัดสินโดยสถิติ NuGet ห้องสมุดนิยมมากที่สุดสำหรับการจัดรูปแบบตารางเป็นConsoleTable ตารางถูกสร้างขึ้นเช่นนี้ (จากไฟล์ readme):

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

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. การเปลี่ยนแปลงพารามิเตอร์ชนิดจากไปStreamWriter TextWriter2. แทนที่PrintToConsoleรหัสด้วย: PrintToStream(arrValues, Console.Out);3. ??? 4. กำไร!
HuBeZa

16

ฉันต้องการคอลัมน์ที่มีความกว้างตัวแปรและฉันไม่ได้สนใจเป็นพิเศษเกี่ยวกับอักขระกล่อง นอกจากนี้ฉันต้องพิมพ์ข้อมูลเพิ่มเติมสำหรับแต่ละแถว

ดังนั้นในกรณีที่ใครต้องการสิ่งนั้นฉันจะช่วยคุณได้ไม่กี่นาที:

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 Name
---- - ----
4/4/2556 8:37:44 น. 1 ชื่อ 1
2/4/2556 8:37:44 น. 1 ชื่อ 2

เมื่อ ID Name
---- - ----
4/4/2556 8:37:44 น. 1 ชื่อ 1
ข้อมูลเพิ่มเติมต่อบรรทัด
2/4/2556 8:37:44 น. 1 ชื่อ 2
ข้อมูลเพิ่มเติมต่อบรรทัด

9

นี่เป็นการปรับปรุงคำตอบก่อนหน้านี้ เพิ่มการรองรับสำหรับค่าที่มีความยาวและแถวที่แตกต่างกันโดยมีจำนวนเซลล์ที่แตกต่างกัน ตัวอย่างเช่น:

┌──────────┬─────────┬──────────────────────────┬────────────────┬─────┬───────┐
│Identifier│     Type│               Description│  CPU Credit Use│Hours│Balance│
├──────────┼─────────┼──────────────────────────┼────────────────┼─────┼───────┘
│ i-1234154│ t2.small│       This is an example.│         3263.75360│
├──────────┼─────────┼──────────────────────────┼────────────────┼─────┘
│ 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
ตามที่ปรากฏหลังจากการทดสอบโค้ดของคุณไม่สามารถจัดการกรณีของคอลัมน์เดียวได้ดี
bazzilic

6

ในกรณีที่สิ่งนี้ช่วยใครสักคนนี่เป็นชั้นเรียนง่ายๆที่ฉันเขียนขึ้นเพื่อต้องการ คุณสามารถเปลี่ยนได้อย่างง่ายดายเพื่อให้เหมาะกับความต้องการของคุณ

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

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              

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

-7

ง่ายกว่าใน 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.