python-docx通过标题给整个docx文档排序

上班有任务之后感觉效率就提高了,果然,人活着还是得有目标、有动力。
我们发现word文档的内容顺序不对,但是有几百条标题,上千页文档,手动排序虽然也可以,但是费时费力,而且万一以后需求又有了变化就白给了,所以依然以python-docx为基础,开发了一个sort脚本

预备知识

我们首先要知道docx的格式,docx的主体本质上是一个xml

<w:body>
body之间的内容就是word文档的内容
</w:body>

<w:p>
p之间的内容就是文档内一个段落的内容
</w:p>

<w:r>
r之间的内容是段落内的小块,当一个段落内字体以及格式不一样是,会存在多个run
</w:r>

这个模块这表示该段落p(模块r)的类型
<w:pPr>
	p:
	<w:Style w:val = "Heading 1">标题
	r:
	<w:b/>加粗
	<w:i/>斜体
</w:pPr>

<w:t>
这个部分用于存放文本内容
</w:t>

上述格式的级别包含关系示例:

<w:body>
	<w:p>
		<w:pPr>
			<w:Style w:val = "Heading 1">
		</w:pPr>
		<w:r>
			<w:pPr>
				<w:b/>
			</w:pPr>
			<w:t>Heading1</w:t>
		</w:r>
	</w:p>
</w:body>

上述示例,在word中显示就是一个加粗的,段落与字体格式为Heading 1的,文本显示为Heading1的一级标题

本方法的适用类型

Notice
仅适用于以下结构的文档,文档的主体部分仅存在段落结构,所有其他的内容都归于段落内

<w:body>
	#段落1
	<w:p>
		标题
	</w:p>
	#段落2
	<w:p>
		正文、图片
	</w:p>
	#段落3
	<w:p>
		表格
	</w:p>
	...
	#段落n
	<w:p>
		标题n
	</w:p>
	...
<w:body>

排序思路

对于这种结构的文档,那么我们的思路就很清晰了:

  1. 遍历文档的所有段落
  2. 找到文档格式(Style)为标题(Heading)的段落
  3. 把所有标题段落的文本(Text)取出生成一个StringList,并按照自己的需求排序
  4. 最后根据StringList的顺序,为文档重新排序,即移动paragraph

这样我们就完成了对于文档的排序

实际操作

import

from os import write
from sys import flags
from docx import Document
from docx.shared import Inches

str_list_sort

第一个函数是排序函数,我们将一个二维列表根据列表的第一个元素(我们的标题)的字母(不区分大小写)从小到大排序,最后返回标题的String,以及标题对应的段落的开始序号和结束序号
[HeadingStr, ParagraphStartIndex, ParagraphEndIndex]

本函数的原型是在网上抄的,感谢一波忘记是谁的不知名网友

#将所有字符串转化为小写进行比较排序
def str_list_sort(string_list):
    listtemp = []
    auxiliary_list = []
    for i in range(0,len(string_list)):
        listtemp.append(string_list[i][0].lower())
        listtemp.append(string_list[i][0])
        listtemp.append(string_list[i][1])
        listtemp.append(string_list[i][2])
        auxiliary_list.append(listtemp)
        listtemp = []
    #将字符串列表转化为:[['str1','str1.lower()'], ['str2','str2.lower()'], ...]的格式
    #auxiliary_list = [(x.lower(), x, index, endindex) for x in string_list[0] for index in string_list[1] for endindex in string_list[2]]
    
    #排序
    auxiliary_list.sort()
    
    #读取排序后的字符串列表(读取第二位字符串)
    new_list = [(x[1], x[2], x[3]) for x in auxiliary_list]
    return new_list

insertParagraph

第二个函数是将原始文档中的段落,插入新的文档中
输入:段落起始序号,段落结束序号,原始文档的路径,新文档的对象,新文档的插入起始位置

def insertParagraph(begin, end, filePath, dNew, startIndex):
    '''
    flag = False
    write = False
    dOri = Document(filePath + 'MC-Basic Document User.docx')
    for element in dOri.element.body:
        d = Document()
        d.element.body.append(element)
        if len(d.paragraphs) > 0:
            p = d.paragraphs[0]
            #print(p.text)
            if len(p.text) > 0:
                if str == p.text:
                    flag = True
                    write = True
                elif "Heading 1" == p.style.name:
                    flag = False
                    if write:
                        break
        if(flag):
            dNew.element.body.append(element)
    '''
        #print(1)
    dOri = Document(filePath)
    i = begin
    while i <= end:
        dNew.element.body.append(dOri.paragraphs[i]._p)
        end = end - 1

这个函数非常简短着重解释以下几点:

'''
这个变量代表:docx类.第i个段落方法.指向该段落本体的指针
因此,当将该变量append到新文档中,会导致旧文档的paragraphs的第i个成员丢失
于是,我们不需要改变序号i,一直读取paragraphs[i]._p,即可实现顺序添加段落
于是,我们通过end-- 的方式完成遍历添加段落
'''
dOri.paragraphs[i]._p
'''
这个变量代表:docx方法类.指向docx本体类的指针.下属的body元素
通过append的方法,把paragraph的本体添加进去
通过这种方法,能够实现对paragraph的整体添加,而不是只加入文本和格式,
确保了表格,超链接,标签等等内容不会丢失
'''
dNew.element.body

sortHeading

第三个函数就是实现通过标题对文档进行sort的函数
输入原始文档的对象(其实好像不需要)和路径

def sortHeading(document, fileName):
	'''
	为了确保文档设置的格式统一性,我们不使用Document()创建新的空白文档
	而是通过读取原始文档,再将文档的内容清空来实现创建空白文档
	'''
	#generate blank document with original Styles and Settings
    dNew = Document(fileName)
    dNew._body.clear_content()

	#define tool variable
    headStr = []
    tempList = []
    index = 0
    end = 0

	#loop all paragraphs
    for p in document.paragraphs:
    	'''
    	我们希望能够不遗漏任何东西,完成全部迁移
    	由于在docx中,标题级别的段落和正文级别的段落在xml格式中都
    	属于paragraph,不存在包含关系,只存在Style的区别,于是我们
    	寻找style为Heading 1的段落,并将此时的序号index作为该标题
    	的起始序号,并将上一个标题的终止序号设置为index - 1
    	将这三个元素保存到tempList中,
    	再将tempList保存袋headStr中,通过str_list_sort实现标题排序
    	'''
        if p.style.name == 'Heading 1':
            endindex = index - 1
            end = max(end, endindex + 1)
            if len(tempList) > 0:
                tempList.append(endindex)
                headStr.append(tempList)
                tempList = []
            tempList.append(p.text)
            tempList.append(index)
        index = index + 1
    headStr = str_list_sort(headStr)

	'''
	根据排完序的二维列表生成新的docx文档
	'''
    for i in range(0, len(headStr)):
        insertParagraph(headStr[i][1], headStr[i][2], fileName, dNew, headStr[0][1])
    
    #insertParagraph(end, len(document.paragraphs), document, dNew)
    return dNew

至此我们就完成了对一个文档基于标题的重新排序

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值