Java 对齐列中的文本:以表格格式打印

后端 潘老师 2年前 (2023-10-11) 411 ℃ (0) 扫码查看

在使用Java时,我们经常需要在列中格式化和对齐程序输出,并以表格形式呈现数据。在本文中,我们将探讨如何使用自定义TextTable类来对齐文本在列中并以表格格式打印。

1.使用自定义TextTable对齐列中文本

Java不提供内置的类或库支持以表格格式打印文本。因此,我们创建了自己的实现,命名为TextTable类。

1.1. 使用TextTable

在进行实现之前,让我们从如何使用TextTable类开始。在本示例中,我们创建了一个包含3列ID、NAME和AGE的表格。我们可以创建任意数量的列。在指定列名时,我们还指定了列的对齐方向。

接下来,我们将数据添加为对象数组。对于每个对象,将调用toString()方法以获取字符串值。我们还可以直接添加字符串、数字和其他原始类型。

最后,我们调用textTable.build()方法并指定要在每行末尾打印的行结束字符。在大多数情况下,它将是行分隔符。

TextTable textTable = new TextTable(
  List.of(
    new Column("ID", Alignment.RIGHT),
    new Column("NAME", Alignment.LEFT),
    new Column("AGE", Alignment.RIGHT)
  )
);

textTable.add(1, "Lokesh", 40);
textTable.add(2, "Alexandru", 27);
textTable.add(3, "Mick", 38);

String formattedText = textTable.build(System.lineSeparator());
System.out.println(formattedText);

上述程序将打印以下输出:

| ID | NAME      | AGE |
| --:|:--------- | ---:|
|  1 | Lokesh    |  40 |
|  2 | Alexandru |  27 |
|  3 | Mick      |  38 |

1.2. TextTable类

现在我们知道如何使用TextTable类了,让我们看看它的实现。TextTable类提供了定义列、添加行和构建格式化表格的方法。

  • Column类表示具有标题、宽度和对齐方式的表格列。
  • Row类表示具有值的表格行。
  • Alignment枚举表示列的对齐方式:LEFT、CENTER、RIGHT。
/**
 *一个用于创建和格式化基于文本的表格的实用类。
 */
public class TextTable {

  /**
   * 创建一个具有指定标题的新列。
   *
   * @param header The header of the column.
   * @return A new column.
   */
  public static Column column(String header) {
    return new Column(header);
  }

  /**
   * 创建一个具有指定标题和对齐方式的新列。
   *
   * @param header     The header of the column.
   * @param alignment  The alignment of the column.
   * @return A new column.
   */
  public static Column column(String header, Alignment alignment) {
    return new Column(header, alignment);
  }

  private final List<Column> columns;
  private final List<Row> rows = new ArrayList<>();

  /**
   * 使用指定的列创建一个TextTable。
   *
   * @param columns The columns to include in the table.
   */
  public TextTable(List<Column> columns) {
    this.columns = columns;
  }

  /**
   * 构建并将表格作为字符串返回,使用给定的行结束字符。
   *
   * @param lineEnding The line ending to use.
   * @return The formatted table as a string.
   */
  public String build(String lineEnding) {
    StringBuilder destination = new StringBuilder();
    append(destination, lineEnding);
    return destination.toString();
  }

  /**
   * 使用指定的行结束字符将表格附加到指定的StringBuilder中。
   *
   * @param destination The StringBuilder to append the table to.
   * @param lineEnding  The line ending to use.
   */
  public void append(StringBuilder destination, String lineEnding) {
    List<String> headers = columns.stream().map(c -> c.formatHeader(" "))
        .collect(Collectors.toList());
    printRow(destination, headers);
    destination.append(lineEnding);
    printSeparators(destination);
    for (Row row : rows) {
      destination.append(lineEnding);
      printRow(destination, row.format(columns, " "));
    }
  }

  private void printSeparators(StringBuilder destination) {
    destination.append('|');
    for (Column column : columns) {
      destination.append(column.alignment != Alignment.RIGHT ? ':' : ' ');
      destination.append(column.getSeparator('-'));
      destination.append(column.alignment != Alignment.LEFT ? ':' : ' ');
      destination.append('|');
    }
  }

  private void printRow(StringBuilder destination, List<String> values) {
    destination.append('|');
    for (String value : values) {
      destination.append(' ');
      destination.append(value);
      destination.append(' ');
      destination.append('|');
    }
  }

  /**
   * 向表格添加具有指定值的行。值的数量必须与列的数量匹配。
   *
   * @param values The values to add to the row.
   * @throws IllegalArgumentException if the number of values doesn't match the number of columns.
   */
  public void add(@Nonnull Object... values) {
    if (values.length != columns.size()) {
      throw new IllegalArgumentException(
          "Received the wrong amount of values for the table row, expected " + columns.size()
              + ", received " + values.length + ".");
    }
    Row row = new Row();
    for (int i = 0; i < values.length; i++) {
      String value = Objects.toString(values[i]);
      row.values.add(value);
      columns.get(i).fit(value);
    }
    rows.add(row);
  }

  /**
   * 通过重置列宽度并移除所有行来清空表格。
   */
  public void clear() {
    for (Column column : columns) {
      column.resetWidth();
    }
    rows.clear();
  }

  /**
   *获取表格中列的不可修改列表。
   *
   * @return An unmodifiable list of columns.
   */
  public List<Column> getColumns() {
    return Collections.unmodifiableList(columns);
  }

  /**
   * 一个表示表格列的类。
   */
  public static class Column {

    private String header;
    private int width;
    private Alignment alignment;

    /**
     * 使用指定的标题和默认左对齐方式创建一个新列。
     *
     * @param header The header of the column.
     */
    public Column(String header) {
      this(header, Alignment.LEFT);
    }

    /**
     *创建一个具有指定标题和对齐方式的新列。
     *
     * @param header     The header of the column.
     * @param alignment  The alignment of the column.
     */
    public Column(String header, Alignment alignment) {
      this.header = header;
      this.width = header.length();
      this.alignment = alignment;
    }

    /**
     *使用指定的填充格式化标题。
     *
     * @param padding The padding to apply.
     * @return The formatted header.
     */
    public String formatHeader(String padding) {
      return format(header, padding);
    }

    /**
     * 使用指定的填充格式化值。
     *
     * @param value   The value to format.
     * @param padding The padding to apply.
     * @return The formatted value.
     */
    public String format(String value, String padding) {
      switch (alignment) {
        case LEFT:
          return StringUtils.rightPad(value, width, padding);
        case RIGHT:
          return StringUtils.leftPad(value, width, padding);
        default:
          int length = value.length();
          int left = (width - length) / 2;
          int leftWidth = left + length;
          return StringUtils.rightPad(StringUtils.leftPad(value, leftWidth, padding), width, padding);
      }
    }

    /**
     *获取特定字符和宽度的分隔符。
     *
     * @param character The character to use for the separator.
     * @return The separator string.
     */
    public String getSeparator(char character) {
      return StringUtils.leftPad("", width, character);
    }

    /**
     * 根据指定的值调整列宽。
     *
     * @param value The value to adjust the column width.
     */
    public void fit(String value) {
      if (value.length() > width) {
        width = value.length();
      }
    }

    /**
     * 将列宽重置为初始标题的长度。
     */
    public void resetWidth() {
      this.width = header.length();
    }
  }

  /**
   * 一个代表表格行的类。
   */
  public static class Row {
    private final ArrayList<String> values = new ArrayList<>();

    /**
     * 使用指定的填充格式化行值。
     *
     * @param columns The list of columns to align the values with.
     * @param padding The padding to apply.
     * @return A list of formatted values.
     * @throws IllegalArgumentException if the number of columns doesn't match the number of values.
     */
    public List<String> format(List<Column> columns, String padding) {
      if (columns.size() != values.size()) {
        throw new IllegalArgumentException(
            "Received the wrong amount of columns for the table row, expected " + columns.size()
                + ", received " + values.size() + ".");
      }
      return Streams.zip(values.stream(), columns.stream(), (v, c) -> c.format(v, padding))
          .collect(Collectors.toList());
    }
  }

  /**
   * 一个枚举代表列的对齐方式:LEFT(左对齐)、CENTER(居中对齐)、RIGHT(右对齐)。
   */
  public enum Alignment {
    LEFT, CENTER, RIGHT
  }
}

2. 使用String.format()进行简单的列对齐

在列中对齐文本的最直接方法之一是使用String.format()方法,该方法允许我们在格式字符串中为每个列指定宽度和对齐方式。

另请参阅:在Java中左对齐、右对齐或居中对齐字符串

以下示例使用String.format()方法打印与上一个示例中相同的数据。

int[] ids = { 1, 2, 3};
String[] names = { "Lokesh", "Alexandru", "Mick" };
int[] scores = { 40, 27, 38};

// 定义列宽
int idColumnWidth = 10;
int nameColumnWidth = 20;
int scoreColumnWidth = 10;

// 打印表头
System.out.println(String.format("%-" + idColumnWidth + "s%-" + nameColumnWidth + "s%-"
  + scoreColumnWidth + "s", "ID", "NAME", "AGE"));
System.out.println("-".repeat(idColumnWidth + nameColumnWidth + scoreColumnWidth));

// 以对齐的列形式打印数据。
for (int i = 0; i < names.length; i++) {

  String formattedId = String.format("%-" + idColumnWidth + "d", ids[i]);
  String formattedName = String.format("%-" + nameColumnWidth + "s", names[i]);
  String formattedScore = String.format("%-" + scoreColumnWidth + "d", scores[i]);
  System.out.println(formattedId + formattedName + formattedScore);
}

我们可以在控制台中验证以表格格式打印的文本。

ID        NAME                AGE
----------------------------------------
1         Lokesh              40
2         Alexandru           27
3         Mick                38

3. 结论

在处理结构化数据表示时,在以表格格式打印程序输出方面是一个非常常见的需求。使用上述讨论的TextTable类或String.format()方法,我们可以轻松地对齐文本在列中并在控制台或任何输出文件中打印它们。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/9453.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】