深圳做网站网络营销公司排名,四川做网站有哪些公司,四川省建设工程网站,wordpress建什么站中文文本切割程序 基本信息代码解析相关包获取 yaml 关键文件类的构造函数切分语句部分特殊处理 PDF重点切分去除数组中空字符串再度切分后长度 附录附录一#xff1a;完整代码附录二#xff1a;可继续思考问题 基本信息
文件名#xff1a; chinese_text_splitter.py 文件地… 中文文本切割程序 基本信息代码解析相关包获取 yaml 关键文件类的构造函数切分语句部分特殊处理 PDF重点切分去除数组中空字符串再度切分后长度 附录附录一完整代码附录二可继续思考问题 基本信息
文件名 chinese_text_splitter.py 文件地址 E:\Code\Knowledge-QA-LLM\Knowledge-QA-LLM-main\knowledge_qa_llm\text_splitter\chinese_text_splitter.py Author SWHL、omahs CSDN Author me脚踏实地的大梦想家 Original Code url 代码解析
相关包
import re
from pathlib import Path
from typing import Listre 正则表达式。在文本处理中正则表达式可用于验证、搜索、提取和替换文本中的特定模式。
re.sub(pattern, replacement, string, count0, flags0)
# pattern要被替换的字符串。
# replacement要替换的字符串。
# string被操作的字符串的名称。
# count可选指定最大替换次数默认为0表示替换所有匹配项。
# flags可选可以对字符串加以限制比如忽略大小写等。import retext Hello, my name is Alice. Nice to meet you, Alice!
new_text re.sub(rAlice, Bob, text) Hello, my name is Bob. Nice to meet you, Bob!Path 负责路径通过使用 Path 类可以创建、连接、分解和操作文件系统路径而无需直接使用字符串拼接或分割。
# 当前脚本文件的路径
Path(__file__)
# resolve()方法会返回规范化的绝对路径解析出符号链接和相对路径使其变为绝对路径
Path(__file__).resolve()
# root_dir为当前脚本的绝对路径下的上级目录的上级目录
root_dir Path(__file__).resolve().parent.parentList 用于表示一个列表类型。
# 表明函数的参数或返回值应该是一个字符串类型的列表
List[str]获取 yaml 关键文件
root_dir Path(__file__).resolve().parent.parent
config_path root_dir / config.yaml
config read_yaml(config_path)上述代码获取了重要描述文件 config.yaml 文件的路径地址信息然后通过 read_yaml() 函数读取到其中信息。关于 read_yaml() 函数的代码提取如下
# 首先 read_yaml() 函数在 ..util.util.py 文件中
from ..utils.utils import read_yaml# 提取出 read_yaml() 函数如下
def read_yaml(yaml_path: Union[str, Path]):with open(str(yaml_path), rb) as f:data yaml.load(f, Loaderyaml.Loader)return data上述代码讲打开 str 字符串类型的地址信息或者 Path 对象的地址信息通过代码读取到该地址文件以二进制的形式返回读取到的结果。 类的构造函数
def __init__(self,pdf: bool False,sentence_size: int config.get(SENTENCE_SIZE),
):self.pdf pdfself.sentence_size sentence_size上述 __init__ 为类 ChineseTextSplitter 的构造函数 pdf: bool False 这是一个布尔型参数默认为False。它用于表示文本是否来自PDF文档。如果设置为True则表示文本来自PDF否则为其他来源。 sentence_size: int config.get(SENTENCE_SIZE) 从 yaml 文件中获取 SENTENCE_SIZE 的值作为 sentence_size 的默认值。 切分语句部分
特殊处理 PDF
切分语句 split_text 是定义在 ChineseTextSplitter 类中的一个成员方法用于将输入的文本分割成句子的列表。
切分语句部分过长将首先切分开介绍附录附完整的切分函数代码
def split_text(self, text: str) - List[str]: ##此处需要进一步优化逻辑if self.pdf:text re.sub(r\n{3,}, r\n, text)text re.sub(\s, , text)text re.sub(\n\n, , text)text 为待分割的文本字符串格式 - List[str] 是方法的返回类型标注表示该方法返回一个字符串列表 re.sub(r\n{3,}, \n, text) 将连续三个以上的换行符替换称为单个换行符 re.sub(\s, , text) 将单个/连续的空白字符串替换为单个空格 text text.replace(\n\n, ) 将连续两个换行符移除 重点切分
text re.sub(r([;.!?。\?])([^”’]), r\1\n\2, text)
text re.sub(r(\.{6})([^’”」』]), r\1\n\2, text) # \.{6} 代表着连续6个英文点作为英文中省略号
text re.sub(r(\…{2})([^’”」』]), r\1\n\2, text) # 中文省略号……
text re.sub(r([;!?。\?][’”」』]{0,2})([^;!?。\?]), r\1\n\2, text) # 其目标与第一个相反想要筛选出以及引号为结尾的字段。
text text.rstrip() # 段尾如果有多余的\n就去掉它上述代码是本文本函数的重点部分总结来说就是运用 re.sub() 函数将句子按照标点的方式分割详细阐述如下
re.sub(r([;.!?。\?])([^”’]), r\1\n\2, text) 重点部分拆分 第一个捕获组([;.!?。\?]) 该部分是一个字符类包含中英文分号中英文句号中英文感叹号以及中英文问号。该捕获组的作用为用来匹配句子分隔符。 第二个捕获组([^”’]) 该部分是一个否定字符类表示匹配除了有双引号与有单引号之外的任何字符。 将第一个捕获组与第二个捕获组的结合其意义在于筛选出以第一个捕获组中字符类为结尾且没有引号在其后的句子。一定要注意其后其前是另一种写法 e . g . e.g. e.g. 案例见替换模式后下述 替换模式\1\n\2 如果捕获成功即满足非引号作为结尾的句子以字符类结尾则使用替换模式。将句子通过换行分隔开。切分前的句子为 \1加入换行符 \n以及切分后的句子 \2 e . g . e.g. e.g.
import retext 你好吗我很好你想吃什么“苹果。”她说。
text re.sub(r([;.!?。\?])([^”’]), r\1\n\2, text)
print(text)# 结果如下
你好吗
我很好
你想吃什么
“苹果。”她说。去除数组中空字符串
ls [i for i in text.split(\n) if i]
# 将不为空的字符串保留在列表 ls 中。若当前空字符串满足单句最大长度要求则视为完成中文句子切分返回 ls 数组。 再度切分后长度
在去除空元素后通过调取 yaml 关键信息文件中的 SENTENCE_SIZE 属性信息获取规定最长的单句文本长度。再根据长度进行判断若超出规定范围则需二次切分。
首先切分
切分除 。” 结尾的语句逗号句号搭配引号
for ele in ls:if len(ele) self.sentence_size:ele1 re.sub(r([,.][’”」』]{0,2})([^,.]), r\1\n\2, ele)ele1_ls ele1.split(\n)其次切分
切分一个或多个连续的换行符 或 两个或多个连续的空格后面可能跟随0到2个特定字符后面紧跟一个非空白字符。然后在这两部分之间插入一个换行符。
for ele_ele1 in ele1_ls:if len(ele_ele1) self.sentence_size:ele_ele2 re.sub(r([\n]{1,}| {2,}[’”」』]{0,2})([^\s]), r\1\n\2, ele_ele1) # 切分换行以及空格ele2_ls ele_ele2.split(\n)继续切分
切分查找一个0到2个特定字符后面跟着的空格然后紧随一个非空格字符。在这两部分之间插入一个换行符。
然后将会找到 ele2_ls 列表中的元素 ele_ele2然后用 ele_ele3 字符串中的多个行替换它。
for ele_ele2 in ele2_ls:if len(ele_ele2) self.sentence_size:ele_ele3 re.sub(( [’”」』]{0,2})([^ ]), r\1\n\2, ele_ele2)ele2_id ele2_ls.index(ele_ele2)ele2_ls (ele2_ls[:ele2_id] [i for i in ele_ele3.split(\n) if i] ele2_ls[ele2_id 1 :])替换超长字符
ele_id ele1_ls.index(ele_ele1)
ele1_ls (ele1_ls[:ele_id] [i for i in ele2_ls if i] ele1_ls[ele_id 1 :])id ls.index(ele)
ls ls[:id] [i.strip() for i in ele1_ls if i] ls[id 1 :]至此为止数组 ls 中所有字符全部都符号长度标准。 附录
附录一完整代码
import re
from pathlib import Path
from typing import List
from ..utils.utils import read_yamlroot_dir Path(__file__).resolve().parent.parent
config_path root_dir / config.yaml
config read_yaml(config_path)class ChineseTextSplitter:def __init__(self,pdf: bool False,sentence_size: int config.get(SENTENCE_SIZE),):self.pdf pdfself.sentence_size sentence_sizedef split_text(self, text: str) - List[str]: ## 此处需要进一步优化逻辑if self.pdf:text re.sub(r\n{3,}, r\n, text)text re.sub(\s, , text)text re.sub(\n\n, , text)text re.sub(r([;.!?。\?])([^”’]), r\1\n\2, text)text re.sub(r(\.{6})([^’”」』]), r\1\n\2, text)text re.sub(r(\…{2})([^’”」』]), r\1\n\2, text)text re.sub(r([;!?。\?][’”」』]{0,2})([^;!?。\?]), r\1\n\2, text)text text.rstrip()ls [i for i in text.split(\n) if i]for ele in ls:if len(ele) self.sentence_size:ele1 re.sub(r([,.][’”」』]{0,2})([^,.]), r\1\n\2, ele)ele1_ls ele1.split(\n)for ele_ele1 in ele1_ls:if len(ele_ele1) self.sentence_size:ele_ele2 re.sub(r([\n]{1,}| {2,}[’”」』]{0,2})([^\s]), r\1\n\2, ele_ele1)ele2_ls ele_ele2.split(\n)for ele_ele2 in ele2_ls:if len(ele_ele2) self.sentence_size:ele_ele3 re.sub(( [’”」』]{0,2})([^ ]), r\1\n\2, ele_ele2)ele2_id ele2_ls.index(ele_ele2)ele2_ls (ele2_ls[:ele2_id] [i for i in ele_ele3.split(\n) if i] ele2_ls[ele2_id 1 :])ele_id ele1_ls.index(ele_ele1)ele1_ls (ele1_ls[:ele_id] [i for i in ele2_ls if i] ele1_ls[ele_id 1 :])id ls.index(ele)ls ls[:id] [i.strip() for i in ele1_ls if i] ls[id 1 :]return ls附录二可继续思考问题
可继续思考的问题
是否可以优化上述代码中对于长度的限制为什么要对长度进行限制长度限制可以调整吗怎样对重复性的ele2ele1与ele3的限制
这些问题我们将在本系列博文最后的部分拓展讨论。
2023年9月5日 徐鸿铎 于 西直门