2012-07-08 41 views

回答

19

如果你想类似于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)"}, 
}); 
0123上述

的代码将产生以下输出:

+----------+---------+----------------+-------------------+ 
|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)| 
+----------+---------+----------------+-------------------+ 
+0

谢谢,但没有工作,有一条新线! – Hunsu 2014-07-07 07:19:44

+1

@ user230137是正确的,但这是MySQL输出的工作方式。如果在本机MySQL命令行客户端中进行SELECT查询,并将结果导入带有换行字符的行,则输出也会中断,因为输出字符不会以任何方式处理。唯一不同的是,我的实现更紧凑一些,因为它不具有1个空格字符水平填充,而不像MySQL命令行输出。 – 2014-07-09 10:09:58

8

您可以尝试

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

这就是漂亮,因为它会得到不特定的代码。

+0

那不是真的表 – 2012-07-08 15:47:36

+2

@DD。我知道它不是,但正如原来的问题是有点光明细节,我想指出这是(afaik)只有没有代码可能是足够好的解决方案.... – fvu 2012-07-08 17:05:46

+0

好的解决方案,但要得到这个更漂亮的我不得不根据建议[这里]使用进一步的'替换'结果(http://stackoverflow.com/questions/12845208#17423410)。 – 2016-08-17 12:50:08

9

我不知道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 
0

自从我偶然发现了这个寻找一个准备使用打印机(但不仅弦)我改变the accepted answer有点书面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<Object> DEFAULT = new Printer<Object>() { 
     @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 <T> String print(T[][] table, Printer<T> 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 <T> String printPreparedTable(T[][] table, int widths[], String horizontalBorder, Printer<T> 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 <T> String getRow(T[] row, int[] widths, int lineLength, Printer<T> 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 <T> void adjustColumnWidths(T[][] rows, int[] widths, Printer<T> 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 <T> String padRight(String s, int n) { 
     return format("%1$-" + n + "s", s); 
    } 

    private static <T> T safeGet(T[] array, int index, Printer<T> printer) { 
     return index < array.length ? array[index] : null; 
    } 

    private static <T> String getCellValue(T value, Printer<T> printer) { 
     return value == null ? DEFAULT_AS_NULL : printer.print(value); 
    } 

} 

而且Printer.java:

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

用法:

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

为什么改变T?那么String是不够的,但我并不总是希望提供相同的输出obj.toString()必须提供,所以我用界面能够随意改变。

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

为什么更改只支持静态调用?我想把它用于伐木工,只需要一个电话就显而易见了。

+0

嗨。感谢您指出'T' - 我只考虑了'String'。关于返回String的静态方法:我宁愿提供一个写入者回调注入或模仿一个PrintStream来写入记录器,因为通常不需要获得这样的打印结果,但是有必要编写它在某个地方没有考虑将结果委托给可写的适当对象。 – 2013-05-17 15:41:04

+0

另请注意,'String'是一般的表示类型。 'T'不是,只包含一个字段的类型,而不是记录。因为你可能想要处理一个对象列表,其中每个字段可以被转换成一个String。如果你想漂亮地打印由'List '或'Person []'组成的行,那么如何转换然后打印'firstName'('String'),'lastName'('String'),' 'T'中分开的'dateOfBirth'('Date')字段?你可能会建议一个打印机接口返回String []或其他东西,但是在结果中总是会出现'String [] []'。 – 2013-05-17 16:10:22

+0

我不同意你在PrintStream上的第一个评论,因为它与slf4j记录器不兼容,但这可能是一场我不想涉及的圣战(正如提议的静态方法)。为了回答你的问题,对于你放入PrettyPrinter的每个对象,调用Printer接口,在这里你可以定义从T到String的任何转换,其结果比原始的灵活性大得多。你可以迭代列表,如果每个字段都包含Person的列表。如果你没有指定打印机T.toString()被调用。 – 2013-05-21 09:56:36

1

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

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