2012-07-08 37 views
12

Tôi đang tìm một tiện ích sẽ in một Chuỗi hình chữ nhật [] [] vào một bảng có thể đọc được với độ dài cột chính xác.Mảng 2D in đẹp trong Java

Trả lời

19

Nếu bạn muốn một cái gì đó tương tự như MySQL đầu ra dòng lệnh của khách hàng, bạn có thể sử dụng một cái gì đó như thế:

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(); 
    } 

} 

Và sử dụng nó như thế:

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)"}, 
}); 

Đoạn mã trên sẽ cho kết quả sau:

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

Cảm ơn nhưng không làm việc có một dòng mới! – Hunsu

+1

@ user230137 đúng, nhưng đây là cách đầu ra MySQL hoạt động. Nếu bạn thực hiện truy vấn 'SELECT', trong trình khách dòng lệnh gốc của MySQL, kết quả là các hàng có các ký tự dòng mới, thì đầu ra cũng bị hỏng, vì các ký tự đầu ra không được xử lý theo bất kỳ cách nào. Sự khác biệt duy nhất được biết với tôi là việc triển khai thực hiện của tôi nhỏ gọn hơn một chút, bởi vì nó không có ký tự đệm ngang 1 ký tự không gian, không giống như đầu ra dòng lệnh MySQL. –

8

Bạn có thể thử

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

Và đó là xinh đẹp như nó sẽ nhận được mà không cần mã cụ thể.

+0

Đó không thực sự là bảng –

+2

@DD. Tôi biết nó không phải là như là câu hỏi ban đầu là một chút ánh sáng về chi tiết tôi muốn chỉ ra điều này là (afaik) chỉ không có mã có thể giải pháp đủ tốt .... – fvu

+0

Giải pháp tốt nhưng để có được điều này đẹp hơn tôi đã phải sử dụng thêm 'thay thế' trên kết quả như được khuyên [ở đây] (http://stackoverflow.com/questions/12845208#17423410). –

9

Tôi không biết về một thư viện util có thể làm điều này nhưng bạn có thể sử dụng hàm String.format để định dạng các chuỗi như bạn muốn hiển thị chúng. Đây là một ví dụ tôi nhanh chóng viết lên:

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(""); 
    } 

này cung cấp cho các đầu ra sau đây:

  Joe    Bloggs     18 
      Steve    Jobs     20 
     George    Cloggs     21 
0

Vì tôi stumbled khi tìm kiếm này cho một sẵn sàng để sử dụng máy in (nhưng không chỉ cho Strings) Tôi đã thay đổi Lyubomyr Shaydariv 's mã được viết cho the accepted answer một chút. Do đây có thể tăng giá trị cho các câu hỏi, tôi sẽ chia sẻ nó:

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); 
    } 

} 

Và Printer.java:

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

Cách sử dụng:

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

Tại sao sự thay đổi để T? Vâng String là không đủ, nhưng tôi không luôn luôn muốn cùng một đầu ra obj.toString() đã cung cấp, vì vậy tôi sử dụng giao diện để có thể thay đổi theo ý muốn.

Thay đổi thứ hai không cung cấp lớp học với outstream, tốt nếu bạn muốn sử dụng lớp với Trình ghi nhật ký như (Log4j // Logback), bạn chắc chắn sẽ gặp sự cố khi sử dụng luồng.

Tại sao thay đổi chỉ hỗ trợ cuộc gọi tĩnh? Tôi muốn sử dụng nó cho logger, và làm cho chỉ một cuộc gọi duy nhất dường như là sự lựa chọn hiển nhiên.

+0

Xin chào. Cảm ơn bạn đã chỉ vào 'T' - tôi chỉ xem xét' Chuỗi '. Về phương thức tĩnh trả về một 'Chuỗi': Tôi muốn cung cấp một cú gọi ngược của nhà văn hoặc bắt chước một' PrintStream' để ghi vào một trình ghi, vì thường không cần thiết để có được kết quả của bản in đó, nhưng nó cần thiết để viết nó ở đâu đó mà không cần quan tâm đến việc ủy ​​thác kết quả cho một đối tượng thích hợp có thể viết. –

+0

Cũng lưu ý rằng 'Chuỗi' là một loại biểu diễn chung. 'T' không phải là và nó bao gồm một loại duy nhất cho một trường duy nhất, không phải là một bản ghi. Bởi vì bạn có thể muốn xử lý một danh sách các đối tượng trong đó mỗi trường có thể được chuyển đổi thành một 'Chuỗi'. Nếu bạn muốn in các hàng đẹp bao gồm một 'Danh sách ' hoặc 'Người []' - làm thế nào để bạn chuyển đổi sau đó để in 'firstName' (' String'), 'lastName' (' String'), ' các trường dateOfBirth' ('Date') tách biệt trong' T'? Bạn có thể đề xuất một giao diện máy in trả về String [] hoặc bất kỳ thứ gì, nhưng trong kết quả nó luôn luôn đi 'String [] []'. –

+0

Tôi không đồng ý với nhận xét đầu tiên của bạn về PrintStream vì nó không tương thích với trình ghi nhật ký slf4j, nhưng đó có thể là một cuộc chiến thánh mà tôi không muốn tham gia (như phương pháp tĩnh được đề xuất). Để trả lời câu hỏi của bạn, đối với mỗi đối tượng bạn đưa vào giao diện Máy in của PrettyPrinter, bạn có thể định nghĩa bất kỳ chuyển đổi nào từ T thành String, kết thúc linh hoạt hơn rất nhiều so với bản gốc. Và bạn có thể lặp lại danh sách, nếu mọi trường đều giữ một danh sách của Person. Nếu bạn không chỉ định máy in T.toString() được gọi. –

1

JakWharton có một giải pháp tốt đẹp https://github.com/JakeWharton/flip-tables và định dạng Chuỗi [], Danh sách các đối tượng có phản ánh về tên thuộc tính và tập hợp kết quả. ví dụ.

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