最近在学习使用java来编写cli应用,但是在信息展示上碰到了难题。原因是没有很好工具来展示一个由字符串组成的表格。在git上搜到阿里巴巴有一个叫做 text-ui 的开源项目可以用,但是这个工具在制作表格的时候如果表格内容是中英文混合的,表格就会乱掉。于是就自己写了一个工具类来打印一个字符串组成的表格。
这个工具满足一下使用要求:
package com.hebaibai.ascmd.text;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TextForm {
/**
* 左边距
*/
protected int paddingL = 1;
/**
* 右边距
*/
protected int paddingR = 1;
/**
* 标题
*/
protected List<String> title = new ArrayList<>();
/**
* 数据
*/
protected List<List<String>> datas = new ArrayList<>();
/**
* 最大列数
*/
protected int maxCol = 0;
/**
* 每个单元格最大字符数
*/
protected int colMaxLength = 8;
/**
* 表格组成符号
*/
protected char separator = '.';
private TextForm() {
}
public static TextFormBulider bulider() {
return new TextFormBulider(new TextForm());
}
/**
* 格式化输出表格
*/
public void printFormat() {
List<List<String>> formData = new ArrayList<>();
formData.add(title);
formData.addAll(datas);
Map<Integer, Integer> colMaxLengthMap = colMaxLength(formData);
for (int i = 0; i < formData.size(); i++) {
List<String> row = formData.get(i);
for (int j = 0; j < row.size(); j++) {
Formatter formatter = new Formatter();
String str = row.get(j);
if (str.length() > colMaxLength) {
str = str.substring(0, colMaxLength);
}
int chineseNum = getChineseNum(str);
Integer maxLength = colMaxLengthMap.get(j);
String val = formatter.format("%-" + (maxLength - chineseNum) + "s", str).toString();
row.set(j, val);
}
}
Map<Integer, Integer> colMinBlankLengthMap = colMinBlankLength(formData);
for (int i = 0; i < formData.size(); i++) {
List<String> row = formData.get(i);
for (int j = 0; j < row.size(); j++) {
String val = row.get(j);
Integer minBlankLength = colMinBlankLengthMap.get(j);
val = val.substring(0, val.length() - minBlankLength);
row.set(j, val);
}
}
String line = "";
List<String> rows = new ArrayList<>();
for (List<String> strings : formData) {
String pL = StringUtils.repeat(" ", paddingL);
String pR = StringUtils.repeat(" ", paddingR);
String row = separator + pL + String.join(pL + separator + pR, strings) + pR + separator;
if (line.length() < row.length()) {
line = StringUtils.repeat(separator, row.length());
}
rows.add(row);
}
System.out.println(line);
for (String row : rows) {
System.out.println(row);
System.out.println(line);
}
}
/**
* 找到每一列最大的长度
*
* @param formData
* @return
*/
private Map<Integer, Integer> colMaxLength(List<List<String>> formData) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < formData.size(); i++) {
int col = 0;
List<String> strings = formData.get(i);
while (strings.size() > col) {
String val = strings.get(col);
if (val.length() > colMaxLength) {
val = val.substring(0, colMaxLength);
strings.set(col, val);
}
int length = val.getBytes().length;
Integer integer = map.get(col);
if (integer == null) {
map.put(col, length);
} else {
if (integer < length) {
map.put(col, length);
}
}
col++;
}
}
return map;
}
/**
* 找到每一列从右开始最小的空格长度
*
* @param formData
* @return
*/
private Map<Integer, Integer> colMinBlankLength(List<List<String>> formData) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < formData.size(); i++) {
int col = 0;
List<String> strings = formData.get(i);
while (strings.size() > col) {
String val = strings.get(col);
int length = 0;
for (int i1 = val.length() - 1; i1 >= 0; i1--) {
if (val.charAt(i1) == ' ') {
length++;
} else {
break;
}
}
Integer integer = map.get(col);
if (integer == null) {
map.put(col, length);
} else {
if (integer > length) {
map.put(col, length);
}
}
col++;
}
}
return map;
}
/**
* 获取中文数量
*
* @param val
* @return
*/
private int getChineseNum(String val) {
if (val == null) {
val = "null";
}
String regex = "[\u4e00-\u9fa5|。|,]";
ArrayList<String> list = new ArrayList<String>();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(val);
while (matcher.find()) {
list.add(matcher.group());
}
int size = list.size();
return size;
}
}
package com.hebaibai.ascmd.text;
import java.util.ArrayList;
import java.util.List;
public class TextFormBulider {
private TextForm textForm;
protected TextFormBulider(TextForm textForm) {
this.textForm = textForm;
}
public TextFormBulider title(String... titles) {
if (textForm.maxCol < titles.length) {
textForm.maxCol = titles.length;
}
for (String title : titles) {
if (title == null) {
title = "null";
}
textForm.title.add(title);
}
return this;
}
public TextFormBulider paddingL(int paddingL) {
textForm.paddingL = paddingL;
return this;
}
public TextFormBulider paddingR(int paddingR) {
textForm.paddingR = paddingR;
return this;
}
public TextFormBulider separator(char separator) {
textForm.separator = separator;
return this;
}
public TextFormBulider colMaxLength(int colMaxLength) {
textForm.colMaxLength = colMaxLength;
return this;
}
public TextFormBulider addRow(String... cols) {
if (textForm.maxCol < cols.length) {
textForm.maxCol = cols.length;
}
List<String> list = new ArrayList<>(cols.length);
for (String col : cols) {
if (col == null) {
col = "null";
}
list.add(col);
}
textForm.datas.add(list);
return this;
}
public TextForm finish() {
int titleSize = textForm.title.size();
if (titleSize < textForm.maxCol) {
for (int i = 0; i < textForm.maxCol - titleSize; i++) {
textForm.title.add(null);
}
}
for (List<String> data : textForm.datas) {
int dataSize = data.size();
if (dataSize < textForm.maxCol) {
for (int i = 0; i < textForm.maxCol - dataSize; i++) {
data.add(null);
}
}
}
return textForm;
}
}
@Test
public void printFormat() {
TextForm.bulider()
.title("name", "age", "sex")//设置标题
.addRow("王麻345子", "21", "男2")//添加行
.addRow("wzeefgrerhei", "21", "男")//添加行
.colMaxLength(5)//设置单元格最大数据长度
.separator('*')//设置表格由什么符号构成
.paddingR(2)//右边距
.paddingL(2)//左边距
.finish()//完成
.printFormat();//打印
}
*****************************
* name * age * sex *
*****************************
* 王麻345 * 21 * 男2 *
*****************************
* wzeef * 21 * 男 *
*****************************