我正在寻找一个实用程序,它将一个矩形 String[][] 打印到一个具有正确列长度的人类可读表中。
问问题
19156 次
5 回答
25
如果你想要类似于 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)|
+----------+---------+----------------+-------------------+
于 2012-07-08T15:41:10.963 回答
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
于 2012-07-08T12:57:22.463 回答
9
你可以试试
System.out.println(Arrays.deepToString(someRectangularStringArray));
这和没有特定代码的情况一样漂亮。
于 2012-07-08T12:21:59.597 回答
2
由于我偶然发现了这种寻找即用型打印机(但不仅适用于字符串),因此我稍微更改了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
,如果你想使用像 (Log4j // Logback) 这样的 Logger 类,你肯定会在使用流时遇到问题。
为什么更改为仅支持静态调用?我想将它用于记录器,并且只打一个电话似乎是显而易见的选择。
于 2013-03-05T19:47:02.627 回答
1
JakWharton 有一个很好的解决方案https://github.com/JakeWharton/flip-tables和格式 String[],具有属性名称和结果集反射的对象列表。例如
List<Person> people = Arrays.asList(new Person("Foo", "Bar"), new Person("Kit", "Kat"));
System.out.println(FlipTableConverters.fromIterable(people, Person.class));
于 2015-02-14T17:09:08.603 回答