검색해도 맘에 드는 것이 없어서 직접 소스를 작성하였다.
에디터는 vscode or vs2022을 추천.
using System.Data;
internal static class DataTableEX
{
const string No = " №";
public static void Print(this DataTable table, bool rowNum = false) =>
Print(table, null, rowNum);
public static void Print(this DataTable table, int top, bool rowNum = false) =>
Print(table, top, null, rowNum);
public static void Print(this DataTable table, string[] col, bool rowNum = false) =>
Print(table, int.MaxValue, col, rowNum);
public static void Print(this DataTable table, int top, string[] col, bool rowNum = false)
{
if (rowNum == true)
{
table = table.Copy();
table.Columns.Add(No, typeof(int)).SetOrdinal(0);
Array.ForEach(table.AsEnumerable().Take(top).ToArray(), row =>
row[No] = table.Rows.IndexOf(row));
}
DataColumn[] columns = table.Columns.Cast<DataColumn>().ToArray();
if (col != null)
{
if (rowNum == true) col = col.Append(No).ToArray();
columns = columns.Where(c => col.Contains(c.ColumnName)).ToArray();
}
if (columns.Length == 0)
{
Console.WriteLine("NO columns");
return;
}
int RealLen(string str) => str.Select(x => (0xFF00 & x) == 0 ? 1 : 2).Sum();
int UnLen(string str) => str.Select(x => (0xFF00 & x) == 0 ? 0 : 1).Sum();
int getAlign(Type t) => t.ToString().ToLower() switch
{
var x when x.StartsWith("system.byte") => 1,
var x when x.StartsWith("system.sbyte") => 1,
var x when x.StartsWith("system.short") => 1,
var x when x.StartsWith("system.ushort") => 1,
var x when x.StartsWith("system.int") => 1,
var x when x.StartsWith("system.uint") => 1,
var x when x.StartsWith("system.long") => 1,
var x when x.StartsWith("system.ulong") => 1,
var x when x.StartsWith("system.float") => 1,
var x when x.StartsWith("system.double") => 1,
var x when x.StartsWith("system.decimal") => 1,
_ => -1
};
string Escape(string str) => String.Join("", str.Select(x => x switch
{
'\b' => "\\b",
'\f' => "\\f",
'\n' => "\\n",
'\r' => "\\r",
'\t' => "\\t",
_ => x.ToString()
}));
var colNames = columns.Select(x => Escape(x.ColumnName));
var colsLen = (from c in columns
select (t: table.AsEnumerable(), l: RealLen(Escape(c.ColumnName)), c) into o
select o.t.Count() == 0
? o.l : o.t.Take(top).Max(x => Math.Max(o.l, RealLen("" + x.Field<object>(o.c))))
).ToArray();
var align = columns.Select(x => getAlign(x.DataType)).Select((al, i) =>
new Func<string, string>(str =>
String.Format($"{{0,{al * (colsLen[i] - UnLen("" + str))}}}", str))).ToArray();
string PadBoth(string source, int length)
{
int spaces = length - RealLen(source);
return new String(' ', spaces / 2) + source + new string(' ', spaces - spaces / 2);
}
Action<string> WL = Console.WriteLine;
var preBg = Console.BackgroundColor;
Console.BackgroundColor = ConsoleColor.DarkCyan;
WL(PadBoth(table.TableName, (colsLen.Sum() + colNames.Count() * 3 + 2)));
Console.BackgroundColor = preBg;
WL($"┌{ String.Join('┬', colsLen.Select(x => new String('-', x + 2))) }┐");
WL($"│ {String.Join(" │ ", colNames.Select((x, i) => x.PadRight(colsLen[i] - UnLen(x))))} │");
WL($"├{ String.Join('┼', colsLen.Select(x => new String('-', x + 2))) }┤");
Array.ForEach(table.AsEnumerable().Take(top).ToArray(), r =>
WL($"│ {String.Join(" │ ", columns.Select((x, i) => align[i](Escape("" + r[x])))) } │"));
WL($"└{ String.Join('┴', colsLen.Select(x => new String('-', x + 2))) }┘");
Console.WriteLine();
}
}
using System.Data;
using System.Text;
Console.OutputEncoding = System.Text.Encoding.UTF8;
var dt = new DataTable("안농");
dt.Columns.Add("숫자", typeof(double)).AllowDBNull = false;
dt.Columns.Add("가나\n다", typeof(int));
dt.Columns.Add("문자");
dt.Columns.Add("날짜", typeof(DateTime));
dt.Rows.Add(new object[] { 11, 2, "맏을 수 없을만큼", DateTime.Now });
dt.Rows.Add(new object[] { 22, 41, "돌아버린 사람인지...", DateTime.Now });
dt.Rows.Add(new object[] { 3, 4, "저는 실패할래야", DateTime.Now });
dt.Rows.Add(new object[] { 4, 2, "실패할 수가 없습니다.", DateTime.Now });
dt.Rows.Add(new object[] { 5, 41, "여러분이 계신데", DateTime.Now });
dt.Rows.Add(new object[] { 446, 4, "제가 어떻게 실패할 수 있습니까", DateTime.Now });
dt.Print(5, new[] { "숫자", "문자", "날짜"}, true );
콘솔 창의 폰트는 돋움체나 굴림체 같은 체(體)로 끝나는.. 크기가 고정적인 폰트를 사용해야 포맷이 깨지지 않는다.