Docx的搜索实现

我做了一个c#的docx函数库。

python操作word实在是太垃了,尤其是涉及到多表、页眉页脚,甚至有时候好好的段落硬是搜不到。所以我转向了对于word操作支持更好的c#。

说是讲实现,实际上只讲了使用。毕竟实现挺复杂的也不好讲,不如感兴趣的自己看源码吧。我这里直接讲讲怎么用我这个函数就好了。

github resourcehere: kasusa/Docx_Search


简介

本库主要适用于docx文件的修改、段落搜索、表格搜索等,填补了官方未提供的“搜索”功能的空白。

功能主要包括:

  • word中所有图片提取为bitmap
  • 搜索段落
  • 搜索表格
  • 删除段落
  • 获取单元格(cell)的内容
  • 批量替换
  • 保存到桌面 out文件夹

依赖

本库是基于开源*非商用的docx库编写的。引用如下两个库,可以在这里下载:xceedsoftware/DocX ,这个项目提供了一些非常实用的例子,建议新手先看他们的例子来学习c#操作word的逻辑。

1using Xceed.Document.NET;
2using Xceed.Words.NET;

创建对象

我把官方的document对象又封装了一遍,这样使用函数的时候更加直观。

1myutil tempo;//我的word对象
2//docx文件的路径
3string tempo_path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @$"\Sample\方案\方案A.docx";
4if (System.IO.Directory.Exists(tempo_path))
5{
6    //初始化对象
7    tempo = new myutil(a);
8}

搜索段落

搜索段落如同word中一样,使用string作为关键字进行搜索,实现下来整体速度还不错。

有的时候一篇word中搜索一个关键词可能有好几个结果,这种时候返回list的就派上用场了。

有的时候可能要操作的段落不好定位,但是可以确定他在某个标题的下面,index搜索就派上用场了,返回的index+1差不多就是需要的段落位置了。

注意:xceed.docx的实现比较完善,可以搜索到目录、表格中的段落,所以有时候搜索错了可能是搜索到目录里面了……

我写了几种类型的搜索函数:

1Find_Paragraph_for_p(string v) 				//搜索后返回paragraph
2Find_Paragraph_for_plist(string v) 			//搜索后返回paragraph列表
3Find_Paragraph_for_text(string v) 			//搜索后返回string(段落的文字内容)
4Find_Paragraph_for_i(string v) 				//搜索后返回段落的index
5Find_Paragraph_for_ilist(string v) 			//搜索后返回段落的index list

举一个实际的例子:

1//搜索“本报告记录编号:”,返回段落的text
2tmpstr = doc.Find_Paragraph_for_text("本报告记录编号:");
3//获取到:本报告记录编号:P2021XXXXX-GB01。
4//处理切分一下字符串,获取到我需要的部分
5tmpstr = myutil.get_string_after(tmpstr, "本报告记录编号:", "P2021XXXXX".Length);//结果:P2021XXXXX

再举一个例子:

1//搜索总体评价(一级标题),获取其后面段落的内容
2int i = doc.Find_Paragraph_for_ilist("总体评价")[0] + 1;
3a = doc.document.Paragraphs[i].Text;

搜索的时候如果得到的结果不是很正常,就要看看word中是不是又多个能搜到的位置了。

搜索表格

搜索表格是一个我自创的功能,他是利用表头(首行)的文字内容对整个word中的所有表标题进行检索,如果某一个表格的第一行和给的参数相同,就认为是查找到了这张表。

经过我的实际测试,速度还算可以。

1//寻找表头文字为“序号	机房名称	物理位置	重要程度”的第一张表
2//注意这里我没有删除空格和tab,这是为了从word中复制过来方便,实际在函数中会把空格、tab字符都删除后进行对比
3var table1 = doc.findTableList("序号	机房名称	物理位置	重要程度")[0];

下面是我实现的一个表格复制函数。从word1中把t1复制到t2,可以选择是否包含表头。

 1#region 表格复制函数
 2/// <param name="t1head">table1 表头</param>
 3/// <param name="i1">table1 所在index</param>
 4/// <param name="t2head">table2 表头</param>
 5/// <param name="i2">table 2 所在index</param>
 6void CopyTable(string t1head, string t2head, int i1 = 0, int i2 = 0)
 7{
 8    bool toremove = false;
 9    var table1 = doc.findTableList(t1head)[i1];
10    ConsoleWriter.WriteColoredText("table 报告中 ↑", ConsoleColor.Green);
11    var table2 = tempo.findTableList(t2head)[i2];
12    ConsoleWriter.WriteColoredText("table 模板中 ↑", ConsoleColor.Green);
13    //如果t1比t2更宽,增加一列临时列
14    if (table1.ColumnCount > table2.ColumnCount)
15    {
16        table2.InsertColumn();
17        toremove = true;
18    }
19    //如果t1比t2更窄,直接给t2瘦身
20    else if (table1.ColumnCount < table2.ColumnCount)
21    {
22        table2.RemoveColumn(table2.ColumnCount-1);
23    }
24    //删除所有空的内容行
25    while (table2.RowCount > 1)
26    {
27        table2.RemoveRow(table2.RowCount - 1);
28    }
29    //从内容行数开始复制
30    for (int i = 1; i < table1.RowCount; i++)
31    {
32        Xceed.Document.NET.Row row = table1.Rows[i];
33
34        table2.InsertRow(row);
35    }
36    //删除多复制过来的列
37    if (toremove)
38    {
39        table2.RemoveColumn(table2.ColumnCount - 1);
40    }
41    ConsoleWriter.WriteColoredText("复制表完毕;", ConsoleColor.Yellow);
42
43}
44
45/// <summary>
46/// 复制表t1到表t2(包含表头)
47/// </summary>
48/// <param name="t1head">table1 表头</param>
49/// <param name="i1">table1 所在位数</param>
50/// <param name="t2head">table2 表头</param>
51/// <param name="i2">table 2 所在位数</param>
52void CopyTable_withHead(string t1head, string t2head, int i1 = 0, int i2 = 0)
53{
54    bool toremove = false;
55    var table1 = doc.findTableList(t1head)[i1];
56    ConsoleWriter.WriteColoredText("table 报告中 ↑", ConsoleColor.Green);
57    var table2 = tempo.findTableList(t2head)[i2];
58    ConsoleWriter.WriteColoredText("table 模板中 ↑", ConsoleColor.Green);
59    //如果t1比t2更宽,增加一列临时列
60    if (table1.ColumnCount > table2.ColumnCount)
61    {
62        table2.InsertColumn();
63        toremove = true;
64    }
65    //如果t1比t2更窄,直接给t2瘦身
66    else if (table1.ColumnCount < table2.ColumnCount)
67    {
68        table2.RemoveColumn(table2.ColumnCount - 1);
69    }
70    //删除所有空的内容行
71    while (table2.RowCount > 1)
72    {
73        table2.RemoveRow(table2.RowCount - 1);
74    }
75    //从内容行数开始复制
76    for (int i = 0; i < table1.RowCount; i++)
77    {
78        Xceed.Document.NET.Row row = table1.Rows[i];
79
80        table2.InsertRow(row);
81    }
82    //删除多复制过来的列
83    if (toremove)
84    {
85        table2.RemoveColumn(table2.ColumnCount - 1);
86    }
87    //删除顶部的原始行(表头总是有问题服了)
88    table2.RemoveRow(0);
89    ConsoleWriter.WriteColoredText("复制表完毕;", ConsoleColor.Yellow);
90
91}
92
93#endregion

文字批量替换

文字批量替换是我参考官方文档的例子写出来的,因为功能很常用所以把他包含到了库中。

1//向字典里面添加【被替换str】、【替换成str】
2doc._replacePatterns.Add("P2021xxxxx", "P202100001");
3doc._replacePatterns.Add("AAAAA", "可口可乐公司");
4
5doc.ReplaceTextWithText_all_noBracket();//自动搜索文档中所有在字典里面的内容并替换

明天过生日啦~


2022
Excel转换pdf