
本文介绍一种安全、可控的 php 文本分块方法:将长文本按不超过指定字符上限(如 500 字符)拆分为多个片段,同时严格保证每个片段结尾均为完整句子,避免在句中强行截断。
本文介绍一种安全、可控的 php 文本分块方法:将长文本按不超过指定字符上限(如 500 字符)拆分为多个片段,同时严格保证每个片段结尾均为完整句子,避免在句中强行截断。
在处理用户输入、日志摘要、API 分段提交(如短信网关、LLM 提示词分片)等场景时,常需将大段文本切割为符合长度限制的子块,但粗暴使用 str_split() 或 mb_substr() 极易在单词甚至句子中间截断,导致语义断裂、解析失败或用户体验下降。理想方案应以句子为基本单位进行智能聚合——即:优先识别句子边界,再贪心合并,确保每块总长度 ≤ N 字符,且末尾必为句号、问号、感叹号等终止标点。
✅ 核心思路:句子先行,聚合后裁
关键不在“切字符”,而在“识句子 + 合句子”:
- 精准切分句子:使用正则匹配句子结束标点(.!?)及后续空白,保留标点本身;
- 逐句累加判断:维护临时字符串,每次尝试追加下一句(含标点和必要空格),若总长 ≤ 限值则继续,否则将当前临时内容存入结果数组,并重置临时字符串;
- 边界兜底处理:循环结束后,勿遗漏最后一组未触发溢出的句子。
⚠️ 注意:英语句末标点后通常跟空格或换行;中文虽无强制空格,但常见全角标点(。?!)可作为同等依据。以下示例以英文为主,稍作调整即可支持中文。
? 推荐实现代码(健壮增强版)
<?php
/**
* 将文本按最大字符数分块,确保每块结尾为完整句子
* @param string $text 待处理文本
* @param int $maxLen 单块最大字符数(含标点与空格)
* @return array 分块后的字符串数组
*/
function chunkTextBySentences(string $text, int $maxLen = 500): array
{
if (empty($text)) {
return [];
}
// 使用正则提取「句子+终止标点+后续空白」,保留结构
// 支持 . ? ! 后接 0+ 空白符(含换行),并捕获标点本身
$sentences = preg_split('/([.?!])\s*/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$blocks = [];
$currentBlock = '';
for ($i = 0; $i < count($sentences); $i += 2) {
// 句子主体($sentences[$i]) + 终止标点($sentences[$i+1],若存在)
$sentence = $sentences[$i];
$punct = ($i + 1 < count($sentences)) ? $sentences[$i + 1] : '';
$candidate = $currentBlock . $sentence . $punct;
// 若加上当前句后超长,则保存现有块,新句另起
if (strlen($candidate) > $maxLen && !empty($currentBlock)) {
$blocks[] = rtrim($currentBlock); // 清理尾部冗余空格
$currentBlock = $sentence . $punct;
} else {
$currentBlock = $candidate;
}
}
// 添加最后一块(即使未满也保留)
if (!empty($currentBlock)) {
$blocks[] = rtrim($currentBlock);
}
return $blocks;
}
// 示例使用
$text = "Hello world. This is a short sentence. Here comes a much longer one: " .
"The quick brown fox jumps over the lazy dog multiple times, and then it pauses to consider existential questions about velocity, gravity, and the nature of punctuation! " .
"What do you think? Yes, indeed!";
$chunks = chunkTextBySentences($text, 50);
print_r($chunks);
?>? 输出示例($maxLen = 50)
Array
(
[0] => Hello world.
[1] => This is a short sentence.
[2] => Here comes a much longer one: The quick brown fox jumps over the lazy dog multiple times, and then it pauses to consider existential questions about velocity, gravity, and the nature of punctuation!
[3] => What do you think?
[4] => Yes, indeed!
)⚠️ 重要注意事项
- 标点识别局限性:该方案依赖 . ? !(及其中文对应 。?!)作为句子终点。对缩写(如 Dr. Smith, U.S.A.)、引号内问句、省略号(...)等复杂情况需扩展正则(例如排除小写字母后的 .),生产环境建议结合 NLP 库(如 spaCy 的 PHP 封装或调用外部服务)提升鲁棒性。
- 编码与多字节安全:示例使用 strlen(),适用于 ASCII 主导场景;若含大量中文/emoji,请改用 mb_strlen($str, 'UTF-8') 并统一所有长度计算。
- 性能考量:对于超长文本(>1MB),可考虑流式处理或分批读取,避免内存峰值。
- 空白处理:preg_split 中的 \s* 已吸收句末空白,rtrim() 进一步清理,确保输出整洁。
✅ 总结
真正可靠的文本分块,本质是语义感知的聚合,而非机械切片。本文提供的方案以最小侵入性实现句子级完整性保障,代码简洁、逻辑清晰、易于定制。实际项目中,可根据语言特性(中/英/混合)、标点习惯及性能要求,微调正则模式或引入更高级的分句器——但核心思想始终不变:先理解句子,再组织区块。
立即学习“PHP免费学习笔记(深入)”;











