解析表格数据因存在跨行跨列,导致数据无法结构化。因此必须将表格还原成标准的表格。主要思想就是先恢复跨列数据,然后再恢复跨行数据。跨列数据需要根据colspan属性来复制指定次数的单元格数据到本行数据中。跨行数据需要根据rowspan属性来复制指定次数的单元格数据到多行数据中。还原过程借助map记录单元格的内容。跨行数据恢复中,由于是从第一行第一列开始,逐行逐列遍历,因此跨行数据所在列索引就是遇到的第一个空单元格所在列位置索引。下面是java代码的实现。
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
public class TableUtil {
/**
* 还原跨列表格为标准表格
* @param tableHtml 表格字符串
* @return String 返回类型
*/
public static String getStandardTableWithColspan(String tableHtml) {
if (!tableHtml.toLowerCase().startsWith("<table")) {
tableHtml = "<table>" + tableHtml + "</table>";
}
StringBuffer sb = new StringBuffer();
sb.append("<table>");
Element table = Jsoup.parse(tableHtml).getElementsByTag("table").get(0);
Elements trs = table.getElementsByTag("tr");
String temp = "";
for (Element tr : trs) {
sb.append("<tr>");
sb.append("\r\n");
Elements tds = tr.children();
for (Element td : tds) {
if (td.outerHtml().toLowerCase().contains("colspan") && StringUtils.isNotBlank(td.attr("colspan"))) {
int end = Integer.parseInt(td.attr("colspan"));
temp = td.removeAttr("colspan").toString();
for (int x = 0; x < end; x++) {
sb.append(temp);
sb.append("\r\n");
}
} else {
sb.append(td.toString());
sb.append("\r\n");
}
}
sb.append("</tr>");
sb.append("\r\n");
}
sb.append("</table>");
return sb.toString();
}
/**
* 还原跨行表格为标准表格
* @param tableHtml 表格字符串
* @return String 返回类型
*/
public static String getStandardTableWithRowspan(String tableHtml) {
if (!tableHtml.startsWith("<table")) {
tableHtml = "<table>" + tableHtml + "</table>";
}
Map<String, String> map = new HashMap<>();
Element table = Jsoup.parse(tableHtml);
Elements trs = table.getElementsByTag("tr");
Element td;
int rowspan=0;
for (int x = 0; x < trs.size(); x++) {
Elements tds = trs.get(x).children();
for(int y=0;y<tds.size();y++){
td = tds.get(y);
if(td.hasAttr("rowspan") && StringUtils.isNotBlank(td.attr("rowspan")) && (rowspan = Integer.parseInt(td.attr("rowspan"))) > 1){
td.removeAttr("rowspan");
int indexNull = 0;
boolean isFind = false;
//查找当前行空值的地方就是该列所在的实际位置
for(int m=0;m<rowspan;m++){
if(indexNull == 0){
for(;!isFind && indexNull<Integer.MAX_VALUE;indexNull++){
if(map.get(x+","+indexNull) == null){
isFind = true;
break;
}
}
}
map.put((x+m)+","+indexNull, td.outerHtml());
}
}else{
//查找当前行空值的地方就是该列所在的实际位置
for(int n=0;n<Integer.MAX_VALUE;n++){
if(map.get(x+","+n) == null){
map.put(x+","+n, td.outerHtml());
break;
}
}
}
}
}
return getStrbyMap(map);
}
/**
* 将map还原成table
* @param map 记录表格中单元格数据 key(单元格行索引,单元格列索引) value(单元格)
* @return String 返回类型
*/
private static String getStrbyMap(Map<String, String> map) {
StringBuffer sb = new StringBuffer();
sb.append("<table>");
for (int i = 0; i < Integer.MAX_VALUE; i++) {
if (!map.containsKey(i + "," + 0)) {
sb.append("</table>");
return sb.toString();
}
sb.append("<tr>");
sb.append("\r\n");
for (int j = 0; j < Integer.MAX_VALUE; j++) {
if (map.containsKey(i + "," + j))
sb.append(map.get(i + "," + j));
sb.append("\r\n");
else {
break;
}
}
sb.append("</tr>");
sb.append("\r\n");
}
sb.append("</table>");
return sb.toString();
}
}