【java实现控制台打印表格】
背景
判断给定数据库是否可以连接,习惯做法是安装一个客户端,输入连接信息后连接测试。但是客户现场通常只提供一个linux系统,没有相关客户端。因此,需要一个能在linux上运行的数据库连接测试工具。我的实现思路:使用jdbc连接目标服务器,并执行一条给定的sql语句,能够连接成功则在控制台输出执行结果,连接失败则打印异常信息。
在实现该功能的过程中,对我来说最麻烦的是要在控制台输出表格,难点在于控制表格列宽相等(涉及中英文长度不一致)以及表格内容要居中对齐。
效果
最终实现效果如下:
核心代码分享
分享此内容的目的有二:
- 为要实现同样功能的童鞋提供参考
- 请大佬们从实现思路或者具体方法上指点一下是否有更佳实现方式
主要写了一个PrintTable类:
定义了一个Table内部类,实现以下方法:
- buildTable(List<List< String>> content): 传入二维list,构建表格
- getLimitTable():限制宽度,最大条数后的表格
- getMaxWidthLenList(Table table):得到表格的每列最大宽度,用于实现列宽相等
- getFormatTable(Table table, String symbol):根据指定分隔符得到最终格式化后的表格
- printTable(String… symbols):打印表格,指定分隔符
PrintTable:
package com.sw.utils;import lombok.Data;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;@Data
public class PrintTable {private Table table;//最大列宽:sql查询结果某列内容可能过大,不想完全显示,因此限制最大列宽private Integer maxWidth;//最大条数:sql查询结果可能有非常多,通常不必完全显示,因此限制最大条数private Integer maxLength;public PrintTable(List<List<String>> content, Integer maxWidth, Integer maxLength) {this.table = buildTable(content);this.maxLength = maxLength;this.maxWidth = maxWidth;}public PrintTable(List<List<String>> content) {this.table = buildTable(content);this.maxLength = 10;this.maxWidth = 40;}/*** 创建Table实例** @param content* @return*/private Table buildTable(List<List<String>> content) {return new Table(content);}/*** 打印表格*/public void printTable(String... symbols) {String symbol = symbols.length == 0 ? "|" : symbols[0];//按照最大列宽、最大数据量过滤后的表格Table limitTable = getLimitTable();//设置表格的最大宽度:得到每列宽度,再求和List<Integer> originMaxWidthList = getMaxWidthLenList(limitTable);limitTable.setMaxWidthList(originMaxWidthList);//得到格式化后的表格数据Table formatTable = getFormatTable(limitTable, symbol);Integer totalColSize = formatTable.getTotalColSize();//打印首行分割符号System.out.println(StringUtils.getRepeatChar("-", totalColSize));formatTable.getContent().forEach(row -> {row.forEach(System.out::print);System.out.println();//打印每行分割符号System.out.println(StringUtils.getRepeatChar("-", totalColSize));});}/*** 格式化表格** @param symbol 定义每列间隔符号* @return*/private Table getFormatTable(Table table, String symbol) {//获取原表每列最大宽度List<Integer> originMaxWidthList = table.getMaxWidthList();//除了间隔符号外,固定在每个单元格前后加两个空格int symbolLen = symbol.length() + 2;//遍历原table,将每个单元格填充到该列最大长度List<List<String>> formatList = table.getContent().stream().map(row -> {//用于流在遍历每行的过程中,获取列序号AtomicInteger atomicInteger = new AtomicInteger(0);return row.stream().map(cell -> {//当前遍历的列序号int j = atomicInteger.getAndIncrement();//原表该列的最大宽度+间隔符号宽度-双字节出现的次数int cellSize = originMaxWidthList.get(j) + symbolLen - StringUtils.getZHCharCount(cell);//如果是首行,还需要再前面加一个分割符号|,故长度加1cellSize = j == 0 ? cellSize + 1 : cellSize;//返回原始字符串按照指定symbol填充到指定长度cellSize,并居中对齐的字符return StringUtils.getPadString(cell, cellSize, symbol, j);}).collect(Collectors.toList());}).collect(Collectors.toList());//存储格式化后的表格数据Table formatTable = buildTable(formatList);//设置格式化表格的总宽度:原始宽度+自定义分割符号的总宽度(列数*符号宽度)+首列前面的符号宽度int totalColSize = table.getTotalColSize() + table.getColCount() * symbolLen + 1;formatTable.setTotalColSize(totalColSize);return formatTable;}/*** @return 获取经过条件过滤的表格*/private Table getLimitTable() {List<List<String>> limitContent = table.getContent().stream().limit(maxLength).map(row -> row.stream()//去除内容中含制表符时对结果展示的影响.map(cell -> cell == null ? null : cell.replaceAll("\t", " ")).map(cell -> cell != null && cell.length() > maxWidth ? cell.substring(0, maxWidth) : cell).collect(Collectors.toList())).collect(Collectors.toList());return buildTable(limitContent);}/*** 计算table每行的最大宽度* 要使列宽相等,就需要将每个单元格宽度设置为该列最大宽度,二计算每行最大宽度相对容易些* 故将content转置后得到的每行最大宽度即为所求* 需要考虑单双字节的情况,比如有数组arr:{"aabb","sql表格","编程学习"},* 按照String.length计算,arr[1]最长,但是实际上arr[2]看起来才是最宽的* 因此计算宽度时,将双字节字符看做2个单位长度,即:每出现一个双字节字符,长度+1** @return*/private List<Integer> getMaxWidthLenList(Table table) {//得到转置数组每个元素的长度,一个中文算两个长度return Arrays.stream(table.transpose()).map(rows -> Arrays.stream(rows).mapToInt(s -> {//sql查询结果如果为null,则认为长度为4if (s == null) {return 4;} else {//加上双字节字符出现的次数,最短为null,四个字符return s.length() + StringUtils.getZHCharCount(s);}}).max().orElse(0)).collect(Collectors.toList());}@Dataprivate class Table {/*** 表格内容(含表头)*/private List<List<String>> content = new ArrayList<>();/*** 表格列总字符长度:便于打印行分割符号*/private Integer totalColSize;/*** 每列最大宽度*/private List<Integer> maxWidthList;Integer getTotalColSize() {if (totalColSize == null && maxWidthList != null && maxWidthList.size() != 0) {this.totalColSize = maxWidthList.stream().reduce(Integer::sum).get();}return totalColSize;}//private限制只能通过外部类构造private Table(List<List<String>> content) {this.content = content;}//获取表格行数int getRowCount() {return content.size();}//获取表格列数,0行代表表头,默认认为content中至少含有表头int getColCount() {return content.get(0).size();}/*** 转置二维数组** @return*/private String[][] transpose() {int rowCount = getRowCount();int colCount = getColCount();String[][] result = new String[colCount][rowCount];for (int i = 0; i < rowCount; i++) {for (int j = 0; j < colCount; j++) {result[j][i] = content.get(i).get(j);}}return result;}}}
用到的工具类:StringUtils
package com.sw.utils;import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;public class StringUtils {/*** 判断字符串是否为空** @param str* @return*/public static boolean isEmpty(String str) {return str == null || "".equals(str);}/*** 将content按照正则匹配,返回可以匹配的字符串列表** @param reg* @param content* @return*/public static List<String> extractMessage(String reg, String content) {Pattern compile = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);Matcher matcher = compile.matcher(content);List<String> list = new ArrayList<>();while (matcher.find()) {list.add(matcher.group());}return list;}/*** 将str重复count次,返回结果** @param str* @param count* @return*/public static String getRepeatChar(String str, int count) {StringBuilder res = new StringBuilder();IntStream.range(0, count).forEach(i -> res.append(str));return res.toString();}/*** 将字符串填充到指定长度并居中对齐** @param str* @param len* @return*/public static String getPadString(String str, Integer len) {StringBuilder res = new StringBuilder();str = str.trim();if (str.length() < len) {int diff = len - str.length();int fixLen = diff / 2;String fix = getRepeatChar(" ", fixLen);res.append(fix).append(str).append(fix);if (res.length() > len) {return res.substring(0, len);} else {res.append(getRepeatChar(" ", len - res.length()));return res.toString();}}return str.substring(0, len);}/*** 此方法主要为表格的单元格数据按照指定长度填充并居中对齐并带上分割符号** @param str 原始字符串* @param len 输出字符串的总长度* @param symbol 分割符号* @param index 传入的cell在list的索引,如果为第一个则需要在前面增加分割符号* @return*/public static String getPadString(String str, Integer len, String symbol, int index) {String origin = str + " ";if (index == 0) {String tmp = getPadString(origin, len - 2);return symbol + tmp + symbol;} else {String tmp = getPadString(origin, len - 1);return tmp + symbol;}}/*** 得到一个字符串中单字节出现的次数** @param cell* @return*/public static Integer getENCharCount(String cell) {if (cell == null) {return 0;}String reg = "[^\t\\x00-\\xff]";cell = cell.replaceAll(reg, "");//把·当做中文字符两个宽度return cell.replaceAll("·", "").length();}/*** 得到制表符长度,每个\t显示四个长度** @param cell* @return*/public static Integer getTableCount(String cell) {if (cell == null) {return 0;}String reg = "\t";
// String reg = "|[^\t\\x00-\\xff]";return cell.length() - cell.replaceAll(reg, "").length();}/*** 得到一个字符串中双字节出现的次数** @param cell* @return*/public static Integer getZHCharCount(String cell) {if (cell == null) {return 0;}return cell.length() - getENCharCount(cell);}public static void main(String[] args) {String test = "ab\t哈哈嘻嘻";String reg = "[^\t\\x00-\\xff]";System.out.println(test.replaceAll(reg, "").length());test.replaceAll("\t|[^\\x00-\\xff]", "");System.out.println(test.length());System.out.println(StringUtils.getZHCharCount(test));System.out.println(StringUtils.getENCharCount(test));}
}
调用方法:
将sql得到的rsultSet封装成二维list,再调用PrintTable即可完成控制台打印
更新
20220715:修改外国人名中的特殊符号宽度,把它当做中文处理,如:兹维·博迪
【java实现控制台打印表格】相关推荐
- java控制台打印表格
1.工具类,目前暂不支持中文 package com.base;import org.apache.commons.lang.StringUtils; import java.util.HashMap ...
- java在控制台打印余弦曲线,java打印正弦曲线示例
代码如下: /* * 绘制0°到360°的正弦曲线 * 分两种情形,y>0和y<=0进行绘制 * 每种情形中要考虑每行打印两个"*"字符 * 并在打印第二个" ...
- Java 1.4(打印表格)编写程序,显示以下表格。
a a^2 a^3 1 1 1 2 4 8 3 9 27 4 16 64 代码: package Try;public class Print {public static void main(Str ...
- Java打印表格 Console/控制台
功能: 控制台打印表格,支持字段动态长度,左对齐,设置最多打印多少行. 类下载地址:https://download.csdn.net/download/qq_26599807/12840079 简单 ...
- Java黑皮书课后题第1章:1.4(打印表格)编写程序,显示以下表格
这是目录标题 题目 题目描述 题目槽点 代码 代码块 代码评析与赘述 修改日志 题目 题目描述 1.4(打印表格)编写程序,显示以下表格 a a^2 a^3 1 1 1 2 4 8 3 9 27 4 ...
- 前端小知识:控制台打印(console)- 模拟Java日志打印、表格形式打印美化输出对象、代码运行时间统计
文章目录 6. 控制台打印(Console) 模拟Java日志打印格式 美化对象打印(表格形式打印输出) 日志等级输出(让其在控制台显示时有颜色提示) 代码运行时间统计打印输出 6. 控制台打印( ...
- java输入a控制台打印1_Java—— 流(Stream)、文件(File)和IO
参考于:https://blog.csdn.net/qq_22063697/article/details/52137369 版权声明:本文为博主原创文章,转载请附上博文链接! 一. 什么是 IO ...
- java如何设置控制台打印的字体颜色、背景、字体样式(idea设置打印字体样式)工具类 - 附插件方式
效果: 设置控制台打印字体颜色.背景.字体样式,java工具类 package org.dxl.log;import java.util.Arrays;/*** 在控制台按照传入格式输出 ** @au ...
- 【Java基础】控制台打印日历
描述 通过Calendar的基本使用,以及对控制台输出内容进行对齐的一些小技巧,在控制台打印出某年某月的一个日历. 一. java.util.Calendar类的使用 在Java类库中有一个类:jav ...
最新文章
- events.out.tfevents文件
- 极客新闻——12、错误和失败的区别是什么?
- 简单灵活的 PHP页面跳转函数
- 2018蓝桥杯省赛java_蓝桥杯2018年A组省赛
- C++counting sort计数排序(针对string)的实现算法(附完整源码)
- linux实时线程调度bug,linux中采用用户级线程模拟实现EDF和RMS两种处理机实时调度算法之改进...
- Linux的gcc编译器下载,gcc编译器下载
- 我没有机器学习的学位,却拿到了 DeepMind 研究工程师的 Offer
- 华为集齐 AI 龙珠,“召唤神龙”为期不远
- JavaScript文档对象模型DOM节点操作之删除节点(6)
- Android里面的各种广播
- Tomcat—如何在Windows 7中修改Tomcat的端口号
- spring与security做项目的时候,控制台一直输出显示favicon.ico错误,解决方案!
- 一个九号mini平衡车电池修复过程
- numpy和pandas的参考手册
- 苹果电脑上不错的几款辅助鼠标软件
- Android应用开发中半透明效果实现方案
- 什么是SRE?SRE需要具备什么能力?
- 教你如何查询车辆出险记录
- java 俄罗斯 id_Java ZoneId systemDefault()用法及代码示例
热门文章
- pinned memory or page locked memory)
- 2015校园招聘联通笔试的几个题目
- 电脑小知识:最常用的10个电脑技巧
- QlikView sheet权限
- android x86安装到硬盘不能启动,PC下安装androidx86一些问题的解决方法,gui start
- html让底部文字居中,网站底部版权居中-html css怎么让文字在页面底部居中
- Java 代码实现rar解压最全攻略操作
- 归宿 - 写给无处安放的内心
- CEPH HEALTH错误(二):HEALTH_WARN mds cluster is degraded
- 2019 SD卡、U盘无法格式化怎么办的解决方法