标签 编程教程 下的文章

在日常企业办公和数据分析中,表格数据的可视化和文档化非常常见。无论是产品销售报表、库存清单,还是项目进度表,通常都会希望将数据直接导出为 Word 文档,以便打印、归档或分发。手动复制粘贴不仅效率低,而且容易出错。借助 C#,我们可以轻松将 DataTable 数据生成格式规范、可自定义样式的 Word 表格,实现自动化办公。

本文将带你完整了解从创建 Word 文档、构建表格、填充数据到保存文档的流程,并重点讲解核心技术细节和关键 API 使用方式。

文中使用的方法需要用到 Free Spire.Doc for .NET,可通过 NuGet 安装:dotnet add package FreeSpire.Doc


核心流程与实现

导出 DataTable 到 Word 文档的流程主要包括以下几个步骤:

  1. 创建 Word 文档对象及章节
  2. 添加文档标题
  3. 校验 DataTable 数据
  4. 构建 Word 表格并设置样式
  5. 填充表头与数据
  6. 保存文档

下面给出完整示例代码(已优化结构和示例数据):

using System;
using System.Data;
using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.Drawing;

public class DataTableToWordExporter
{
    public static void ExportDataTableToWord(DataTable dataTable, string filePath)
    {
        // 1. 创建 Word 文档
        Document document = new Document();
        Section section = document.AddSection();

        // 2. 添加文档标题
        Paragraph titlePara = section.AddParagraph();
        titlePara.Format.HorizontalAlignment = HorizontalAlignment.Center;
        TextRange titleText = titlePara.AppendText("月度产品库存报表");
        titleText.CharacterFormat.FontSize = 20;
        titleText.CharacterFormat.Bold = true;

        // 添加空行
        section.AddParagraph().AppendText(Environment.NewLine);

        // 3. 校验 DataTable 数据
        if (dataTable == null || dataTable.Rows.Count == 0)
        {
            section.AddParagraph().AppendText("当前没有可用数据。");
            document.SaveToFile(filePath, FileFormat.Docx);
            Console.WriteLine("数据为空,文档已保存。");
            return;
        }

        // 4. 创建 Word 表格
        Table table = section.AddTable(true);
        table.ResetCells(dataTable.Rows.Count + 1, dataTable.Columns.Count);

        // 设置表格整体样式
        table.TableFormat.Borders.LineWidth = 1;
        table.TableFormat.Borders.BorderType = BorderStyle.Single;
        table.TableFormat.Borders.Color = Color.Black;
        table.PreferredWidth = new PreferredWidth(WidthType.Percentage, 100);
        table.TableFormat.HorizontalAlignment = RowAlignment.Center;

        // 5. 填充表头
        TableRow headerRow = table.Rows[0];
        headerRow.IsHeader = true;
        headerRow.RowFormat.BackColor = Color.LightGray;
        headerRow.RowFormat.Height = 25;
        headerRow.RowFormat.HeightType = TableRowHeightType.Exactly;

        for (int i = 0; i < dataTable.Columns.Count; i++)
        {
            headerRow.Cells[i].CellFormat.VerticalAlignment = VerticalAlignment.Middle;
            Paragraph p = headerRow.Cells[i].AddParagraph();
            p.Format.HorizontalAlignment = HorizontalAlignment.Center;
            TextRange tr = p.AppendText(dataTable.Columns[i].ColumnName);
            tr.CharacterFormat.Bold = true;
            tr.CharacterFormat.FontSize = 11;
        }

        // 6. 填充数据行
        for (int r = 0; r < dataTable.Rows.Count; r++)
        {
            TableRow dataRow = table.Rows[r + 1];
            dataRow.RowFormat.Height = 20;
            dataRow.RowFormat.HeightType = TableRowHeightType.Exactly;

            for (int c = 0; c < dataTable.Columns.Count; c++)
            {
                dataRow.Cells[c].CellFormat.VerticalAlignment = VerticalAlignment.Middle;
                Paragraph p = dataRow.Cells[c].AddParagraph();
                p.Format.HorizontalAlignment = HorizontalAlignment.Center;
                TextRange tr = p.AppendText(dataTable.Rows[r][c].ToString());
                tr.CharacterFormat.FontSize = 10;
            }
        }

        // 7. 保存文档
        try
        {
            document.SaveToFile(filePath, FileFormat.Docx);
            Console.WriteLine($"DataTable 已成功导出到 Word 文档:{filePath}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"导出 Word 文档时发生错误:{ex.Message}");
        }
    }

    public static void Main()
    {
        // 模拟 DataTable 数据
        DataTable dt = new DataTable("Products");
        dt.Columns.Add("产品ID", typeof(int));
        dt.Columns.Add("产品名称", typeof(string));
        dt.Columns.Add("类别", typeof(string));
        dt.Columns.Add("单价", typeof(decimal));
        dt.Columns.Add("库存量", typeof(int));

        dt.Rows.Add(201, "激光打印机", "办公设备", 3200.00m, 25);
        dt.Rows.Add(202, "办公桌椅套装", "家具", 1800.00m, 15);
        dt.Rows.Add(203, "液晶显示器", "显示设备", 1500.00m, 40);
        dt.Rows.Add(204, "无线键鼠套装", "外设", 250.00m, 100);
        dt.Rows.Add(205, "移动硬盘", "存储设备", 480.00m, 60);

        string outputPath = "ProductInventoryReport.docx";
        ExportDataTableToWord(dt, outputPath);

        // 测试空数据情况
        DataTable emptyDt = new DataTable("Empty");
        emptyDt.Columns.Add("ID");
        ExportDataTableToWord(emptyDt, "EmptyReport.docx");
    }
}

以下是上面代码生成的Word文档:

C#导出DataTable到Word结果文档


核心技术解析

在这个示例中,最关键的技术点如下:

  1. Word 文档对象与章节
    Document document = new Document();
    Section section = document.AddSection();
    使用 Document 对象创建新文档,Section 提供页布局和内容容器。
  2. 表格创建与单元格操作
    Table table = section.AddTable(true);
    table.ResetCells(rows, columns);
    表格的行列数量与 DataTable 对应,单元格填充通过 AddParagraph() + AppendText() 实现。
  3. 表头样式设置
    通过 RowFormat.BackColorRowFormat.HeightTextRange.CharacterFormat 设置字体加粗、字号和单元格背景色,使表格专业美观。
  4. 数据填充与居中对齐
    利用循环遍历 DataTable.RowsDataTable.Columns,将数据逐行写入 Word 单元格,并使用 HorizontalAlignment.CenterVerticalAlignment.Middle 保持表格整齐。
  5. 空数据处理
    在 DataTable 无数据时提供提示并仍保存文档,保证程序稳健性。

核心 API 总结

类 / 属性 / 方法说明
DocumentWord 文档对象,可添加 Section、表格、段落等
Section文档章节容器,承载段落和表格
Section.AddParagraph()添加段落
Section.AddTable(bool)添加表格,参数表示是否自动适应页面宽度
Table.ResetCells(rows, cols)重置表格行列数量
TableRow表格行对象,可设置高度、背景色
TableRow.Cells单元格集合
Paragraph段落对象,可添加文本
Paragraph.AppendText(string)向段落添加文本
TextRange.CharacterFormat设置字体、字号、加粗等文本样式
CellFormat单元格格式,包括垂直对齐等
HorizontalAlignment / VerticalAlignment文本水平/垂直对齐方式
Document.SaveToFile()保存文档,支持 DOCX、PDF 等格式

总结

本文展示了如何使用 C#DataTable 数据导出为 Word 文档,实现表格化展示与自动排版。通过 Spire.Doc,你不仅可以轻松创建文档和章节,还能自动生成格式规范的表格,同时处理空数据情况,保证程序运行的稳健性。在表头样式和数据对齐的控制下,导出的文档既美观又易于阅读。掌握这些技术后,你可以将数据库或 Excel 中的业务数据快速转换为 Word 报表,大幅减少手动操作的时间,同时在企业报表自动化、数据归档和文档生成等场景中提升工作效率和专业性。

更多 Word 文档处理技巧请前往 Spire.Doc 文档中心查看。

GMI Cloud Inference Engine 是全球 AI 模型统一接入与在线使用的“高性能推理引擎平台”,底层搭载 H100/H200 芯片,集成全球近百个最前沿的大语言模型和视频生成模型,如 Gemini、Claude、Minimax、DeepSeek、GPT、Qwen、Kling 等,为 AI 开发者与企业提供速度更快、质量更高的模型服务。

欢迎来到!🎉🎉🎉

GMI Cloud Inference Engine AI 场景实践案例集【AI Coding 篇】之二。

**本期任务目标:**在 Windows 终端里,使用 Claude Code 命令行工具,连接 GMI Cloud Inference Engine 的 MiniMax 模型 API。

Claude Code 是 Anthropic 推出的命令行 AI 编程工具,基于 Claude 大模型,可在终端 / IDE 中用自然语言交互,深度理解代码库,支持跨文件编辑、Git 协作。其具有 agent 优势,与超大上下文+多文件编辑+终端原生+安全自主执行+顶级模型能力,在处理大型项目、复杂重构和企业级开发时展现出明显优势。

本文将以接入 Inference Engine 中的 MiniMax-M2 api 为例,详细讲解在 Claude Code 中接入 api 的过程。Token福利文末自行领取!!

MiniMax-M2 界面:

https://console.gmicloud.ai/playground/llm/minimax-m2/bbfb2cb...

01

准备工作

Get ready?

确保你已经掌握 AI Coding 基础知识,没有可看上一篇:

附上链接~

Kooty,公众号:GMI Cloud 黑板报小白友好教程!如何在 Cursor 接入 GMI Cloud 的 API

确保你的电脑已经安装了:

  • Python (为了运行 LiteLLM)
  • Node.js (为了运行 Claude Code)

02

接入步骤

API Connection Guide

步骤 1:安装必要工具

打开 PowerShell,依次运行以下命令:

1.安装 Claude Code 工具

npm install -g @anthropic-ai/claude-code

2.安装 LiteLLM(带代理功能)


# 注意加上引号,因为[proxy]是特殊字符 
pip install "litellm[proxy]"

如果不懂怎么安装,可以直接在 Cursor 聊天框输入(亲测 Gemini3 可以直接一步到位,模型不够好可能中途会报错):

https://docs.claude.com/en/docs/claude-code/overview参考这个文档,帮我安装claudecode

无论是通过哪种安装方式,Claude Code 在安装后都会引导你配置参数或者注册登录,如果你有账号可以按照引导往下走。如果没有、希望和笔者一样直接接入自己的(便宜的)api,可以登录到非得付费的那一步退出,然后继续步骤 2。

步骤 2:启动“翻译官” (LiteLLM)

我们需要启动一个本地服务,用来做连接我们的 api 和 Anthropic 之间的桥梁。在 PowerShell 中运行(替换为你自己的 API Key):


# 设置 Key (必须加引号)
$env:OPENAI_API_KEY = "你的MiniMax_API_Key"

# 启动服务
# --drop_params: 自动丢弃不兼容的参数,防止报错
litellm --model openai/MiniMaxAI/MiniMax-M2 --api_base https://api.gmi-serving.com/v1 --drop_params

✅ 成功标志:看到 Running on http://0.0.0.0:4000

⚠️ 注意:这个窗口不要关闭。步骤 3 打开一个新的 powershell 窗口。

步骤 3:配置 PowerShell 连接

现在我们要告诉 Claude 工具:“别去连官网了,来连我们本地的翻译官”。

1. 打开配置文件:

在新的 PowerShell 窗口中输入:

 notepad $PROFILE

2.粘贴以下代码:


   function minimax {
       & {
           # 1. 把目标地址指向本地 LiteLLM (端口 4000)
           $env:ANTHROPIC_BASE_URL = "http://localhost:4000"
           
           # 2. Key 随便填,因为真实的 Key 已经在 LiteLLM 那边配好了
           $env:ANTHROPIC_AUTH_TOKEN = "sk-placeholder"
           
           # 3. 模型名称要和 LiteLLM 启动时的匹配
           $env:ANTHROPIC_MODEL = "MiniMaxAI/MiniMax-M2"
           $env:ANTHROPIC_SMALL_FAST_MODEL = "MiniMaxAI/MiniMax-M2"
           
           # 4. 启动 Claude 工具
           if (Get-Command claude -ErrorAction SilentlyContinue) {
               claude @args
           } else {
               Write-Error "请先安装 claude-code: npm install -g @anthropic-ai/claude-code"
           }
       }
   }

步骤 4:开始使用

  1. 新建一个 PowerShell 窗口(确保配置生效)。
  2. 输入命令:

# 启动自设定的minimax程序 
minimax 
# 进行测试 
你好

🎉 看到回复即搞定! 现在你就在用 Anthropic 的顶级命令行体验,驱动着公司的 MiniMax 模型了。

大家可以对比输入“claude code”和“minimax”下的差别:

图片

步骤 5:将 LiteLLM 的启动简化(选做)

Cursor 聊天框输入:

帮我将LiteLLM的启动简化,生成一个一键启动脚本。

下次使用时,就只需两步:

  1. 点击该脚本
  2. 在另一个终端窗口中输入“minimax”

另外,如果想更方便,比如在桌面启动 LiteLLM,也可以将这个 .bat 的文件和 .yaml 的参数文件一起复制到目标位置。比如我将其复制到了桌面。

图片

图片

💡 常见报错

Q: 报错 ImportError: Missing dependency 'backoff'?

A: 你安装时少装了组件。请运行 pip install "litellm[proxy]"。

Q: 报错 UnsupportedParamsError: ... reasoning\_effort?

A: 启动 LiteLLM 时忘了加 --drop\_params 参数。

Q: 输入 minimax 提示找不到命令?

A: 修改完配置文件后,需要重启 PowerShell 窗口,或者运行 。 $PROFILE 刷新一下。

03

总结和拓展

Summary & Expansion

总结

1. 核心文件

图片

2. 完整的逻辑链路图

  • 准备层(启动网关)

运行 start\_minimax\_proxy.bat。

关键动作:它不仅加载了 yaml 配置,还通过 set OPENAI\_API\_KEY 把**通行证(Token)**交给了 LiteLLM 进程。

结果:本地 4000(或其他)端口开始监听。

  • 调用层(触发指令)

你输入 minimax。

关键动作:系统执行 ps1 脚本里的函数。

  • 重定向层(配置环境)

关键动作:ps1 脚本在内存里临时改了两个环境变量:

ANTHROPIC\_BASE\_URL:指路,让 Claude Code 走向本地端口。

ANTHROPIC\_MODEL:定名,告诉 Claude Code 要发出的“暗号”是什么。

结果:Claude Code 启动并按照这个路标发包。

  • 翻译层(中转适配)

关键动作:这是最复杂的一步。

收包:LiteLLM 收到 Claude Code 的 Anthropic 格式请求。

查表:它看一眼 yaml,发现 model\_name(暗号)对上了。

变身:它把请求拆开,去掉多余参数(drop\_params),重新包装成标准的 OpenAI 格式。

送达:最后,它带着 .bat 里的那个 Token,把请求发给供应商的 v1 接口。

拓展:思考题

如果不想用MiniMax了,想用Inference Engine平台的其他模型,该修改哪几个文件?

**正确答案:**以Deepseek为例

修改.ps1、修改yaml,将 minimax function 一样的格式复制一份、修改模型名称部分就可以啦!

图片

图片

在启动时则可在终端输入deepseek,同样能成功启动

图片

教程完毕!😍😍😍 快去试试吧~

我们在实际的项目开发的过程中,有时候不得不将文件从一种格式转换为另一种格式。

DOCX(由 Microsoft Word 使用)是一种非常常见的文件格式,被很多人使用。有时候,我们希望将 Word 文档转换为 HTML。

这可以通过 Mammoth 包轻松实现。它是一个用于将 DOCX 文件转换为 HTML 的简单、高效、快速的库。在本文中,我们将学习如何在 Python 中使用 Mammoth 将 DOCX 转换为 HTML。

安装 Mammoth

首先,在安装之前准备好并激活你的虚拟环境:

python3 -m venv myenv
. myenv/bin/activate

然后,使用pip安装 Mammoth:
pip3 install mammoth
本教程使用的 Mammoth 版本是 1.4.15。在测试的时候,请确保它是.docx 文件!

以上环境准备好后,现在让我们开始提取文本并将其转换成 HTML。

提取 DOCX 文件的原始文本

在转换为 HTML 时保留格式是 Mammoth 的最佳功能之一。这里我们只需要几行代码转换成你需要 DOCX 文件的文本。

使用 extract_raw_text()方法来获取它:

import mammoth

with open(input_filename, "rb") as docx_file:
    result = mammoth.extract_raw_text(docx_file)
    text = result.value # The raw text
    with open('output.txt', 'w') as text_file:
        text_file.write(text)

注意,此方法不会返回有效的 HTML 文档。它只返回页面上的文本,因此我们使用.txt 扩展来保存它。如果你确实需要保持布局或格式,你需要提取 HTML 内容。

将 Docx 转换为 HTML 并自定义样式映射

默认情况下,Mammoth 将文档转换为 HTML,但它不会提供有效的 HTML 页面。虽然网页浏览器可以显示内容,但它缺少一个<html>标签来封装文档,以及一个<body>标签来包含文档。

假设使用的是带有模板的网络框架。可能会定义一个模板来显示 Word 文档,并将 Mammoth 的输出加载到模板主体内。

Mammoth 不仅在如何使用其输出方面具有灵活性,而且在如何创建输出方面也具有很大的灵活性。特别是在我们想要样式化我们生成的 HTML 时,我们有很多选项。我们通过将每个 DOCX 格式规则匹配到相应的 CSS 规则来映射样式。

要查看你的 DOCX 文件有哪些样式,你有两个选择:

  1. 使用 MS Word 打开您的 docx 文件,并检查样式工具栏。
  2. 通过用解压管理器打开你的 DOCX 文件来研究 XML 文件,然后导航到/word/styles.xml并找到你的样式。

第二个选项适用于无法使用 MS Word 或无法解释和显示样式的替代文字处理程序的用户。

Mammoth 已经默认涵盖了某些最常用的样式映射。例如,Heading1在 docx 样式中映射到 HTML 元素的<h1>,bold被映射到 HTML 元素的 strong,等等。

我们还可以在映射时使用 Mammoth 来自定义文档的样式。例如,如果您想将 DOCX 文件中的所有bold出现次数更改为 HTML 中的斜体,可以这样子实现:

import mammoth

custom_styles = "b => i"

with open(input_filename, "rb") as docx_file:
    result = mammoth.convert_to_html(docx_file, style_map = custom_styles)
    text = result.value
    with open('output.html', 'w') as html_file:
        html_file.write(text)

通过 custom_styles 变量,左边的样式来自 DOCX 文件,而右边的是相应的 CSS。
custom_styles = "b => "
有时我们转换的文档会有很多样式需要保留。这个时候再这样实现就会变得不切实际,要为每一个我们要映射的样式都创建一个变量。

不过有解法,我们可以使用docstrings一次映射我们想要的任意多个样式:

custom_styles = """ b => del
                    u => em
                    p[style-name='Heading 1'] => i"""

你可能已经注意到,最后的映射与其他的有点不同。在映射样式时,我们可以使用方括号[]并在其中添加条件,这样只有部分元素会以这种方式进行样式设置。

在我们的示例中,p[style-name='Heading 1']选择具有样式名称的段落Heading 1。我们也可以使用p[style-name^='Heading']来选择具有以Heading开头的样式名称的每个段落。

样式映射还允许我们将样式映射到自定义 CSS 类。通过这样做,我们可以随心所欲地修改 HTML 的样式。让我们举一个例子,我们在文档字符串中定义基本的自定义 CSS:

custom_css ="""
    <style>
    .red{
        color: red;
    }
    .underline{
        text-decoration: underline;
    }
    .ul.li{
        list-style-type: circle;
    }
    table, th, td {
    border: 1px solid black;
    }
    </style>
    """

现在我们可以更新我们的映射,以引用我们在<style>块中定义的 CSS 类:

custom_styles = """ b => b.red
                    u => em.red
                    p[style-name='Heading 1'] => h1.red.underline"""

并将 CSS 和 HTML 合并在一起:
edited_html = custom_css + html
这个时候如果 DOCX 文件包含任何这些元素,就能看到我们设置的样式结果。

通过以上方法我们已经知道如何映射样式,那就让我们使用一个更著名的 CSS 框架(以及相关的 JS)来让我们的 HTML 看起来更好,并练习一个更有可能的现实场景。

使用 Bootstrap(或其他任何前端框架)映射样式
就像我们之前处理custom_css一样,我们需要确保 CSS 与 HTML 一起加载。我们需要将 Bootstrap 文件 URI 或 CDN 添加到我们的 HTML 中:

bootstrap_css = '<link rel="nofollow" href="https://mybj123.com/links?url=aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9ib290c3RyYXBANS4wLjAtYmV0YTIvZGlzdC9jc3MvYm9vdHN0cmFwLm1pbi5jc3M=" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">'
bootstrap_js = '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>'

这里我稍微调整我们的 custom_styles,以匹配我们的新 CSS 类:

custom_styles = """ b => b.mark
                    u => u.initialism
                    p[style-name='Heading 1'] => h1.card
                    table => table.table.table-hover
                    """

在第一行,我们将粗体 DOCX 样式映射到具有类的 HTML 元素,该类是 HTML 标签的 Bootstrap 类,用于突出显示文本的一部分。bmark <mark>

在第二行,我们为 HTML 元素添加了类,稍微减小了字体大小,并将文本转换为大写。initialism u

在第三行,我们选择所有具有样式名称的段落,并将其转换为具有 Bootstrap 类的 HTML 元素,该类为元素设置多个样式属性,例如背景颜色、位置和边框。Heading 1 h1 card

在最后一行,我们将 docx 文件中的所有表格转换为 HTML 元素,并使用 Bootstrap 的类来给它一个新的外观,同时我们通过添加 Bootstrap 类使其在悬停时高亮显示。table table table-hover

和之前一样,我们使用点符号将多个类映射到同一个 HTML 元素,即使这些样式来自另一个来源。

最后,将 Bootstrap CDNs 添加到我们的 HTML 中:

edited_html = bootstrap_css + html + bootstrap_js

现在可以分享我们的 HTML,以下是完整的代码以供参考:

import mammoth

input_filename = "file-sample_100kB.docx"

custom_styles = """ b => b.mark
                    u => u.initialism
                    p[style-name='Heading 1'] => h1.card
                    table => table.table.table-hover
                    """


bootstrap_css = '<link rel="nofollow" href="https://mybj123.com/links?url=aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9ib290c3RyYXBANS4wLjAtYmV0YTIvZGlzdC9jc3MvYm9vdHN0cmFwLm1pbi5jc3M=" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">'
bootstrap_js = '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>'


with open(input_filename, "rb") as docx_file:
    result = mammoth.convert_to_html(docx_file, style_map = custom_styles)
    html = result.value 

edited_html = bootstrap_css + html + bootstrap_js

output_filename = "output.html"
with open(output_filename, "w") as f: 
    f.writelines(edited_html)

此外,需要注意的一点是,在实际情况下,你可能不会像我们在这里所做的那样直接将 Bootstrap CSS 添加到 HTML 内容中。相反,你会将 HTML 内容加载或注入到一个特殊的 HTML 页面中,该页面已经包含了必要的 CSS 和 JS 捆绑包。

Mammoth 还允许我们修改我们正在转换的内容。

处理我们不想分享的图片

假设我们希望跳过 DOCX 文件中的图像不进行转换。convert_to_html()方法接受一个convert_image参数,这是一个图像处理函数。它返回一个应该转换并添加到 HTML 文档中的图像列表。

当然,如果我们覆盖它并返回一个空列表,它们将从转换后的页面中省略:
`def ignore_image(image):

return []`

现在,让我们将该函数作为参数传递到convert_to_html()方法中:

with open(input_filename, "rb") as docx_file:
    result = mammoth.convert_to_html(docx_file, style_map = custom_styles, convert_image=ignore_image)
    html = result.value
    with open('output.html', 'w') as html_file:
        html_file.write(text)

就是这样! Mammoth 在生成 HTML 文件时将忽略所有图像。

到目前为止,我们一直在用 Python 编程方式使用 Mammoth。Mammoth 也是一个命令行工具,因此我们有了另一个将 DOCX 转换为 HTML 的接口。让我们在下一节中看看它的工作情况。

使用命令行工具将 DOCX 转换为 HTML

使用 Mammoth 的 CLI 进行文件转换通常如下所示:

mammoth path/to/input_filename.docx path/to/output.html

如果你想将图像从 HTML 中分离出来,可以指定一个输出文件夹:

mammoth file-sample_100kB.docx --output-dir=imgs

我们也可以像在 Python 中那样添加自定义样式。首先需要创建一个自定义样式文件:
touch my-custom-styles
然后,我们将在其中添加自定义样式,语法与之前相同:

b => b.red
u => em.red
p[style-name='Heading 1'] => h1.red.underline

现在我们可以生成带有自定义样式的 HTML 文件:

mammoth file-sample_100kB.docx output.html --style-map=my-custom-styles

大功告成!您的文档已按定义的自定义样式进行转换。https://mybj123.com/28792.html

结语

文件类型转换在处理网页技术时是一种常见需求。将 DOCX 文件转换为易于操作的 HTML 格式,使我们能够根据需要重建数据。使用 Mammoth,我们学会了如何从 docx 中提取文本并将其转换为 HTML。

在转换为 HTML 时,我们可以使用我们创建的 CSS 规则或常见的 UI 框架提供的规则来样式化输出。我们还可以省略不需要在 HTML 中可用的数据。

精简 Excel 工作簿、删除多余或不再使用的工作表,是一种非常有效的整理方式。通过移除无关内容,可以减少冗余信息,使文件结构更加清晰,只保留最有价值的数据。删除不必要的工作表不仅有助于释放存储空间,还能让工作簿的浏览与管理更加高效、直观。

在本文中,你将学习如何使用 Spire.XLS for .NET 库,通过 C# 从 Excel 工作簿中删除指定的工作表。

安装 Spire.XLS for .NET

首先,你需要将 Spire.XLS for .NET 包中包含的 DLL 文件添加为 .NET 项目的引用。你可以通过提供的下载链接手动下载 DLL 文件并引入项目,或者直接使用 NuGet 进行安装。

PM> Install-Package Spire.XLS

在 C# 中通过索引删除工作簿中的工作表

Spire.XLS for .NET 提供了 WorksheetsCollection.RemoveAt(int index) 方法,可根据工作表在工作簿中的索引位置删除指定的工作表。

具体示例代码如下:

using Spire.Xls;
using Spire.Xls.Collections;

namespace RemoveWorksheetByIndex
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 Workbook 对象
            Workbook wb = new Workbook();

            // 加载 Excel 文件
            wb.LoadFromFile(@"C:\Users\Administrator\Desktop\Input.xlsx");

            // 从工作簿中获取工作表集合
            WorksheetsCollection worksheets = wb.Worksheets;

            // 根据索引删除指定的工作表
            worksheets.RemoveAt(0);

            // 将工作簿保存为新的 Excel 文件
            wb.SaveToFile("RemoveByIndex.xlsx", ExcelVersion.Version2016);

            // 释放资源
            wb.Dispose();
        }
    }
}

在 C# 中通过工作表名称删除工作簿中的工作表

如果你已经知道需要删除的工作表名称,可以使用 WorksheetsCollection.Remove(string sheetName) 方法,直接按名称从工作簿中移除对应的工作表。

具体示例代码如下:

using Spire.Xls;
using Spire.Xls.Collections;

namespace RemoveWorksheetByName
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 Workbook 对象
            Workbook wb = new Workbook();

            // 加载 Excel 文件
            wb.LoadFromFile(@"C:\Users\Administrator\Desktop\Input.xlsx");

            // 从工作簿中获取工作表集合
            WorksheetsCollection worksheets = wb.Worksheets;

            // 根据工作表名称删除指定的工作表
            worksheets.Remove("sheet2");

            // 将工作簿保存为新的 Excel 文件
            wb.SaveToFile("RemoveByName.xlsx", ExcelVersion.Version2016);

            // 释放资源
            wb.Dispose();
        }
    }
}

在 C# 中一次性删除工作簿中的所有工作表

如果需要一次性移除工作簿中的所有工作表,可以使用 WorksheetsCollection.Clear() 方法快速清空工作表集合。

具体示例代码如下:

using Spire.Xls;
using Spire.Xls.Collections;

namespace RemoveAllWorksheets
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 Workbook 对象
            Workbook wb = new Workbook();

            // 加载 Excel 文件
            wb.LoadFromFile(@"C:\Users\Administrator\Desktop\Input.xlsx");

            // 从工作簿中获取工作表集合
            WorksheetsCollection worksheets = wb.Worksheets;

            // 删除所有工作表
            worksheets.Clear();

            // 将工作簿保存为新的 Excel 文件
            wb.SaveToFile("RemoveAllWorksheets.xlsx", ExcelVersion.Version2016);

            // 释放资源
            wb.Dispose();
        }
    }
}

申请临时许可证

如果你希望移除生成文档中的评估提示信息,或解除功能限制,请申请一个 为期 30 天的试用许可证。

在数据处理工作中,提取 PDF 文档中的文本和图像坐标是一个常见需求。本文将介绍如何使用 Spire.PDF for Python 库实现这一功能,通过简单的代码示例帮助你快速上手。

Spire.PDF 简介

Spire.PDF for Python 是一个功能强大的 PDF 处理库,允许开发者以编程方式操作 PDF 文件。它支持提取文本、图像、元数据等。当我们需要获取特定文本或图像的坐标时,这个库显得尤为便捷。

安装命令:pip install spire-pdf

坐标系设定

在 Spire.PDF 中,坐标系设定非常重要:

  1. 原点 (0, 0) 位于页面的左上角。
  2. X 轴向右延伸,Y 轴向下延伸。

理解这一点有助于我们更好地定位 PDF 中的元素。

获取文本坐标

以下是使用 Spire.PDF 提取 PDF 中指定文本坐标的步骤:

  1. 创建 PdfDocument 对象。
  2. 加载 PDF 文档。
  3. 获取特定页面。
  4. 创建 PdfTextFinder 对象并设置查找选项。
  5. 查找文本并获取其坐标。

下面是获取文本坐标的示例代码:

from spire.pdf.common import *
from spire.pdf import *

# 创建 PdfDocument 对象
doc = PdfDocument()

# 加载 PDF 文档
doc.LoadFromFile("Input.pdf")

# 获取特定页面
page = doc.Pages.get_Item(0)

# 创建 PdfTextFinder 对象
textFinder = PdfTextFinder(page)

# 指定查找选项
findOptions = PdfTextFindOptions()
findOptions.Parameter = TextFindParameter.WholeWord
textFinder.Options = findOptions

# 在页面中查找字符串 "隐私政策"
findResults = textFinder.Find("隐私政策")

# 获取查找结果中第一个实例
result = findResults[0]

# 获取找到文本的 X/Y 坐标
x = int(result.Positions[0].X)
y = int(result.Positions[0].Y)
print("The coordinates of the first instance of the found text are:", (x, y))

# 释放资源
doc.Dispose()

代码解析

  • PdfDocument 对象用于打开现有 PDF 文件。
  • 通过 PdfTextFinder 可以轻松找到指定文本,设置的查找选项允许忽略大小写并确保匹配完整单词。
  • 最后,通过 result.Positions 获取文本坐标,其中 (0, 0) 表示页面的左上角。

获取图片坐标

获取图像坐标的过程与文本提取类似,但使用 PdfImageHelper 处理图像信息。以下是示例代码:

from spire.pdf.common import *
from spire.pdf import *

# 创建 PdfDocument 对象
doc = PdfDocument()

# 加载 PDF 文档
doc.LoadFromFile("Input.pdf")

# 获取特定页面
page = doc.Pages.get_Item(0)

# 创建 PdfImageHelper 对象
imageHelper = PdfImageHelper()

# 获取页面中的图像信息
imageInformation = imageHelper.GetImagesInfo(page)

# 获取指定图像的 X/Y 坐标
x = int(imageInformation[0].Bounds.X)
y = int(imageInformation[0].Bounds.Y)
print("The coordinates of the specified image are:", (x, y))

# 释放资源
doc.Dispose()

代码解析

  • 使用 PdfImageHelper 类来获取特定页面上的所有图像信息。
  • 通过 imageInformation 对象获取图像的边界坐标(X, Y),便于后续处理。

总结

本文介绍了如何使用 Spire.PDF for Python 提取 PDF 中文本及图像的坐标,并提供了相关示例代码。无论是在信息提取、数据分析,还是文档处理方面,掌握这些技术都将极大提升你的工作效率。希望这篇博客能帮助你快速上手 PDF 坐标提取的相关操作!

struct类型的定义以关键字struct开头,后跟struct的名字,接着是定义在一对花括号中的struct定义体。struct定义体中可以定义一系列的成员变量、成员属性、静态初始化器、构造函数和成员函数。

定义struct类型

以下是定义struct类型的一个示例:

struct Rectangle {
    let width: Int64
    let height: Int64

    public init(width: Int64, height: Int64) {
        this.width = width
        this.height = height
    }

    public func area() {
        width * height
    }
}

上例中定义了名为Rectangle的struct类型,它有两个Int64类型的成员变量width和height,一个有两个Int64类型参数的构造函数init,以及一个成员函数area,用于返回width和height的乘积。

1. struct成员变量

struct成员变量分为实例成员变量和静态成员变量(使用static修饰符修饰,且必须有初值),二者访问上的区别在于实例成员变量只能通过struct实例访问,静态成员变量只能通过struct类型名访问。

实例成员变量定义时可以不设置初值(但必须标注类型),如上例中的width和height。也可以设置初值,例如:

struct Rectangle {
    let width = 10
    let height = 20
}

2. struct静态初始化器

struct支持定义静态初始化器,并在静态初始化器中通过赋值表达式来对静态成员变量进行初始化。

静态初始化器以关键字组合static init开头,后跟无参参数列表和函数体,且不能被访问修饰符修饰。函数体中必须完成对所有未初始化的静态成员变量的初始化,否则编译报错。

struct Rectangle {
    static let degree: Int64
    static init() {
        degree = 180
    }
}

一个struct中最多允许定义一个静态初始化器,否则报重定义错误。

struct Rectangle {
    static let degree: Int64
    static init() {
        degree = 180
    }
    static init() { // 错误!用前面的静态init函数重新定义
        degree = 180
    }
}

3. struct构造函数

struct支持两类构造函数:普通构造函数和主构造函数。

普通构造函数以关键字init开头,后跟参数列表和函数体,函数体中必须完成对所有未初始化的实例成员变量的初始化,否则编译报错。

struct Rectangle {
    let width: Int64
    let height: Int64

    public init(width: Int64, height: Int64) { // 错误! 'height'未在构造函数中初始化
        this.width = width
    }
}

一个struct中可以定义多个普通构造函数,但它们必须构成重载,否则报重定义错误。

struct Rectangle {
    let width: Int64
    let height: Int64

    public init(width: Int64) {
        this.width = width
        this.height = width
    }

    public init(width: Int64, height: Int64) { // 正确!用第一个init函数重载
        this.width = width
        this.height = height
    }

    public init(height: Int64) { // 错误!使用第一个init函数重新定义
        this.width = height
        this.height = height
    }
}

除了可以定义若干普通的以init为名字的构造函数外,struct内还可以定义(最多)一个主构造函数。主构造函数的名字和struct类型名相同,它的参数列表中可以有两种形式的形参:普通形参和成员变量形参(需要在参数名前加上let或var),成员变量形参同时扮演定义成员变量和构造函数参数的功能。

使用主构造函数通常可以简化struct的定义,例如,上述包含一个init构造函数的Rectangle可以简化为如下定义:

struct Rectangle {
    public Rectangle(let width: Int64, let height: Int64) {}
}

主构造函数的参数列表中也可以定义普通形参,例如:

struct Rectangle {
    public Rectangle(name: String, let width: Int64, let height: Int64) {}
}

如果struct定义中不存在自定义构造函数(包括主构造函数),并且所有实例成员变量都有初始值,则会自动为其生成一个无参构造函数(调用此无参构造函数会创建一个所有实例成员变量的值均等于其初值的对象);否则,不会自动生成此无参构造函数。例如,对于如下struct定义,注释中给出了自动生成的无参构造函数:

struct Rectangle {
    let width: Int64 = 10
    let height: Int64 = 10
    /* Auto-generated memberwise constructor:
    public init() {
    }
    */
}

4. struct成员函数

struct成员函数分为实例成员函数和静态成员函数(使用static修饰符修饰),二者的区别在于:实例成员函数只能通过struct实例访问,静态成员函数只能通过struct类型名访问;静态成员函数中不能访问实例成员变量,也不能调用实例成员函数,但在实例成员函数中可以访问静态成员变量以及静态成员函数。

下例中,area是实例成员函数,typeName是静态成员函数。

struct Rectangle {
    let width: Int64 = 10
    let height: Int64 = 20

    public func area() {
        this.width * this.height
    }

    public static func typeName(): String {
        "Rectangle"
    }
}

实例成员函数中可以通过this访问实例成员变量,例如:

struct Rectangle {
    let width: Int64 = 1
    let height: Int64 = 1

    public func area() {
        this.width * this.height
    }
}

5. struct成员的访问修饰符

struct的成员,包括成员变量、成员属性、构造函数、成员函数、操作符函数,可以用4种访问修饰符修饰:private、internal、protected和public,缺省的修饰符是internal。

  • private表示在struct定义内可见。
  • internal表示仅当前包及子包内可见。
  • protected表示当前模块可见。
  • public表示模块内外均可见。

下面的例子中,width是public修饰的成员,在类外可以访问,height是缺省访问修饰符的成员,仅在当前包及子包可见,其他包无法访问。

package a
publicstructRectangle {
    public var width: Int64
    var height: Int64
    private var area: Int64
    ...
}

func samePkgFunc() {
    var r = Rectangle(10, 20)
    r.width = 8               // Ok: public 'width' can be accessed here
    r.height = 24             // Ok: 'height' has no modifier and can be accessed here
    r.area = 30               // 错误!, private 'area' can't be accessed here
}
package b
import a.*
main() {
    var r = Rectangle(10, 20)
    r.width = 8               // Ok: public 'width' can be accessed here
    r.height = 24             // 错误!, no modifier 'height' can't be accessed here
    r.area = 30               // 错误!, private 'area' can't be accessed here
}

6. 禁止递归struct

递归和互递归定义的struct均是非法的。例如:

struct R1 { // 错误!'R1' 递归引用自身
    let other: R1
}
struct R2 { // 错误!'R2' 和 'R3' 递归引用自身
    let other: R3
}
struct R3 { // 错误!'R2' 和 'R3' 递归引用自身
    let other: R2
}

创建struct实例

定义了struct类型后,即可通过调用struct的构造函数来创建struct实例。在struct定义之外,通过struct类型名调用构造函数。例如,下例中定义了一个Rectangle类型的变量r。

let r = Rectangle(10, 20)

创建了struct实例之后,可以通过实例访问它的(public修饰的)实例成员变量和实例成员函数。例如,下例中通过r.width和r.height可分别访问r中width和height的值,通过r.area()可以调用r的成员函数area。

let r = Rectangle(10, 20)
let width = r.width   // width = 10
let height = r.height // height = 20
let a = r.area()      // a = 200

如果希望通过struct实例去修改成员变量的值,需要将struct类型的变量定义为可变变量,并且被修改的成员变量也必须是可变成员变量(使用var定义)。举例如下:

struct Rectangle {
    public var width: Int64
    public var height: Int64

    public init(width: Int64, height: Int64) {
        this.width = width
        this.height = height
    }

    public func area() {
        width * height
    }
}

main() {
    var r = Rectangle(10, 20) // r.width = 10, r.height = 20
    r.width = 8               // r.width = 8
    r.height = 24             // r.height = 24
    let a = r.area()          // a = 192
}

在赋值或传参时,会对struct实例进行复制,生成新的实例,对其中一个实例的修改并不会影响另外一个实例。以赋值为例,下面的例子中,将r1赋值给r2之后,修改r1的width和height的值,并不会影响r2的width和height值。

struct Rectangle {
    public var width: Int64
    public var height: Int64

    public init(width: Int64, height: Int64) {
        this.width = width
        this.height = height
    }

    public func area() {
        width * height
    }
}

main() {
    var r1 = Rectangle(10, 20) // r1.width = 10, r1.height = 20
    var r2 = r1                // r2.width = 10, r2.height = 20
    r1.width = 8               // r1.width = 8
    r1.height = 24             // r1.height = 24
    let a1 = r1.area()         // a1 = 192
    let a2 = r2.area()         // a2 = 200
}

mut函数

struct类型是值类型,其实例成员函数无法修改实例本身。例如,下例中,成员函数g中不能修改成员变量i的值。

struct Foo {
    var i = 0

    public func g() {
        i += 1  // 错误!无法在实例成员函数中修改实例成员变量的值
    }
}

mut函数是一种可以修改struct实例本身的特殊的实例成员函数。在mut函数内部,this的语义是特殊的,这种this拥有原地修改字段的能力。

:只允许在interface、struct和struct的扩展内定义mut函数,禁止在class中定义mut函数。

mut函数与普通的实例成员函数相比,多一个mut关键字来修饰。

例如,下例中在函数g之前增加mut修饰符之后,即可在函数体内修改成员变量i的值。

struct Foo {
    var i = 0

    public mut func g() {
        i += 1  // 正确
    }
}

参考引用

逛公众号发现的一篇关于 CC 教程的推送

Note

From:唐霜

写的不错,总结的很全面,而且有大纲,确实很方便新手入坑,大家可以收藏下

附网站截图:


📌 转载信息
转载时间:
2026/1/4 12:21:06