Java 实现 Excel 与 Markdown 相互转换:表格导出、格式设置与多表提取

在开发中,我们经常需要在 Excel 和 Markdown 之间转换表格数据。例如,将 Excel 中的统计结果导出为 Markdown,用于 README、技术文档或项目报告;或者将 Markdown 文档中的表格导入 Excel,以便进一步编辑、筛选和分析。对于需要批量处理或定期更新的文件,通过 Java 自动完成转换可以减少手动操作,提高处理效率。

本文将介绍如何使用 Java 实现 Excel 与 Markdown 双向转换,主要包括:

  • 将 Excel 工作簿转换为 Markdown 文件
  • 设置 Markdown 导出时的图片路径和超链接格式
  • 将 Markdown 文件转换为 Excel 工作簿
  • 提取 Markdown 文档中的多个表格,并分别保存到不同的 Excel 工作表

一、准备开发环境

本文使用 Spire.XLS for Java 处理 Excel 文件。该库支持独立读取、编辑和转换 Excel,无需安装 Microsoft Office。

Excel 与 Markdown 转换功能从 15.11.3 版本开始支持,本文使用 16.10.1 版本。

在 Maven 项目的 pom.xml 中添加以下配置:

xml 复制代码
<repositories>
    <repository>
        <id>com.e-iceblue</id>
        <name>e-iceblue</name>
        <url>https://repo.e-iceblue.com/nexus/content/groups/public/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>e-iceblue</groupId>
        <artifactId>spire.xls</artifactId>
        <version>16.10.1</version>
    </dependency>
</dependencies>

二、使用 Java 将 Excel 转换为 Markdown

Spire.XLS for Java 支持将 Excel 工作簿直接导出为 Markdown 文件。使用 Workbook.loadFromFile() 加载 Excel 文件后,调用 saveToFile() 方法,并将输出格式指定为 FileFormat.Markdown 即可完成转换。

实现代码:

java 复制代码
import com.spire.xls.Workbook;
import com.spire.xls.FileFormat;

public class ExcelToMarkdown {
    public static void main(String[] args) {

        Workbook workbook = new Workbook();

        try {
            // 加载 Excel 文件
            workbook.loadFromFile("report.xlsx");

            // 导出为 Markdown
            workbook.saveToFile("report.md", FileFormat.Markdown);

        } finally {
            workbook.dispose();
        }
    }
}

运行后,将生成 report.md 文件。假设原始 Excel 包含一张部门工作完成情况表,转换后的 Markdown 内容如下:

markdown 复制代码
| 部门 | 负责人 | 完成率 |
| --- | --- | --- |
| 技术部 | 张明 | 92% |
| 市场部 | 李华 | 88% |
| 客服部 | 王芳 | 95% |

需要注意的是,Markdown 不支持 Excel 的公式计算、合并单元格及复杂样式。如果原始工作簿包含这些内容,建议检查导出后的数据和表格结构是否符合预期。

三、自定义 Excel 转 Markdown 的图片和超链接格式

如果 Excel 文件包含图片或超链接,除了表格数据本身,还需要考虑这些内容在 Markdown 中的保存方式。

例如,将生成的 Markdown 文件上传到 GitHub 时,通常希望图片使用相对路径,以便整个文档目录可以一起移动。对于超链接,则可以选择行内链接或引用式链接。

Spire.XLS for Java 提供了 MarkdownOptions 类,用于控制这些导出选项。

其中两个比较实用的方法是:

方法 作用
isSavePicInRelativePath(true) 使用相对路径保存图片引用,例如 ![图片](images/pic.png)
isSaveHyperlinkAsRef(true) 将超链接保存为引用式 Markdown 链接

例如,Markdown 中的行内链接通常是:

markdown 复制代码
[项目官网](https://www.example.com)

设置 isSaveHyperlinkAsRef(true) 后,可以使用类似下面的引用式格式:

markdown 复制代码
[项目官网][ref1]

[ref1]: https://www.example.com

以下代码演示如何让图片使用相对路径,同时将超链接保存为行内格式:

java 复制代码
import com.spire.xls.Workbook;
import com.spire.xls.MarkdownOptions;

public class ExcelToMarkdownWithOptions {
    public static void main(String[] args) {

        Workbook workbook = new Workbook();

        try {
            workbook.loadFromFile("report.xlsx");

            // 配置 Markdown 导出选项
            MarkdownOptions options = new MarkdownOptions();
            options.isSavePicInRelativePath(true);
            options.isSaveHyperlinkAsRef(false);

            // 使用指定选项导出 Markdown
            workbook.saveToMarkdown("report_custom.md", options);

        } finally {
            workbook.dispose();
        }
    }
}

与上一节不同,这里使用的是 saveToMarkdown() 方法,并传入 MarkdownOptions 对象。

如果希望使用引用式链接,只需将 isSaveHyperlinkAsRef(false) 改为 true。

对于需要发布到 GitHub 或静态文档网站的文件,图片相对路径通常更方便管理。不过,发布时仍需保留导出的图片资源,并确保图片与 Markdown 文件之间的相对目录结构没有变化。

四、使用 Java 将 Markdown 转换为 Excel

除了将 Excel 导出为 Markdown,有时还需要将 Markdown 文档中的表格重新导入 Excel。

将 Markdown 转换为 Excel的流程同样简单,使用 Workbook.loadFromMarkdown() 方法直接加载 Markdown 文件,再通过 saveToFile() 将其保存为 Excel。

实现代码:

java 复制代码
import com.spire.xls.Workbook;
import com.spire.xls.ExcelVersion;

public class MarkdownToExcel {
    public static void main(String[] args) {

        Workbook workbook = new Workbook();

        try {
            // 加载 Markdown 文件
            workbook.loadFromMarkdown("tasks.md");

            // 保存为 Excel 工作簿
            workbook.saveToFile(
                    "tasks.xlsx",
                    ExcelVersion.Version2016
            );

        } finally {
            workbook.dispose();
        }
    }
}

假设 tasks.md 包含以下任务表格:

markdown 复制代码
| 任务 | 负责人 | 状态 |
| --- | --- | --- |
| 接口开发 | 张明 | 已完成 |
| 页面优化 | 李华 | 进行中 |
| 功能测试 | 王芳 | 待开始 |

执行程序后,将生成 tasks.xlsx,可以在 Excel 中查看导入的内容,并进一步编辑或处理数据。

需要留意的是,Markdown 本身不包含 Excel 的单元格类型信息。转换后,如果需要对数字、日期等数据进行计算,建议检查单元格是否被正确识别为数值或日期,而不是普通文本。

五、提取 Markdown 中的多个表格并保存到不同的 Excel 工作表

直接使用 loadFromMarkdown() 适合整体转换文件。但在一些项目中,我们可能只需要 Markdown 文档里的表格,而不需要其他说明文字。

例如,一份销售报告按地区记录了多个表格:

markdown 复制代码
# 区域销售报告

## 华东地区

| 产品 | 销量 | 销售额 |
| --- | ---: | ---: |
| 产品A | 120 | 18000 |
| 产品B | 85 | 12750 |

## 华南地区

| 产品 | 销量 | 销售额 |
| --- | ---: | ---: |
| 产品A | 96 | 14400 |
| 产品B | 110 | 16500 |

如果希望将华东、华南两个地区的数据分别写入 Excel 工作表,可以先解析 Markdown 表格,再通过 Spire.XLS for Java 创建工作表并写入数据。

下面的示例使用 Java 标准库读取 Markdown 文件,识别表头和分隔行,并将每个表格保存到独立的工作表中。

Java 实现代码

java 复制代码
import com.spire.xls.Workbook;
import com.spire.xls.Worksheet;
import com.spire.xls.ExcelVersion;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class MarkdownTablesToExcel {

    public static void main(String[] args) throws Exception {

        // 读取 Markdown 文件
        List<String> lines = Files.readAllLines(
                Paths.get("report.md"), StandardCharsets.UTF_8);

        Workbook workbook = new Workbook();
        int tableCount = 0;
        boolean inCodeBlock = false;
        char fenceChar = '\0';
        int fenceLength = 0;

        try {
            workbook.getWorksheets().clear();

            for (int i = 0; i < lines.size() - 1; i++) {
                String line = lines.get(i).trim();

                // 跳过 Markdown 代码块
                if (inCodeBlock) {
                    int length = countLeading(line, fenceChar);
                    if (length >= fenceLength
                            && line.substring(length).trim().isEmpty()) {
                        inCodeBlock = false;
                    }
                    continue;
                }

                int backticks = countLeading(line, '`');
                int tildes = countLeading(line, '~');

                if (backticks >= 3 || tildes >= 3) {
                    fenceChar = backticks >= 3 ? '`' : '~';
                    fenceLength = Math.max(backticks, tildes);
                    inCodeBlock = true;
                    continue;
                }

                if (!isTableRow(line)) {
                    continue;
                }

                List<String> headers = splitCells(line);
                List<String> separators =
                        splitCells(lines.get(i + 1).trim());

                // 检查表头和分隔行
                if (headers.isEmpty()
                        || headers.size() != separators.size()
                        || !isSeparator(separators)) {
                    continue;
                }

                // 为每个表格创建工作表
                Worksheet sheet = workbook.getWorksheets()
                        .add("表格" + (++tableCount));

                // 写入表头并设置加粗
                for (int col = 0; col < headers.size(); col++) {
                    sheet.getCellRange(1, col + 1)
                            .setText(headers.get(col));

                    sheet.getCellRange(1, col + 1)
                            .getStyle().getFont().isBold(true);
                }

                // 写入表格数据
                int row = 2;
                int j = i + 2;

                while (j < lines.size()
                        && isTableRow(lines.get(j).trim())) {

                    List<String> cells =
                            splitCells(lines.get(j).trim());

                    if (cells.size() != headers.size()) {
                        break;
                    }

                    for (int col = 0; col < cells.size(); col++) {
                        sheet.getCellRange(row, col + 1)
                                .setText(cells.get(col));
                    }

                    row++;
                    j++;
                }

                // 自动调整列宽
                sheet.getAllocatedRange().autoFitColumns();

                i = j - 1;
            }

            if (tableCount == 0) {
                System.out.println("未找到 Markdown 表格。");
                return;
            }

            // 保存 Excel 工作簿
            workbook.saveToFile(
                    "tables.xlsx", ExcelVersion.Version2016);

            System.out.println("已提取 " + tableCount + " 个表格。");

        } finally {
            workbook.dispose();
        }
    }

    // 统计行首连续出现的指定字符
    private static int countLeading(String line, char marker) {
        int i = 0;
        while (i < line.length() && line.charAt(i) == marker) {
            i++;
        }
        return i;
    }

    // 判断是否为带首尾竖线的表格行
    private static boolean isTableRow(String line) {
        return line.startsWith("|") && line.endsWith("|");
    }

    // 验证 Markdown 表格分隔行
    private static boolean isSeparator(List<String> cells) {
        if (cells.isEmpty()) {
            return false;
        }

        for (String cell : cells) {
            if (!cell.matches(":?-+:?")) {
                return false;
            }
        }
        return true;
    }

    // 拆分表格单元格
    private static List<String> splitCells(String line) {

        List<String> cells = new ArrayList<>();

        if (!isTableRow(line)) {
            return cells;
        }

        String content = line.substring(1, line.length() - 1);
        StringBuilder cell = new StringBuilder();

        for (int i = 0; i < content.length(); i++) {
            char ch = content.charAt(i);

            // 处理转义竖线 \|
            if (ch == '\\' && i + 1 < content.length()
                    && content.charAt(i + 1) == '|') {
                cell.append('|');
                i++;
            } else if (ch == '|') {
                cells.add(cell.toString().trim());
                cell.setLength(0);
            } else {
                cell.append(ch);
            }
        }

        cells.add(cell.toString().trim());
        return cells;
    }
}

实现说明

这段代码首先跳过 Markdown 代码块,然后根据表头和分隔行识别表格。例如:

text 复制代码
| --- | ---: | :---: |

识别到表格后,程序通过 splitCells() 拆分各列,并使用 getWorksheets().add() 创建工作表,再通过 setText() 写入数据。最后调用 autoFitColumns() 自动调整列宽。

对于前面的地区销售报告,生成的 tables.xlsx 将包含两个工作表:

  • 表格1:华东地区的产品销售数据
  • 表格2:华南地区的产品销售数据

适用范围: 该示例适用于结构规则、带首尾竖线的 Markdown 表格,能够识别常见的表格分隔行、简单的转义竖线,并跳过使用反引号或波浪线围栏的普通代码块。对于复杂转义、行内代码中的竖线或嵌套结构等情况,建议使用专门的 Markdown 解析库。

此外,示例使用 setText() 写入数据,单元格内容会按文本处理。如果需要对销量、销售额等字段进行计算,可以根据数据类型使用 setNumberValue() 写入数值。

六、转换时需要注意的几个问题

1. Excel 转 Markdown 后,公式和单元格格式能否保留?

Markdown 不支持 Excel 的公式计算、条件格式及复杂单元格样式,因此转换后无法完整保留这些功能。

对于包含公式的工作簿,建议在转换前确认公式结果已更新;对于包含复杂格式的表格,则需要检查导出的 Markdown 是否符合预期。

2. 为什么转换后的 Markdown 表格显示不正常?

通常需要检查表格结构和目标 Markdown 渲染器的支持情况。

例如,Excel 中的合并单元格、多层表头或复杂排版无法直接对应标准 Markdown 表格。对于这些内容,可以先在 Excel 中整理成规则的数据区域,再执行转换。

3. Markdown 转 Excel 后,数字为什么可能无法参与计算?

如果数字被写入为文本,可能会影响 Excel 中的求和、统计或其他数值计算。

例如,使用 Spire.XLS for Java 写入单元格时,可以根据数据类型选择不同的方法:

java 复制代码
// 写入文本
sheet.getCellRange("A1").setText("1200");

// 写入数值
sheet.getCellRange("A2").setNumberValue(1200);

如果后续需要计算销售额、平均值或生成图表,建议在导入数据时,根据字段类型分别写入文本、数值和日期。

总结

在 Java 项目中实现 Excel 与 Markdown 互转,可以减少人工复制表格和重新整理数据的工作量。

本文介绍了如何使用 Spire.XLS for Java 完成 Excel 与 Markdown 的双向转换,以及如何自定义图片路径和超链接的导出格式。此外,还演示了如何提取 Markdown 文档中的多个表格,并将其分别保存到 Excel 工作表中。实际开发中,可以根据需求选择直接转换整个文件,或提取指定的表格数据进行处理。

相关推荐
zjx23881 小时前
java很老的web工程重构,加入详细异常链信息分析
java·重构
山东孙燕姿1 小时前
设备台账重构:从一张表到三层模型
android·java·网络·低代码·重构·rxjava
萧瑟余晖1 小时前
Spring缓存抽象详解
java·spring·缓存
贾伟康1 小时前
【共创稿事节】一个 UserDataManager 管八类数据:Preferences 持久化的克制用法
java·服务器·前端·harmonyos·arkts·鸿蒙
卓豪终端管理2 小时前
【无标题】
java·服务器·前端
智_永无止境2 小时前
又找到一个纯Java的OCR项目,识别的格式还挺多
java·开发语言·ocr
学编程就要猛2 小时前
MCP(Model Context Protocol)
java·springai
用户072367066602 小时前
Apollo 配置热更新落地:让 Spring Boot @ConfigurationProperties Bean 不重启即时生效
java
2601_970703832 小时前
中国除湿机与湿度控制行业研究报告
java·大数据·人工智能