如何基于标点将超长文本分割为长度相近、不超长度上限的最少块?
文本按标点边界均等拆分方案
现有成熟方案说明
- 轻量无依赖场景:建议自行实现核心逻辑,无需引入第三方库
- NLP场景:可直接使用HuggingFace Tokenizer的
chunk_size+split_special_tokens相关配置,或NLTK、jieba的句子分割+分块组合能力,以上工具都原生支持按语义边界拆分的能力
核心算法实现逻辑
前置约束:单句长度必须小于阈值X,否则无法满足「不截断完整语句」的要求,需先处理超长单句场景。
实现步骤如下:
- 标记合法拆分点:遍历字符串,记录所有语句结束标点(.、!、?、。、!、?,可根据需求扩展)的后一个下标作为合法拆分位置,只有在这些位置拆分才不会截断完整语句。
- 确定最小拆分块数k:从最低要求k=2开始向上尝试,校验是否可以用k-1个拆分点将字符串分为k段,每段长度都小于X,找到满足条件的最小k值。
- 均等拆分:计算每块的理想长度
avg = 总长度 / k,遍历合法拆分点,优先选择让当前块长度最接近avg的位置拆分,保证所有块长度尽可能接近,且都不超过阈值。
Python实现代码示例
import re def split_string_by_punctuation(s: str, max_len: int) -> list: # 字符串本身长度小于阈值直接返回 if len(s) < max_len: return [s] # 匹配所有语句结束标点的位置,兼容中英文标点,可根据需求扩展标点列表 split_pattern = re.compile(r'[.。!!??,,;;]') split_points = [match.end() for match in split_pattern.finditer(s)] # 合法拆分点去重、排序 split_points = sorted(list(set(split_points))) # 校验是否存在单句超过max_len,无法按规则拆分 prev = 0 for p in split_points: if p - prev >= max_len: raise ValueError(f"存在单句长度超过阈值{max_len},无法按规则拆分") prev = p if len(s) - prev >= max_len: raise ValueError(f"存在单句长度超过阈值{max_len},无法按规则拆分") # 找到最小拆分块数k total_len = len(s) min_k = 2 while True: # 平均长度超过max_len直接跳过当前k if (total_len / min_k) >= max_len: min_k += 1 continue # 模拟贪心拆分校验可行性 current = 0 chunks = 0 for p in split_points: if p - current >= max_len: break if (p - current) >= (total_len / min_k) or p == split_points[-1]: chunks += 1 current = p if current >= total_len: break if current >= total_len and chunks <= min_k: break min_k += 1 # 执行最优均等拆分 avg = total_len / min_k res = [] current_start = 0 for i, p in enumerate(split_points): next_p = split_points[i+1] if i < len(split_points)-1 else total_len # 当前拆分点更接近平均长度,或下一个拆分点会超过长度阈值则拆分 if (p - current_start) >= avg or (next_p - current_start) >= max_len: res.append(s[current_start:p].strip()) current_start = p if current_start >= total_len: break if current_start < total_len: res.append(s[current_start:].strip()) return res # 示例测试 if __name__ == "__main__": max_len = 10 strings = ["Hello. How are you? I'm fine", "other string containg dots", "another string containg dots"] result = [] for s in strings: if len(s) > max_len: chunks = split_string_by_punctuation(s, max_len) result.extend(chunks) else: result.append(s) print(result)
内容的提问来源于stack exchange,提问作者Paolo Magnani
相关产品推荐
相关产品推荐

