Java中的漂亮打印2D数组

我正在寻找一个将一个矩形String [] []打印到一个具有正确列长度的人类可读表中的util。

如果你想要类似于MySQL命令行客户端输出的东西,你可以使用类似的东西:

import java.io.PrintStream; import static java.lang.String.format; import static java.lang.System.out; public final class PrettyPrinter { private static final char BORDER_KNOT = '+'; private static final char HORIZONTAL_BORDER = '-'; private static final char VERTICAL_BORDER = '|'; private static final String DEFAULT_AS_NULL = "(NULL)"; private final PrintStream out; private final String asNull; public PrettyPrinter(PrintStream out) { this(out, DEFAULT_AS_NULL); } public PrettyPrinter(PrintStream out, String asNull) { if ( out == null ) { throw new IllegalArgumentException("No print stream provided"); } if ( asNull == null ) { throw new IllegalArgumentException("No NULL-value placeholder provided"); } this.out = out; this.asNull = asNull; } public void print(String[][] table) { if ( table == null ) { throw new IllegalArgumentException("No tabular data provided"); } if ( table.length == 0 ) { return; } final int[] widths = new int[getMaxColumns(table)]; adjustColumnWidths(table, widths); printPreparedTable(table, widths, getHorizontalBorder(widths)); } private void printPreparedTable(String[][] table, int widths[], String horizontalBorder) { final int lineLength = horizontalBorder.length(); out.println(horizontalBorder); for ( final String[] row : table ) { if ( row != null ) { out.println(getRow(row, widths, lineLength)); out.println(horizontalBorder); } } } private String getRow(String[] row, int[] widths, int lineLength) { final StringBuilder builder = new StringBuilder(lineLength).append(VERTICAL_BORDER); final int maxWidths = widths.length; for ( int i = 0; i < maxWidths; i++ ) { builder.append(padRight(getCellValue(safeGet(row, i, null)), widths[i])).append(VERTICAL_BORDER); } return builder.toString(); } private String getHorizontalBorder(int[] widths) { final StringBuilder builder = new StringBuilder(256); builder.append(BORDER_KNOT); for ( final int w : widths ) { for ( int i = 0; i < w; i++ ) { builder.append(HORIZONTAL_BORDER); } builder.append(BORDER_KNOT); } return builder.toString(); } private int getMaxColumns(String[][] rows) { int max = 0; for ( final String[] row : rows ) { if ( row != null && row.length > max ) { max = row.length; } } return max; } private void adjustColumnWidths(String[][] rows, int[] widths) { for ( final String[] row : rows ) { if ( row != null ) { for ( int c = 0; c < widths.length; c++ ) { final String cv = getCellValue(safeGet(row, c, asNull)); final int l = cv.length(); if ( widths[c] < l ) { widths[c] = l; } } } } } private static String padRight(String s, int n) { return format("%1$-" + n + "s", s); } private static String safeGet(String[] array, int index, String defaultValue) { return index < array.length ? array[index] : defaultValue; } private String getCellValue(Object value) { return value == null ? asNull : value.toString(); } } 

并使用它:

 final PrettyPrinter printer = new PrettyPrinter(out); printer.print(new String[][] { new String[] {"FIRST NAME", "LAST NAME", "DATE OF BIRTH", "NOTES"}, new String[] {"Joe", "Smith", "November 2, 1972"}, null, new String[] {"John", "Doe", "April 29, 1970", "Big Brother"}, new String[] {"Jack", null, null, "(yes, no last name)"}, }); 

上面的代码将产生以下输出:

 +----------+---------+----------------+-------------------+ |FIRST NAME|LAST NAME|DATE OF BIRTH |NOTES | +----------+---------+----------------+-------------------+ |Joe |Smith |November 2, 1972|(NULL) | +----------+---------+----------------+-------------------+ |John |Doe |April 29, 1970 |Big Brother | +----------+---------+----------------+-------------------+ |Jack |(NULL) |(NULL) |(yes, no last name)| +----------+---------+----------------+-------------------+ 

我不知道一个可以执行此操作的util库,但您可以使用String.format函数来格式化字符串,因为您想要显示它们。 这是我很快写下的一个例子:

 String[][] table = {{"Joe", "Bloggs", "18"}, {"Steve", "Jobs", "20"}, {"George", "Cloggs", "21"}}; for(int i=0; i<3; i++){ for(int j=0; j<3; j++){ System.out.print(String.format("%20s", table[i][j])); } System.out.println(""); } 

这给出了以下输出:

  Joe Bloggs 18 Steve Jobs 20 George Cloggs 21 

你可以试试

 System.out.println(Arrays.deepToString(someRectangularStringArray)); 

这就像没有特定代码那样漂亮。

JakWharton有一个很好的解决方案https://github.com/JakeWharton/flip-tables和格式String [],具有对属性名称和结果集的reflection的对象列表。 例如

 List people = Arrays.asList(new Person("Foo", "Bar"), new Person("Kit", "Kat")); System.out.println(FlipTableConverters.fromIterable(people, Person.class)); 

因为我偶然发现了这个搜索即用型打印机(但不仅仅是针对Strings),我改变了Lyubomyr Shaydariv为接受的答案编写的代码。 由于这可能会增加问题的价值,我将分享它:

 import static java.lang.String.format; public final class PrettyPrinter { private static final char BORDER_KNOT = '+'; private static final char HORIZONTAL_BORDER = '-'; private static final char VERTICAL_BORDER = '|'; private static final Printer DEFAULT = new Printer() { @Override public String print(Object obj) { return obj.toString(); } }; private static final String DEFAULT_AS_NULL = "(NULL)"; public static String print(Object[][] table) { return print(table, DEFAULT); } public static  String print(T[][] table, Printer printer) { if ( table == null ) { throw new IllegalArgumentException("No tabular data provided"); } if ( table.length == 0 ) { return ""; } if( printer == null ) { throw new IllegalArgumentException("No instance of Printer provided"); } final int[] widths = new int[getMaxColumns(table)]; adjustColumnWidths(table, widths, printer); return printPreparedTable(table, widths, getHorizontalBorder(widths), printer); } private static  String printPreparedTable(T[][] table, int widths[], String horizontalBorder, Printer printer) { final int lineLength = horizontalBorder.length(); StringBuilder sb = new StringBuilder(); sb.append(horizontalBorder); sb.append('\n'); for ( final T[] row : table ) { if ( row != null ) { sb.append(getRow(row, widths, lineLength, printer)); sb.append('\n'); sb.append(horizontalBorder); sb.append('\n'); } } return sb.toString(); } private static  String getRow(T[] row, int[] widths, int lineLength, Printer printer) { final StringBuilder builder = new StringBuilder(lineLength).append(VERTICAL_BORDER); final int maxWidths = widths.length; for ( int i = 0; i < maxWidths; i++ ) { builder.append(padRight(getCellValue(safeGet(row, i, printer), printer), widths[i])).append(VERTICAL_BORDER); } return builder.toString(); } private static String getHorizontalBorder(int[] widths) { final StringBuilder builder = new StringBuilder(256); builder.append(BORDER_KNOT); for ( final int w : widths ) { for ( int i = 0; i < w; i++ ) { builder.append(HORIZONTAL_BORDER); } builder.append(BORDER_KNOT); } return builder.toString(); } private static int getMaxColumns(Object[][] rows) { int max = 0; for ( final Object[] row : rows ) { if ( row != null && row.length > max ) { max = row.length; } } return max; } private static  void adjustColumnWidths(T[][] rows, int[] widths, Printer printer) { for ( final T[] row : rows ) { if ( row != null ) { for ( int c = 0; c < widths.length; c++ ) { final String cv = getCellValue(safeGet(row, c, printer), printer); final int l = cv.length(); if ( widths[c] < l ) { widths[c] = l; } } } } } private static  String padRight(String s, int n) { return format("%1$-" + n + "s", s); } private static  T safeGet(T[] array, int index, Printer printer) { return index < array.length ? array[index] : null; } private static  String getCellValue(T value, Printer printer) { return value == null ? DEFAULT_AS_NULL : printer.print(value); } } 

和Printer.java:

 public interface Printer { String print(T obj); } 

用法:

  System.out.println(PrettyPrinter.print(some2dArray, new Printer() { @Override public String print(Integer obj) { return obj.toString(); } })); 

为什么改为T ? Well String还不够,但我并不总是希望obj.toString()提供相同的输出,所以我使用该接口能够随意改变它。

第二个变化不是为类提供一个outstream ,如果你想使用类似Logger的类(Log4j // Logback),你肯定会遇到使用stream的问题。

为什么更改只支持静态调用? 我想将它用于记录器,只做一个电话似乎是明显的选择。