读取如图文件
并以表格形式输出至控制台(直接发代码或远程操作)
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;
import java.util.stream.IntStream;
@Data
public class PrintTable {
private Table table;
private Integer maxWidth;
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 = 12;
this.maxWidth = 150;
}
/**
* 创建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(getRepeatChar("-", totalColSize));
formatTable.getContent()
.forEach(row -> {
row.forEach(System.out::print);
System.out.println();
//打印每行分割符号
System.out.println(getRepeatChar("-", totalColSize));
});
}
/**
* 将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 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 - getZHCharCount(cell);
//如果是首行,还需要再前面加一个分割符号|,故长度加1
cellSize = j == 0 ? cellSize + 1 : cellSize;
//返回原始字符串按照指定symbol填充到指定长度cellSize,并居中对齐的字符
return 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;
}
/**
* 将字符串填充到指定长度并居中对齐
*
* @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 getZHCharCount(String cell) {
if (cell == null) {
return 0;
}
return cell.length() - getENCharCount(cell);
}
/**
* 得到一个字符串中单字节出现的次数
*
* @param cell
* @return
*/
public static Integer getENCharCount(String cell) {
if (cell == null) {
return 0;
}
String reg = "[^\t\\x00-\\xff]";
// String reg = "|[^\t\\x00-\\xff]";
return cell.replaceAll(reg, "").length();
}
/**
* @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,则认为长度为4
if (s == null) {
return 4;
} else {
//加上双字节字符出现的次数,最短为null,四个字符
return s.length() + getZHCharCount(s);
}
}).max().orElse(0)
).collect(Collectors.toList());
}
@Data
private 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;
}
}
}
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Sheet;
import org.springframework.util.StringUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author liuzaihuan
* @date 2021/4/12 13:54
*/
public class ExcelReadUtil {
//初始化Sheet
private static Sheet initSheet;
static {
initSheet = new Sheet(1, 0);
initSheet.setSheetName("sheet");
//设置自适应宽度
initSheet.setAutoWidth(Boolean.TRUE);
}
private static List<Object> readRowBySheet(String fileUrl) {
if (!StringUtils.hasText(fileUrl)) {
return null;
}
InputStream in = null;
try {
in = new FileInputStream(fileUrl);
ExcelListener excelListener = new ExcelListener();
EasyExcelFactory.readBySax(in, initSheet, excelListener);
return excelListener.getDatas();
} catch (Exception e) {
System.out.println("找不到文件或文件路径错误, 文件 : " + fileUrl);
} finally {
close(in);
}
return null;
}
/**
* 工具类关闭流 可变参数:... 只能形参的最后一个位置 处理方式与数组一致
*/
public static void close(Closeable... io) {
// 增强for循环
for (Closeable temp : io) {
if (null != temp) {
try {
temp.close();
} catch (IOException e) {
System.out.println("关闭 IO 流 发生异常 : " + e.getMessage());
e.printStackTrace();
}
}
}
}
/**
* 解析监听器,
* 每解析一行会回调invoke()方法。
* 整个excel解析结束会执行doAfterAllAnalysed()方法
*/
public static class ExcelListener extends AnalysisEventListener {
private List<Object> datas = new ArrayList<>();
/**
* 逐行解析
* object : 当前行的数据
*/
@Override
public void invoke(Object object, AnalysisContext context) {
//当前行
// context.getCurrentRowNum()
if (object != null) {
datas.add(object);
}
}
/**
* 解析完所有数据后会调用该方法
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//解析结束销毁不用的资源
}
public List<Object> getDatas() {
return datas;
}
public void setDatas(List<Object> datas) {
this.datas = datas;
}
}
public static void main(String[] args) {
List<Object> dataList = readRowBySheet("D:\\点位.xlsx");
List<List<String>> content = new ArrayList<>();
dataList.forEach(data -> {
List<String> strList = (List<String>) data;
content.add(strList);
});
PrintTable table = new PrintTable(content);
table.printTable();
}
}
依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.6</version> </dependency>