興味深い質問があります。非常に速い間隔で多くのデータが変化していると想像してください。そのデータをコンソールアプリのテーブルとして表示したい。 f.ex:
-------------------------------------------------------------------------
| Column 1 | Column 2 | Column 3 | Column 4 |
-------------------------------------------------------------------------
| | | | |
| | | | |
| | | | |
-------------------------------------------------------------------------
物事を高速に保つ方法と列幅を修正する方法は? Javaでそれを行う方法は知っていますが、C#でそれを行う方法はわかりません。
次のようなことができます:
static int tableWidth = 77;
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);
}
}
String.Format をアライメント値とともに使用します。
例えば:
String.Format("|{0,5}|{1,5}|{2,5}|{3,5}|", arg0, arg1, arg2, arg3);
1つのフォーマットされた行を作成します。
編集:@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;
}
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();
}
}
コンソールテーブルのフォーマットを可能にするいくつかのオープンソースライブラリがあり、単純なもの(ここの回答のコードサンプルなど)からより高度なものまであります。
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 。†を使用できます。ここに、プロセスリスト(サンプルプロジェクト)から生成されたテーブルを示します。
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は私が開発しました。
可変幅の列が必要でしたが、ボックスの文字は特に気にしませんでした。また、各行の追加情報を印刷する必要がありました。
したがって、他の誰かがそれを必要とする場合に備えて、数分節約します。
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名 --------- 2/4/2013 8:37:44 PM 1 Name1 2013年2月4日8:37:44 PM 1 Name2 ID名 ---- ----- 2013年2月4日8:37:44 PM 1 Name1 行ごとのその他のもの 2/4/2013 8:37:44 PM 1 Name2 行ごとのその他のもの
これは、以前の回答の改善です。さまざまな長さの値とさまざまなセル数の行のサポートを追加します。例えば:
┌──────────┬─────────┬──────────────────────────┬────────────────┬─────┬───────┐
│Identifier│ Type│ Description│ CPU Credit Use│Hours│Balance│
├──────────┼─────────┼──────────────────────────┼────────────────┼─────┼───────┘
│ 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();
}
}
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
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));
}
これが誰かを助ける場合、これは私が私のニーズのために書いた簡単なクラスです。ニーズに合わせて簡単に変更できます。
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 |
+-----+----------+----------+
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());
次の結果が得られます。