C# Tips: Draw a data table in console
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace datatable
{
public class ConsoleTable
{
/// <summary>
/// This will hold the header of the table.
/// </summary>
private string[] header; /// <summary>
/// This will hold the rows (lines) in the table, not including the
/// header. I'm using a List of lists because it's easier to deal with...
/// </summary>
private List<List<string>> rows; /// <summary>
/// This is the default element (character/string) that will be put
/// in the table when user adds invalid data, example:
/// ConsoleTable ct = new ConsoleTable();
/// ct.AddRow(new List<string> { null, "bla", "bla" });
/// That null will be replaced with "DefaultElement", also, empty
/// strings will be replaced with this value.
/// </summary>
private const string DefaultElement = "X"; public enum AlignText
{
ALIGN_RIGHT,
ALIGN_LEFT,
} public ConsoleTable()
{
header = null;
rows = new List<List<string>>();
TextAlignment = AlignText.ALIGN_LEFT;
} /// <summary>
/// Set text alignment in table cells, either RIGHT or LEFT.
/// </summary>
public AlignText TextAlignment
{
get;
set;
} public void SetHeaders(string[] h)
{
header = h;
} public void AddRow(List<string> row)
{
rows.Add(row);
} private void AppendLine(StringBuilder hsb, int length)
{
// " " length is 1
// "\r\n" length is 2
// +1 length because I want the output to be prettier
// Hence the length - 4 ...
hsb.Append(" ");
hsb.Append(new string('-', length - ));
hsb.Append("\r\n");
} /// <summary>
/// This function returns the maximum possible length of an
/// individual row (line). Of course that if we use table header,
/// the maximum length of an individual row should equal the
/// length of the header.
/// </summary>
private int GetMaxRowLength()
{
if (header != null)
return header.Length;
else
{
int maxlen = rows[].Count;
for (int i = ; i < rows.Count; i++)
if (rows[i].Count > maxlen)
maxlen = rows[i].Count; return maxlen;
}
} private void PutDefaultElementAndRemoveExtra()
{
int maxlen = GetMaxRowLength(); for (int i = ; i < rows.Count; i++)
{
// If we find a line that is smaller than the biggest line,
// we'll add DefaultElement at the end of that line. In the end
// the line will be as big as the biggest line.
if (rows[i].Count < maxlen)
{
int loops = maxlen - rows[i].Count;
for (int k = ; k < loops; k++)
rows[i].Add(DefaultElement);
}
else if (rows[i].Count > maxlen)
{
// This will apply only when header != null, and we try to
// add a line bigger than the header line. Remove the elements
// of the line, from right to left, until the line is equal
// with the header line.
rows[i].RemoveRange(maxlen, rows[i].Count - maxlen);
} // Find bad data, loop through all table elements.
for (int j = ; j < rows[i].Count; j++)
{
if (rows[i][j] == null)
rows[i][j] = DefaultElement;
else if (rows[i][j] == "")
rows[i][j] = DefaultElement;
}
}
} /// <summary>
/// This function will return an array of integers, an element at
/// position 'i' will return the maximum length from column 'i'
/// of the table (if we look at the table as a matrix).
/// </summary>
private int[] GetWidths()
{
int[] widths = null;
if (header != null)
{
// Initially we assume that the maximum length from column 'i'
// is exactly the length of the header from column 'i'.
widths = new int[header.Length];
for (int i = ; i < header.Length; i++)
widths[i] = header[i].ToString().Length;
}
else
{
int count = GetMaxRowLength();
widths = new int[count];
for (int i = ; i < count; i++)
widths[i] = -;
} foreach (List<string> s in rows)
{
for (int i = ; i < s.Count; i++)
{
s[i] = s[i].Trim();
if (s[i].Length > widths[i])
widths[i] = s[i].Length;
}
} return widths;
} /// <summary>
/// Returns a valid format that is to be passed to AppendFormat
/// member function of StringBuilder.
/// General form: "|{i, +/-widths[i]}|", where 0 <= i <= widths.Length - 1
/// and widths[i] represents the maximum width from column 'i'.
/// </summary>
/// <param name="widths">The array of widths presented above.</param>
private string BuildRowFormat(int[] widths)
{
string rowFormat = String.Empty;
for (int i = ; i < widths.Length; i++)
{
if (TextAlignment == AlignText.ALIGN_LEFT)
rowFormat += "| {" + i.ToString() + ",-" + (widths[i]) + "} ";
else
rowFormat += "| {" + i.ToString() + "," + (widths[i]) + "} ";
} rowFormat = rowFormat.Insert(rowFormat.Length, "|\r\n");
return rowFormat;
} /// <summary>
/// Prints the table, main function.
/// </summary>
public void PrintTable()
{
if (rows.Count == )
{
Console.WriteLine("Can't create a table without any rows.");
return;
}
PutDefaultElementAndRemoveExtra(); int[] widths = GetWidths();
string rowFormat = BuildRowFormat(widths); // I'm using a temporary string builder to find the total width
// of the table, and increase BufferWidth of Console if necessary.
StringBuilder toFindLen = new StringBuilder();
toFindLen.AppendFormat(rowFormat, (header == null ? rows[].ToArray() : header));
int length = toFindLen.Length;
if (Console.BufferWidth < length)
Console.BufferWidth = length; // Print the first row, or header (if it exist), you can see that AppendLine
// is called before/after every AppendFormat.
StringBuilder hsb = new StringBuilder();
AppendLine(hsb, length);
hsb.AppendFormat(rowFormat, (header == null ? rows[].ToArray() : header));
AppendLine(hsb, length); // If header does't exist, we start from 1 because the first row
// was already printed above.
int idx = ;
if (header == null)
idx = ;
for (int i = idx; i < rows.Count; i++)
{
hsb.AppendFormat(rowFormat, rows[i].ToArray());
AppendLine(hsb, length);
} Console.WriteLine(hsb.ToString());
} static void Main(string[] args)
{
// Some test table, with header, and 3 lines by 3 columns.
ConsoleTable ct = new ConsoleTable();
ct.TextAlignment = ConsoleTable.AlignText.ALIGN_RIGHT;
ct.SetHeaders(new string[] { "ID", "Name", "City" });
ct.AddRow(new List<string> { "", "John", "New York" });
ct.AddRow(new List<string> { "", "Mark", "Washington" });
ct.AddRow(new List<string> { "", "Alice", "Chicago" });
ct.PrintTable();
}
}
}
C# Tips: Draw a data table in console的更多相关文章
- [Javascript] Logging Pretty-Printing Tabular Data to the Console
Learn how to use console.table to render arrays and objects in a tabular format for easy scanning ov ...
- data.table包
data.table 1.生成一个data.table对象 生成一个data.table对象,记为DT. library(data.table) :],V3=round(rnorm(),),V4=:) ...
- R之data.table -melt/dcast(数据合并和拆分)
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 30.0px "Helvetica Neue"; color: #323333 } p. ...
- R之data.table速查手册
R语言data.table速查手册 介绍 R中的data.table包提供了一个data.frame的高级版本,让你的程序做数据整型的运算速度大大的增加.data.table已经在金融,基因工程学等领 ...
- 两种Data Table参数化设置的区别
首先介绍Data Table的语法: 1.DataTable.value(ParameterID, SheetID) 2.DataTable(ParameterID, SheetID) 以上2种方法的 ...
- R语言数据分析利器data.table包 —— 数据框结构处理精讲
R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代码简洁,只要一行命令就可以完成诸多任务,另一方面是处理 ...
- R语言基因组数据分析可能会用到的data.table函数整理
R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代码简洁,只要一行命令就可以完成诸多任务,另一方面是处理快,内部 ...
- 将基因组数据分类并写出文件,python,awk,R data.table速度PK
由于基因组数据过大,想进一步用R语言处理担心系统内存不够,因此想着将文件按染色体拆分,发现python,awk,R 语言都能够非常简单快捷的实现,那么速度是否有差距呢,因此在跑几个50G的大文件之前, ...
- data.table包简介
data.table包主要特色是:设置keys.快速分组和滚得时序的快速合并.data.table主要通过二元检索法大大提高数据操作的效率,同时它也兼容适用于data.frame的向量检索法. req ...
随机推荐
- Android Studio Push rejected: Push to origin/Alpha1.0 was rejected
android studio git 右键项目, git pull 刷新选择Alpha1.0同步后,再commit and push
- Apache、Tomcat、JBoss、Weblogic四个常用的JavaEE服务器
Apache全球应用最广泛的http服务器,免费,出自apache基金组织 Tomcat应用也算非常广泛的web 服务器,支持部分j2ee,免费,出自 apache基金组织 JBoss开源的应用服务器 ...
- C# 操作数据库就的那点代码
操作数据库的那点代码,别在费劲每个数据库都写一遍SQLHelper,SQLiteHelper,OleDbHelper,了,这里都有了. 接口不发了,自己抽取定义就行了. public abstract ...
- java搭建finagle(2)
上篇文章是1年前写的惭愧惭愧,今天一个同事问我要demo然后看了下文章 好吧主要的代码 没写出来,今天补充下 上篇地址:http://www.cnblogs.com/rufus-hua/p/41592 ...
- Ajax获得站点文件内容实例
一个简单的Ajax实例:选择一部著作,会通过 Ajax 实时获得相关的名字. 把4个html文件放到 web站点 的同一个文件下. index.html <html> <head&g ...
- windows 32位系统中进程最大可用内存空间为3GB (转)
http://msdn.microsoft.com/zh-cn/library/ms189334.aspx 进程地址空间 所有 32 位应用程序都有 4 GB 的进程地址空间(32 位地址最多可以映射 ...
- Swift编程语言学习4.3—— 控制语句
控制传递语句(Control Transfer Statements) 控制转移语句改变你代码的运行顺序,通过它你能够实现代码的跳转.Swift有四种控制转移语句. continue break fa ...
- 谷歌技术"三宝"之MapReduce
江湖传说永流传:谷歌技术有"三宝",GFS.MapReduce和大表(BigTable)! 谷歌在03到06年间连续发表了三篇非常有影响力的文章,各自是03年SOSP的GFS,04 ...
- grep经常使用使用方法
grep简单介绍 正如linux的man文件里所描写叙述的那样: grep searches the named input FILEs (or standard input if no files ...
- CF B. Kolya and Tandem Repeat
Kolya got string s for his birthday, the string consists of small English letters. He immediately ad ...