C# Tips: Draw a data table in console

  1 using System;

  2 using System.Collections.Generic;

  3 using System.Linq;

  4 using System.Text;

  5 using System.Threading.Tasks;

  6 

  7     namespace datatable

  8     {

  9         public class ConsoleTable

 10         {

 11             /// <summary>  

 12             /// This will hold the header of the table.  

 13             /// </summary>  

 14             private string[] header;

 15 

 16             /// <summary>  

 17             /// This will hold the rows (lines) in the table, not including the  

 18             /// header. I'm using a List of lists because it's easier to deal with...  

 19             /// </summary>  

 20             private List<List<string>> rows;

 21 

 22             /// <summary>  

 23             /// This is the default element (character/string) that will be put  

 24             /// in the table when user adds invalid data, example:  

 25             ///     ConsoleTable ct = new ConsoleTable();  

 26             ///     ct.AddRow(new List<string> { null, "bla", "bla" });  

 27             /// That null will be replaced with "DefaultElement", also, empty  

 28             /// strings will be replaced with this value.  

 29             /// </summary>  

 30             private const string DefaultElement = "X";

 31 

 32             public enum AlignText

 33             {

 34                 ALIGN_RIGHT,

 35                 ALIGN_LEFT,

 36             }

 37 

 38             public ConsoleTable()

 39             {

 40                 header = null;

 41                 rows = new List<List<string>>();

 42                 TextAlignment = AlignText.ALIGN_LEFT;

 43             }

 44 

 45             /// <summary>  

 46             /// Set text alignment in table cells, either RIGHT or LEFT.  

 47             /// </summary>  

 48             public AlignText TextAlignment

 49             {

 50                 get;

 51                 set;

 52             }

 53 

 54             public void SetHeaders(string[] h)

 55             {

 56                 header = h;

 57             }

 58 

 59             public void AddRow(List<string> row)

 60             {

 61                 rows.Add(row);

 62             }

 63 

 64             private void AppendLine(StringBuilder hsb, int length)

 65             {

 66                 // " " length is 1  

 67                 // "\r\n" length is 2  

 68                 // +1 length because I want the output to be prettier  

 69                 // Hence the length - 4 ...  

 70                 hsb.Append(" ");

 71                 hsb.Append(new string('-', length - 4));

 72                 hsb.Append("\r\n");

 73             }

 74 

 75             /// <summary>  

 76             /// This function returns the maximum possible length of an  

 77             /// individual row (line). Of course that if we use table header,  

 78             /// the maximum length of an individual row should equal the  

 79             /// length of the header.  

 80             /// </summary>  

 81             private int GetMaxRowLength()

 82             {

 83                 if (header != null)

 84                     return header.Length;

 85                 else

 86                 {

 87                     int maxlen = rows[0].Count;

 88                     for (int i = 1; i < rows.Count; i++)

 89                         if (rows[i].Count > maxlen)

 90                             maxlen = rows[i].Count;

 91 

 92                     return maxlen;

 93                 }

 94             }

 95 

 96             private void PutDefaultElementAndRemoveExtra()

 97             {

 98                 int maxlen = GetMaxRowLength();

 99 

100                 for (int i = 0; i < rows.Count; i++)

101                 {

102                     // If we find a line that is smaller than the biggest line,  

103                     // we'll add DefaultElement at the end of that line. In the end  

104                     // the line will be as big as the biggest line.  

105                     if (rows[i].Count < maxlen)

106                     {

107                         int loops = maxlen - rows[i].Count;

108                         for (int k = 0; k < loops; k++)

109                             rows[i].Add(DefaultElement);

110                     }

111                     else if (rows[i].Count > maxlen)

112                     {

113                         // This will apply only when header != null, and we try to  

114                         // add a line bigger than the header line. Remove the elements  

115                         // of the line, from right to left, until the line is equal  

116                         // with the header line.  

117                         rows[i].RemoveRange(maxlen, rows[i].Count - maxlen);

118                     }

119 

120                     // Find bad data, loop through all table elements.  

121                     for (int j = 0; j < rows[i].Count; j++)

122                     {

123                         if (rows[i][j] == null)

124                             rows[i][j] = DefaultElement;

125                         else if (rows[i][j] == "")

126                             rows[i][j] = DefaultElement;

127                     }

128                 }

129             }

130 

131             /// <summary>  

132             /// This function will return an array of integers, an element at  

133             /// position 'i' will return the maximum length from column 'i'  

134             /// of the table (if we look at the table as a matrix).  

135             /// </summary>  

136             private int[] GetWidths()

137             {

138                 int[] widths = null;

139                 if (header != null)

140                 {

141                     // Initially we assume that the maximum length from column 'i'  

142                     // is exactly the length of the header from column 'i'.  

143                     widths = new int[header.Length];

144                     for (int i = 0; i < header.Length; i++)

145                         widths[i] = header[i].ToString().Length;

146                 }

147                 else

148                 {

149                     int count = GetMaxRowLength();

150                     widths = new int[count];

151                     for (int i = 0; i < count; i++)

152                         widths[i] = -1;

153                 }

154 

155                 foreach (List<string> s in rows)

156                 {

157                     for (int i = 0; i < s.Count; i++)

158                     {

159                         s[i] = s[i].Trim();

160                         if (s[i].Length > widths[i])

161                             widths[i] = s[i].Length;

162                     }

163                 }

164 

165                 return widths;

166             }

167 

168             /// <summary>  

169             /// Returns a valid format that is to be passed to AppendFormat  

170             /// member function of StringBuilder.  

171             /// General form: "|{i, +/-widths[i]}|", where 0 <= i <= widths.Length - 1  

172             /// and widths[i] represents the maximum width from column 'i'.  

173             /// </summary>  

174             /// <param name="widths">The array of widths presented above.</param>  

175             private string BuildRowFormat(int[] widths)

176             {

177                 string rowFormat = String.Empty;

178                 for (int i = 0; i < widths.Length; i++)

179                 {

180                     if (TextAlignment == AlignText.ALIGN_LEFT)

181                         rowFormat += "| {" + i.ToString() + ",-" + (widths[i]) + "} ";

182                     else

183                         rowFormat += "| {" + i.ToString() + "," + (widths[i]) + "} ";

184                 }

185 

186                 rowFormat = rowFormat.Insert(rowFormat.Length, "|\r\n");

187                 return rowFormat;

188             }

189 

190             /// <summary>  

191             /// Prints the table, main function.  

192             /// </summary>  

193             public void PrintTable()

194             {

195                 if (rows.Count == 0)

196                 {

197                     Console.WriteLine("Can't create a table without any rows.");

198                     return;

199                 }

200                 PutDefaultElementAndRemoveExtra();

201 

202                 int[] widths = GetWidths();

203                 string rowFormat = BuildRowFormat(widths);

204 

205                 // I'm using a temporary string builder to find the total width  

206                 // of the table, and increase BufferWidth of Console if necessary.  

207                 StringBuilder toFindLen = new StringBuilder();

208                 toFindLen.AppendFormat(rowFormat, (header == null ? rows[0].ToArray() : header));

209                 int length = toFindLen.Length;

210                 if (Console.BufferWidth < length)

211                     Console.BufferWidth = length;

212 

213                 // Print the first row, or header (if it exist), you can see that AppendLine  

214                 // is called before/after every AppendFormat.  

215                 StringBuilder hsb = new StringBuilder();

216                 AppendLine(hsb, length);

217                 hsb.AppendFormat(rowFormat, (header == null ? rows[0].ToArray() : header));

218                 AppendLine(hsb, length);

219 

220                 // If header does't exist, we start from 1 because the first row  

221                 // was already printed above.  

222                 int idx = 0;

223                 if (header == null)

224                     idx = 1;

225                 for (int i = idx; i < rows.Count; i++)

226                 {

227                     hsb.AppendFormat(rowFormat, rows[i].ToArray());

228                     AppendLine(hsb, length);

229                 }

230 

231                 Console.WriteLine(hsb.ToString());

232             }

233 

234 

235             static void Main(string[] args)

236             {

237                 // Some test table, with header, and 3 lines by 3 columns.  

238                 ConsoleTable ct = new ConsoleTable();

239                 ct.TextAlignment = ConsoleTable.AlignText.ALIGN_RIGHT;

240                 ct.SetHeaders(new string[] { "ID", "Name", "City" });

241                 ct.AddRow(new List<string> { "1", "John", "New York" });

242                 ct.AddRow(new List<string> { "2", "Mark", "Washington" });

243                 ct.AddRow(new List<string> { "3", "Alice", "Chicago" });

244                 ct.PrintTable();  

245         }

246     }

247   }

 

你可能感兴趣的:(console)