eec icon indicating copy to clipboard operation
eec copied to clipboard

【读取excel】样式获取

Open 2259289435 opened this issue 3 years ago • 27 comments

请教读取excel的时候,通过现有 api 或者某种方式怎么获取到单元格样式? 方便后续使用。

2259289435 avatar Dec 05 '22 05:12 2259289435

读的话目前只解析了Numfmt,其它样式未解析,后续会补上请持续关注本项目了解最新进度

wangguanquan avatar Dec 05 '22 06:12 wangguanquan

嗯,我可以先用哪个api呢,后续可以关注项目了解进度,现在样式只有numfmt也没关系。

2259289435 avatar Dec 05 '22 06:12 2259289435

  1. 首先在ExcelReader#init方法大约570有一段Styles.load(s);代码初始化加载样式文件
  2. 解析Cell的时候样式保存在org.ttzero.excel.reader.Cell对象的xf属性中,xf为style index并不是style
  3. 在Row对象中需要增加public int getStyle(){ }方法,调用Styles#getStyleByIndex方法使用第2步的style index换取style
  4. 然后就可以Styles#getNumFmt, Styles#getFill, Styles#getFont, Styles#getBorder 等方法获取具体的样式了

有兴趣的话可以尝试实现xlsx格式的样式解析

wangguanquan avatar Dec 05 '22 06:12 wangguanquan

  1. 首先在ExcelReader#init方法大约570有一段Styles.load(s);代码初始化加载样式文件
  2. 解析Cell的时候样式保存在org.ttzero.excel.reader.Cell对象的xf属性中,xf为style index并不是style
  3. 在Row对象中需要增加public int getStyle(){ }方法,调用Styles#getStyleByIndex方法使用第2步的style index换取style
  4. 然后就可以Styles#getNumFmt, Styles#getFill, Styles#getFont, Styles#getBorder 等方法获取具体的样式了

有兴趣的话可以尝试实现xlsx格式的样式解析

3q, 对第 3 点 还有点疑问。我搜索代码发现 ExcelReader#init 570 获取到的 styles 在599设置到了 sheet 中,是不是我如果需要获取 cell 的 style,可以通过 sheet.getStyles().getStyleByIndex(row.getCell(0).xf). 不明白为什么说通过 row 去获取style。把业务样板代码写好了来尝试做除fmt外的样式解析。

2259289435 avatar Dec 05 '22 07:12 2259289435

  1. 首先在ExcelReader#init方法大约570有一段Styles.load(s);代码初始化加载样式文件
  2. 解析Cell的时候样式保存在org.ttzero.excel.reader.Cell对象的xf属性中,xf为style index并不是style
  3. 在Row对象中需要增加public int getStyle(){ }方法,调用Styles#getStyleByIndex方法使用第2步的style index换取style
  4. 然后就可以Styles#getNumFmt, Styles#getFill, Styles#getFont, Styles#getBorder 等方法获取具体的样式了

有兴趣的话可以尝试实现xlsx格式的样式解析

请教一下这种用法有问题吗?

image

2259289435 avatar Dec 05 '22 08:12 2259289435

写法是正确的,但是基建好像不对,并没有拿到样式,晚点我尝试下

wangguanquan avatar Dec 05 '22 09:12 wangguanquan

不好意思可能误导你了,因为Styles.load(s);并没有装载到styleIndex,所以styles.getStyleByIndex(cell.xf)返回的是-1,无法拿到样式,需要实现Styles#load方法才可以的

wangguanquan avatar Dec 05 '22 09:12 wangguanquan

不好意思可能误导你了,因为Styles.load(s);并没有装载到styleIndex,所以styles.getStyleByIndex(cell.xf)返回的是-1,无法拿到样式,需要实现Styles#load方法才可以的

Styles#load 要实现才可以,是不是意味着现在没有任何方法可以拿到任何样式? 有什么变通的方案没,希望能尽快拿到 numfmt 跑通业务。

2259289435 avatar Dec 06 '22 01:12 2259289435

我稍后推一个分支获取numfmts

wangguanquan avatar Dec 06 '22 02:12 wangguanquan

int style = styles.of(cell.xf); NumFmt numFmt= styles.getNumFmt(style);

这样写就可以了

wangguanquan avatar Dec 06 '22 02:12 wangguanquan

int style = styles.of(cell.xf); NumFmt numFmt= styles.getNumFmt(style); 这样写就可以了

多谢支持,已经取到了。 int style = styles.of(cell.xf),这里获取的 style 是不是就是完整的 style,只是暂时只有 NumFmt 的位数设置了,其他的还没有实现而没有设置。 我需要做个样式复制的功能,所以如果此 style 代表的就是完整的 style,那就可以直接使用,后续再去补充其他位数的实现。

2259289435 avatar Dec 06 '22 07:12 2259289435

你理解的很正确,这个样式就是全部的样式,只是现在只有numfmt才会放进map。 但是这样样式并不能用于复制,样式需要先addNumFmt进目标styles才会生效,而这个值大概率和原值不同,换句话说你不能直接在目标styles上使用of方法添加新样式

wangguanquan avatar Dec 06 '22 07:12 wangguanquan

继续请教,从 a excel 的单元格获取到多个 style(styles.of(cell.xf)),然后现在需要写 b excel,希望能用从 a 里的 style 直接设置 b。不知道最佳实践如何处理比较好。 或者说这个场景现在框架未考虑,需要去实现?

2259289435 avatar Dec 06 '22 08:12 2259289435

需要完全解析才可以的,当前虽然可以拿到完全的样式值但是各个主体(字体,边框,填充,和格式化)并没有解析,通过当前的style只能得到水平/垂直对其,是否折行。

当然如果目标和原有着相同样式是可以通过of方法复制的,我说的是整个styles.xml完全一样,比如模版

wangguanquan avatar Dec 06 '22 09:12 wangguanquan

今天网络访问不了github,我的建议是如果是模版的话优先使用easyexcel,它有完整的功能,eec有计划支持模版但短时间还无法发布,部分对内存和性能要求的功能可以继续使用eec

wangguanquan avatar Dec 06 '22 09:12 wangguanquan

fix#312分支增增加对样式的解析但并未经过完全测试,你可以fork本项目并切到fix#312分支进行BUG修改,欢迎提交PR。

wangguanquan avatar Dec 06 '22 16:12 wangguanquan

需要完全解析才可以的,当前虽然可以拿到完全的样式值但是各个主体(字体,边框,填充,和格式化)并没有解析,通过当前的style只能得到水平/垂直对其,是否折行。 当然如果目标和原有着相同样式是可以通过of方法复制的,我说的是整个styles.xml完全一样,比如模版

目标是我要生成的一个新文件,在新文件内写入内容,这种目标的styles.xml和模板不相同呀,能请教下你说的模块复制怎么处理吗,我是需要在生成的新文件内完全复用模板的样式。

2259289435 avatar Dec 07 '22 10:12 2259289435

你可以clone本项目并切到fix#312分支,该分支已实现获取单元格样式(row#getStyle ),可以install到本地使用

可以使用如下代码获取单元格样式

reader.sheet(0).reset().dataRows().forEach(row -> {
    Cell cell = row.getCell(3);
    int style = row.getCellStyle(cell);
    NumFmt numFmt = row.getStyles().getNumFmt(style);
    println(numFmt);
    Font font = row.getStyles().getFont(style);
    println(font);
    Fill fill = row.getStyles().getFill(style);
    println(fill);
});

wangguanquan avatar Dec 07 '22 10:12 wangguanquan

如果只是简单字段替换的话可以参照TemplateTest测试类,只是目前还不支持表格,可自行扩展实现

@Test public void testTemplate() throws IOException {
    try (InputStream fis = Files.newInputStream(testResourceRoot().resolve("template.xlsx"))) {
        // Map data
        Map<String, Object> map = new HashMap<>();
        map.put("name", "guanquan.wang");
        map.put("score", 90);
        map.put("date", "2019-05-05");
        map.put("desc", "暑假");

        // java bean
//            BindEntity entity = new BindEntity();
//            entity.score = 67;
//            entity.name = "张三";
//            entity.date = new Date(System.currentTimeMillis());

        new Workbook("模板导出")
            .withTemplate(fis, map)
            .writeTo(defaultTestPath);
    }
}

wangguanquan avatar Dec 08 '22 01:12 wangguanquan

testTemplate

我其实就是想用模板这种,但我的模板只有样式,和你预设的这种场景有点不匹配。

比如模板原来有 a,b 两个 sheet,我可以基于当前这个模板再去创建 c, d, e 三个 sheet,希望在写入 c,d,e三个 sheet 的的cell可以直接用 a,b 两个 sheet 中的样式,写完 c, d, e sheet 后再去删除 a, b 两个sheet。

2259289435 avatar Dec 09 '22 07:12 2259289435

可以加我微信聊吧,听你描述好像并不太难的样子,如果没有敏感信息的话可以将样本和预期的效果发邮箱我详细看下,邮箱地址点我头像查看

wangguanquan avatar Dec 09 '22 08:12 wangguanquan

可以加我微信聊吧,听你描述好像并不太难的样子,如果没有敏感信息的话可以将样本和预期的效果发邮箱我详细看下,邮箱地址点我头像查看

已发示例邮件

2259289435 avatar Dec 12 '22 08:12 2259289435

微信号已在邮箱中回复,如何收件箱没有的话就去垃圾邮件中找找

wangguanquan avatar Dec 12 '22 13:12 wangguanquan

我写了一个示例,读取模板某一行做为范本追写数据

  1. 自定义模板实现
 public static class MyEmbedTemplate<T> extends EmbedTemplate {

    private int sheetId, copyRowNum; // 指定要复制的Sheet和要复制的行(从1开始)
    private final Supplier<List<T>> supplier; // 获取数据用
    private ExtBufferedWriter bw; // 写文件
    private int rowNum; // 记录当前行号
    private TemplateRow tr;

    public MyEmbedTemplate(Path zipPath, Workbook wb, int sheetId, int copyRowNum, Supplier<List<T>> supplier) {
        super(zipPath, wb);
        this.sheetId = sheetId;
        this.copyRowNum = copyRowNum;
        this.supplier = supplier;
        this.rowNum = copyRowNum;
    }

    @Override
    protected int bindSstData() {
        return 0;
    }

    @Override
    protected int bindSheetData() {
        // Read content
        Path contentTypePath = zipPath.resolve("[Content_Types].xml");
        SAXReader reader = new SAXReader();
        Document document;
        try {
            document = reader.read(Files.newInputStream(contentTypePath));
        } catch (DocumentException | IOException e) {
            return 0;
        }

        // Find Override
        List<Element> overrides = document.getRootElement().elements("Override");
        for (Element e : overrides) {
            if (Const.ContentType.SHEET.equals(e.attributeValue("ContentType"))) {
                Path sheetPath = zipPath.resolve(e.attributeValue("PartName").substring(1));
                String sheetId = sheetPath.getFileName().toString();
                sheetId = sheetId.substring(5, sheetId.length() - 4);
                System.out.println("读取 " + sheetId);

                // 只操作第一个Sheet页
                if (sheetId.equals(String.valueOf(this.sheetId))) {
                    try {
                        writeSheet1(sheetPath);
                    } catch (IOException ex) {
                        throw new ExcelWriteException("Write data failed", ex);
                    }
                }
            }
        }
        return 0;
    }

    // Write template sheet, returns rows
    protected int writeSheet1(Path path) throws IOException {
        // 解析模板行
        this.tr = parseTemplateRow(path);

        bw = new ExtBufferedWriter(Files.newBufferedWriter(path, StandardCharsets.UTF_8));

        // 写头部
        bw.write(tr.header);

        // 循环写行数据
        List<T> list = supplier.get();
        if (list != null && !list.isEmpty()) {
            // 解析实体与列进行匹配,这里可以借助于ListSheet
            ListSheet<T> listSheet = new ListSheet<>(list);
            listSheet.setSheetWriter(new XMLWorksheetWriter());
            listSheet.setWorkbook(new Workbook());
            org.ttzero.excel.entity.Column[] columns = listSheet.getAndSortHeaderColumns();
            Cell[] cells = tr.getCells();

            for (org.ttzero.excel.entity.Column col : columns) {
                if (col.getRealColIndex() <= cells.length) {
                    Cell c = cells[col.getRealColIndex() - 1];
                    Class<?> clazz = col.getClazz();
                    if (isString(clazz)) {
                        c.t = Cell.INLINESTR;
                    }
                    else if (isInt(clazz) || isLong(clazz) || isFloat(clazz) || isDouble(clazz) || isBigDecimal(clazz)) {
                        c.t = Cell.NUMERIC;
                    } else c.t = Cell.INLINESTR;
                }
            }
            tr.columns = columns;

            for (T t : list) writeRow(t);
            for (; (list = supplier.get()) != null && !list.isEmpty();)
                for (T t : list) writeRow(t);
        } else bw.write(tr.tmp); // 如果没有数据的话写入原本的模板行

        // 写尾部
        bw.write(tr.tail);
        bw.close();

        return 0;
    }

    private TemplateRow parseTemplateRow(Path path) {
        SAXReader reader = new SAXReader();
        Document document;
        try {
            document = reader.read(Files.newInputStream(path));
        } catch (DocumentException | IOException e) {
            throw new ExcelWriteException("Miss the template Worksheet " + path);
        }

        List<Element> rows = document.getRootElement().element("sheetData").elements("row");
        if (rows.size() < copyRowNum) {
            throw new ExcelWriteException("Specify row " + copyRowNum + " is large than template sheet rows " + rows.size());
        }

        Element row = rows.get(copyRowNum - 1);
        String span = Styles.getAttr(row, "spans");
        String customHeight = Styles.getAttr(row, "customHeight"), ht = Styles.getAttr(row, "ht"); // 如果customHeight为1或者true则ht属性生效
        String customFormat = Styles.getAttr(row, "customFormat"), rs = Styles.getAttr(row, "s"); // 如果customFormat为1或者true则rs属性生效

        int i = span.indexOf(':');
        int[] spans = i > 0 ? new int[]{Integer.parseInt(span.substring(0, i)), Integer.parseInt(span.substring(i + 1))} : new int[]{0, Integer.parseInt(span)};
        int[] xfs = new int[spans[1]];
        List<Element> list = row.elements();
        for (Element c : list) {
            String cr = Styles.getAttr(c, "r"), cs = Styles.getAttr(c, "s");
            long r = ExcelReader.cellRangeToLong(cr);
            int ci = (int) (r & 0x7FFF), s = StringUtil.isNotEmpty(cs) ? Integer.parseInt(cs) : 0;
            xfs[ci - 1] = s;
        }

        String sheetData;
        try {
            InputStream is = Files.newInputStream(path);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int n;
            byte[] bytes = new byte[1 << 11];
            while ((n = is.read(bytes)) > 0) {
                bos.write(bytes, 0, n);
            }
            sheetData = new String(bos.toByteArray(), 0, bos.size(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new ExcelWriteException("");
        }

        for (int n = 1; n <= copyRowNum; n++) i = sheetData.indexOf("<row ", i + 1);
        int j = sheetData.indexOf("</row>", i) + 6;
        String header = sheetData.substring(0, i), tmp = sheetData.substring(i, j), tail = sheetData.substring(j);

        TemplateRow tr = new TemplateRow();
        tr.header = header;
        tr.tail = tail;
        tr.tmp = tmp;
        tr.ht = ht;
        tr.customHeight = ("1".equals(customHeight) || "true".equalsIgnoreCase(customHeight)) && StringUtil.isNotEmpty(ht);
        tr.rs = rs;
        tr.customFormat = ("1".equals(customFormat) || "true".equalsIgnoreCase(customFormat)) && StringUtil.isNotEmpty(rs);
        tr.calloc(spans[1]); // 分配列
        Cell[] cells = tr.getCells();
        int col = 0;
        for (Cell c : cells) {
            c.i = (short) ++col;
            c.xf = xfs[col - 1]; // Style index
        }

        return tr;
    }

    private void writeRow(T o) throws IOException {
        Cell[] cells = tr.getCells();
        org.ttzero.excel.entity.Column[] columns = tr.columns;
        int len = cells.length;

        // 写行
        startRow(len);
        try {
            boolean notNull = o != null;
            for (int i = 0; i < columns.length; i++) {
                Object e;
                ListSheet.EntryColumn column = (ListSheet.EntryColumn) columns[i];
                // Clear cells
                Cell cell = cells[column.getRealColIndex() - 1];
                if (column.isIgnoreValue())
                    e = null;
                else if (notNull) {
                    if (column.getMethod() != null)
                        e = column.getMethod().invoke(o);
                    else if (column.getField() != null)
                        e = column.getField().get(o);
                    else e = o;
                }
                else e = null;

                if (e != null) {
                    if (cell.t == Cell.NUMERIC) {
                        writeNumeric(Long.parseLong(e.toString()), rowNum, column.getRealColIndex(), cell.xf);
                    } else {
                        writeString(e.toString(), rowNum, column.getRealColIndex(), cell.xf);
                    }
                } else writeNull(rowNum, column.getRealColIndex(), cell.xf);
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new ExcelWriteException(e);
        }

        // 行尾
        endRow();
    }

    protected int startRow(int columns) throws IOException {
        bw.write("<row r=\"");
        bw.writeInt(rowNum);
        // default data row height 16.5
        bw.write("\" spans=\"1:");
        bw.writeInt(columns);
        if (tr.customHeight) {
            bw.write("\" ht=\"");
            bw.write(tr.ht);
        }
        if (tr.customFormat) {
            bw.write("\" s=\"");
            bw.write(tr.rs);
        }
        bw.write("\">");
        return rowNum;
    }

    protected void endRow() throws IOException {
        rowNum++; // 行+1
        bw.write("</row>");
    }

    protected void writeNumeric(long l, int row, int col, int xf) throws IOException {
        bw.write("<c r=\"");
        bw.write(int2Col(col));
        bw.writeInt(row);
        bw.write("\" s=\"");
        bw.writeInt(xf);
        bw.write("\"><v>");
        bw.write(l);
        bw.write("</v></c>");
    }

    protected void writeNull(int row, int col, int xf) throws IOException {
        bw.write("<c r=\"");
        bw.write(int2Col(col));
        bw.writeInt(row);
        bw.write("\" s=\"");
        bw.writeInt(xf);
        bw.write("\"/>");
    }

    protected void writeString(String s, int row, int col, int xf) throws IOException {
        // The limit characters per cell check
        if (s != null && s.length() > Const.Limit.MAX_CHARACTERS_PER_CELL) {
            throw new ExcelWriteException("Characters per cell out of limit. size=" + s.length()
                + ", limit=" + Const.Limit.MAX_CHARACTERS_PER_CELL);
        }
        bw.write("<c r=\"");
        bw.write(int2Col(col));
        bw.writeInt(row);
        if (StringUtil.isEmpty(s)) {
            bw.write("\" s=\"");
            bw.writeInt(xf);
            bw.write("\"/>");
        } else {
            bw.write("\" t=\"inlineStr\" s=\"");
            bw.writeInt(xf);
            bw.write("\"><is><t>");
            bw.escapeWrite(s); // escape text
            bw.write("</t></is></c>");
        }
    }

    public static class TemplateRow extends Row {
        public String header, tail, tmp;
        public String ht, rs;
        public boolean customHeight, customFormat;
        public org.ttzero.excel.entity.Column[] columns;
    }
}
  1. 指定需要复制的worksheet的列
@Test public void testTemplate312() throws IOException {
    try (InputStream fis = Files.newInputStream(Paths.get("template.xlsx"))) {
        Workbook workbook = new Workbook("Issue#312").withTemplate(fis, new Object()); // 这里不需要指定数据

        // 查询条件
        GoodsQueryVo vo = new GoodsQueryVo();
        vo.setPage(1);
        vo.setPageSize(100);

        workbook.setWorkbookWriter(new XMLWorkbookWriter() {
            @Override
            public Path template() throws IOException {
                // Store template stream as zip file
                Path temp = FileUtil.mktmp(Const.EEC_PREFIX);
                ZipUtil.unzip(workbook.getTemplate(), temp);

                // Bind data  其中参数1表示第一个Sheet页 4表示第4行做为模板行
                EmbedTemplate bt = new MyEmbedTemplate<>(temp, workbook, 1, 4, () -> {
                    List<Goods> goods = goodsService.query(vo); // 查询数据
                    vo.setPage(vo.getPage() + 1); // 页码+1
                    return goods;
                });

                if (bt.check()) { // Check files
                    bt.bind(workbook.getBind());
                }

                // Zip compress
                Path zipFile = ZipUtil.zipExcludeRoot(temp, temp);

                // Delete source files
                FileUtil.rm_rf(temp.toFile(), true);

                // Close shared string table
                workbook.getSst().close();

                return zipFile;
            }
        }).writeTo(defaultTestPath);
    }
}

如果想要使用其它单元格的样式,可以通过ExcelReader读取拿到Cell#xf,写数据的时候使用该xf即可。

wangguanquan avatar Dec 14 '22 07:12 wangguanquan

导出的对象字段上需要添加@ExcelColumn(colIndex = x) 顺序与模板一样

wangguanquan avatar Dec 14 '22 08:12 wangguanquan

刚刚看到这个问题,刚好我写过应该能解决你的问题,希望对你有帮助,以下代码是完整复刻sheet,即完整复制样式

//样式类 @NoArgsConstructor @Data @AllArgsConstructor public class MyStyleProcessor implements StyleProcessor { List<CellStyle> cellStyles; @Override public int build(Object o, int style, Styles styles) { CellStyle cellStyle = cellStyles.get(0); cellStyles.remove(0); return cellStyle.getStyle(style,styles); }

}

//样式类 @Data @Accessors(chain = true) @NoArgsConstructor public class CellStyle {

public CellStyle(String name,int style,Styles styles){
    // 获取字体
    this.font = styles.getFont(style);
    // 获取边框
    this.border = styles.getBorder(style);
    // 获取填充
    this.fill = styles.getFill(style);
    // 获取格式化
    this.numFmt = styles.getNumFmt(style);
    this.horizontal = styles.getHorizontal(style);
    this.vertica = styles.getVertical(style);
}

public int getStyle(int style,Styles styles){
    styles.clearFont(style);
    styles.clearFill(style);
    styles.clearBorder(style);

    if (numFmt==null){
        return  styles.addFont(font)|styles.addBorder(border)|styles.addFill(fill)|Horizontals.CENTER|Verticals.CENTER;
    }
    styles.clearNumFmt(style);
    return  styles.addNumFmt(numFmt)|styles.addFont(font)|styles.addBorder(border)|styles.addFill(fill)|Horizontals.CENTER|Verticals.CENTER;
}

private String name;
private Font font;
private Border border;
private  Fill fill;
private  NumFmt numFmt;
private  int horizontal = Horizontals.CENTER;
private  int vertica = Verticals.CENTER;

}

//核心代码 public static void main(String[] args) {

    Path path = Paths.get(你的模板excel);
    try (ExcelReader reader = ExcelReader.read(path,COPY_ON_MERGED)) {

        Workbook wb = new Workbook();

        ArrayList<Map<String,?>> objects = new ArrayList<>();

        List<Column> columns = new ArrayList<>(); //每一列
        List<List<CellStyle>> cellStylesList = new ArrayList<>(); //每一列
        Sheet sheet = reader.sheet(0); //MergeSheet
        List<Dimension> mergeCells = getMergeCells(sheet);


        reader.sheet(0).rows().forEach(row -> {
            HashMap<String, String> dataMap = new HashMap<>();

            for (int i = columns.size(); i < row.getLastColumnIndex(); i++) { //创建列
                ArrayList<CellStyle> cellStyles = new ArrayList<>();
                columns.add(new Column(i+"",i+"").setStyleProcessor(new MyStyleProcessor(cellStyles)));
                cellStylesList.add(cellStyles);
            }

            for (int i = 0; i < row.getLastColumnIndex(); i++) { //行的每一格
                Cell cell = row.getCell(i);
                String name = row.getString(cell);
                dataMap.put(i+"", StringUtils.isNoneBlank(name)? name:"");
                // 第一步 获取Styles对象
                Styles styles = row.getStyles();
                // 第二步 获取指定单元格样式
                int style = row.getCellStyle(cell);
                cellStylesList.get(i).add(new CellStyle(name,style,styles));
            }
            objects.add(dataMap);
        });

        ListMapSheet tmpSheet = new ListMapSheet("sheet", objects, columns.toArray(new Column[columns.size()]));
        tmpSheet.ignoreHeader();
        tmpSheet.putExtProp(Const.ExtendPropertyKey.MERGE_CELLS, mergeCells);
        wb.addSheet(tmpSheet);
        wb.writeTo(new File(excel输出路径));


    } catch (IOException e) {
        e.printStackTrace();
    }
}
public static  List<Dimension>  getMergeCells(Object o){ //获取合并格
    try {
        Method[] methods = ReflectUtils.getMethods(o.getClass()); //TODO 注意这里要使用 getDeclaredMethods,工具类使用的是hutool
        Method method = Arrays.stream(methods).filter(m -> m.getName().equals("getMergeCells")).findFirst().get();
        method.setAccessible(true);
        return (List<Dimension>) method.invoke(o);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

zhangmuto avatar Mar 23 '23 12:03 zhangmuto

@zhangmuto 获取哪些单元格被合并了可以通过MergeSheet#getMergeCells直接获取,像这样List<Dimension> mergeCells = reader.sheet(0).asMergeSheet().getMergeCells();,不需要反射遍历。

像上面的代码你读取时候已经指定了COPY_ON_MERGED属性,所以调用asMergedSheet并不会增加额外开销,这里使用类型强转也可以,如果未指定COPY_ON_MERGED属性调用asMergedSheet方法会跳到尾部读取合并信息,所以asMergeSheet方法是安全的。

wangguanquan avatar Mar 23 '23 13:03 wangguanquan