当前位置:网站首页>数据表格化打印输出
数据表格化打印输出
2022-08-02 07:24:00 【逆水行舟没有退路】
前言
有时候,我们需要将一堆的数据打印到控制台,方便观察,或者做一些监控功能的时候,希望以表格的形式输出到控制台,方便进行观察。
例如下图,显示服务器列表的状态。
如何实现数据格式化打印
- 导入依赖(如果已经包含可以忽略)
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
- 定义打印工具类
/** * 数据打印工具,支持控制台和日志打印,支持数据左对齐,居中和右对齐,默认右对齐。小标题自动居中(不支持设置) * <br>备注:大标题可以使用中文,小标题和内容不建议包含中文 */
@Slf4j
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DataPrinter {
/** * 日志输出模式(默认控制台输出) */
private LogOutputModel logOutputModel = LogOutputModel.CONSOLE;
/** * 数据对齐模式(默认右对齐) */
private DataAlignModel dataAlignModel = DataAlignModel.RIGHT;
/** * 是否显示行数 */
private boolean showRows = true;
/** * 日志输出模式 */
public enum LogOutputModel {
LOG, CONSOLE;
}
/** * 数据对齐模式 */
public enum DataAlignModel {
LEFT, CENTER, RIGHT;
}
/** * 数据格式化输出 * @param topTitle 大标题 * @param titles 小标题 * @param data 数据集合 */
public void printListMap(String topTitle, String[] titles, List<Map<String, Object>> data) {
List<Object[]> objsList = new ArrayList<>();
if (null != data) {
for (Map<String, Object> map : data) {
Object[] objs = new Object[titles.length];
for (int i = 0; i < titles.length; i++) {
Object obj = map.get(titles[i]);
objs[i] = obj;
}
objsList.add(objs);
}
}
print(topTitle,titles,objsList);
}
/** * 数据格式化输出 * @param topTitle 大标题 * @param titles 小标题 * @param data 数据集合 */
public void print(String topTitle, String[] titles, List<Object[]> data) {
topTitle = null == topTitle ? "Not Defined" : topTitle;
/** * 控制日志输出 */
Function<String, Integer> outputLog_fun = (string) -> {
if (this.getLogOutputModel() == LogOutputModel.CONSOLE) {
System.out.println(string);
} else if (this.getLogOutputModel() == LogOutputModel.LOG) {
log.info(string);
}
return 1;
};
/** * 1. 根据标题和内容计算每一列需要的最大宽度 */
Integer[] lineWidths = new Integer[titles.length];
int totalWidth = 0;//最大长度
for (int i = 0; i < lineWidths.length; i++) {
int maxWidth = 0; // 默认最小长度
String title = titles[i];
if (null != title) {
maxWidth = Math.max(maxWidth, title.length());
} else {
titles[i] = "";
}
if (null != data) {
for (Object[] array : data) {
Object obj = array[i];
if (null != obj) {
maxWidth = Math.max(maxWidth, obj.toString().length());
}
}
}
if (maxWidth + 4 < 10) {
maxWidth = 10;
} else {
maxWidth = maxWidth + 4; //避免数据左右顶到表格
}
lineWidths[i] = maxWidth;
totalWidth = totalWidth + maxWidth;
}
/** * 2. 输出大标题 */
if (isShowRows()) {
int dataSize = null != data ? data.size() : 0;
outputLog_fun.apply(StringUtils.leftPad(topTitle + "(rows:" + dataSize + ")", (int) (totalWidth * 0.5)));
} else {
outputLog_fun.apply(StringUtils.leftPad(topTitle, (int) (totalWidth * 0.5)));
}
/** * 3. 输出标题头 */
StringBuffer head_sb = new StringBuffer("+");
for (int i = 0; i < titles.length; i++) {
String title = titles[i];
String str = StringUtils.center(title, lineWidths[i], "-");
head_sb.append(str).append("+");
}
outputLog_fun.apply(head_sb.toString());
/** * 4. 输出数据 */
if (null != data) {
for (int i = 0; i < data.size(); i++) {
Object[] array = data.get(i);
StringBuffer data_sb = new StringBuffer("|");
for (int j = 0; j < array.length; j++) {
Object obj = array[j];
String str = null;
if (this.getDataAlignModel() == DataAlignModel.RIGHT) {
str = String.format("%" + (lineWidths[j] - 2) + "s ", obj);
} else if (this.getDataAlignModel() == DataAlignModel.CENTER) {
str = StringUtils.center(obj + "", lineWidths[j]);
} else if (this.getDataAlignModel() == DataAlignModel.LEFT) {
str = String.format(" %-" + (lineWidths[j] - 2) + "s", obj);
}
data_sb.append(str).append("|");
}
outputLog_fun.apply(data_sb.toString());
}
}
/** * 5.输出结尾 */
StringBuffer foot_sb = new StringBuffer("+");
for (int i = 0; i < titles.length; i++) {
String str = StringUtils.center("", lineWidths[i], "-");
foot_sb.append(str).append("+");
}
outputLog_fun.apply(foot_sb.toString());
}
}
- 编写测试用例,执行测试代码
@Test
void testDataPrinter() {
String topTitle = "服务器的运行状态表";
String[] titles = {
"IP", "PORT", "SERVICE NAME", "QPS",null};
List<Object[]> objs = new ArrayList<>();
objs.add(new Object[]{
"1.2.3.4", 65121, "order", 600000,"UP"});
objs.add(new Object[]{
"192.82.9.247", 85, "game", 7788,"DOWN"});
objs.add(new Object[]{
"114.114.115.115", 65121, "financial", 2,"UP"});
objs.add(new Object[]{
"88.2.3.4", 65121, "game99", 989,"UP"});
objs.add(new Object[]{
"199.77.35.4", 65121, "pay", 333,"UP"});
objs.add(new Object[]{
null, "", null, 133,"UP"});
DataPrinter dataPrinter= new DataPrinter();//默认数据是右对齐,控制台打印
dataPrinter.setLogOutputModel(DataPrinter.LogOutputModel.CONSOLE);//控制台输出
dataPrinter.print(topTitle, titles, objs);
dataPrinter.setDataAlignModel(DataPrinter.DataAlignModel.LEFT);//左对齐
dataPrinter.print(topTitle, titles, objs);
dataPrinter.setDataAlignModel(DataPrinter.DataAlignModel.CENTER);//居中
dataPrinter.print(topTitle, titles, objs);
/** * 自定义参数日志输出 */
DataPrinter dataPrinter2=new DataPrinter(DataPrinter.LogOutputModel.LOG,DataPrinter.DataAlignModel.RIGHT,true);
dataPrinter2.print(topTitle, titles, objs);
/** * ListMap的打印 */
String[] titles2={
"ip","port"};
List<Map<String,Object>> objsList=new ArrayList<>();
Map<String,Object> m1=new HashMap<>();
m1.put("ip","1.1.2.3");
m1.put("port",9898);
objsList.add(m1);
Map<String,Object> m2=new HashMap<>();
m2.put("ip","1.190.22.3");
m2.put("port",18908);
objsList.add(m2);
dataPrinter.setDataAlignModel(DataPrinter.DataAlignModel.RIGHT);//居中
dataPrinter.printListMap("服务器列表", titles2, objsList);
}
控制台内容解释
关于问题
中文位宽问题
由于中文字符,中文输出的时候,一个中文占用1.5个位宽(见下图),两个中文字占用3个位宽。所以标题和内容有中文时,将会出现对齐错误的问题。所以数据输出的时候,小标题和内容不建议包含中文和中文字符。
如何解决中文位宽问题
- 如何控制对齐?
我们知道了一个中文或者中文字符占位1.5个位宽,如果是奇数个中文,就意味着肯定对不齐,因此需要对包含中文字符的字符串补一个中文状态下的空格(中文状态下输出的空格也是1.5个位宽),所以这样就得到了整数个位宽。计算长度的时候,只需要将中文识别为1.5个位宽,将包含中文的内容长度进行转换,即可实现数据表的对齐。 - 我们知道了补位和长度转换这个思路后,那么另一个问题就来了,如何识别中文和中文字符(包含中文空格,中文标点等)?
下面我已经提供好了这个方法
/** * 判断是否是中文或者中文符号 * * @param c * @return */
private static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
return true;
}
return false;
}
边栏推荐
- MySQL-索引优化和查询优化
- The best interests of buying and selling stocks with handling fees [What is missing in the definition of DP status?]
- OC - NSSet (set)
- AcWing 2811. 最长公共子串(后缀自动机 fa 指针的性质)
- 替换ptmalloc,使用tcmalloc和jemalloc
- [mixed] PIP in domestic source tutorial and domestic source address
- (2022 Niu Ke Duo School 5) B-Watches (two points)
- MySQL - based
- View zombie processes
- postgres 水平分表,自动创建分区,按时间分表
猜你喜欢
随机推荐
HCIP 第八天
暂未找到具体原因但解决了的bug
HCIP 第十一天
Fatal error compiling: 无效的目标发行版: 11
spark 读取本地文件
MySQL - Index Optimization and Query Optimization
spark read folder data
Kind of weird!Access the destination URL, the host can container but not
I.MX6U-ALPHA开发板(EPIT定时器实验)
MySQL - locking mechanism
MGRE综合实验
MySQL-底层设置
Comprehensive experiment of MPLS and BGP
理论问题与工程问题的差异在哪里?
Understand Chisel language. 31. Chisel advanced communication state machine (3) - Ready-Valid interface: definition, timing and implementation in Chisel
论文理解:“Cross-Scale Residual Network: A GeneralFramework for Image Super-Resolution,Denoising, and “
From cloud computing to function computing
A full review of mainstream timed task solutions
MySQL-索引详解
MySQL batch update