100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【java实现控制台打印表格】

【java实现控制台打印表格】

时间:2020-01-23 05:10:47

相关推荐

【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;@Datapublic 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 = pile(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即可完成控制台打印

更新

0715:修改外国人名中的特殊符号宽度,把它当做中文处理,如:兹维·博迪

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。