当前位置:网站首页>数据表格化打印输出

数据表格化打印输出

2022-08-02 07:24:00 逆水行舟没有退路

前言

有时候,我们需要将一堆的数据打印到控制台,方便观察,或者做一些监控功能的时候,希望以表格的形式输出到控制台,方便进行观察。
例如下图,显示服务器列表的状态。
在这里插入图片描述

如何实现数据格式化打印

  1. 导入依赖(如果已经包含可以忽略)
<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
  1. 定义打印工具类
/** * 数据打印工具,支持控制台和日志打印,支持数据左对齐,居中和右对齐,默认右对齐。小标题自动居中(不支持设置) * <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());
    }
}

  1. 编写测试用例,执行测试代码
 @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;
    }
原网站

版权声明
本文为[逆水行舟没有退路]所创,转载请带上原文链接,感谢
https://blog.csdn.net/u011628753/article/details/126113396