Skip to content

为社交媒体分割长消息:处理字符限制的 PHP 工具

简介

在构建聊天机器人、社交媒体工具或 AI 驱动的应用程序时,你将不可避免地面临一个常见挑战:你想发送的文本超过了平台允许的长度。

Instagram 私信限制为 1,000 个字符。Twitter/X 将帖子限制在 280 个字符。SMS 消息在 160 个字符处分割。而当你处理 AI 生成的回复时,它们很少遵守这些限制。

简单的方法——在字符限制处精确截断文本——会导致单词被切断和消息难以阅读。你需要的是智能分割,能够尊重单词边界并产生干净、可读的片段。

问题

考虑这个场景:你的 AI 助手为客户查询生成了 3,000 字符的回复。你需要通过 Instagram 私信发送(限制 1,000 字符)。简单地在位置 1000 处截断可能产生:

...最好的方法是重新配

而不是:

...最好的方法是

你的用户值得更好的体验。

解决方案

这是一个 PHP 工具函数,它在自然边界——空格和换行符——处分割长消息,同时遵守你的字符限制:

php
public static function splitMessageByLength(string $message, int $length = 4096): array
{
    // 将换行符标准化为 \n
    $message = str_replace("\r\n", "\n", $message);
    $message = str_replace("\r", "\n", $message);

    $messages = [];
    $current_message = '';

    // 逐字符遍历消息
    for ($i = 0, $count = mb_strlen($message, 'UTF-8'); $i < $count; $i++) {
        $char = mb_substr($message, $i, 1, 'UTF-8');

        // 添加字符
        $current_message .= $char;

        // 检查当前消息是否超过限制
        if (mb_strlen($current_message, 'UTF-8') >= $length) {
            // 找到最后一个空格或换行符的位置
            $lastSpace = mb_strrpos($current_message, ' ', 0, 'UTF-8');
            $lastBreak = mb_strrpos($current_message, "\n", 0, 'UTF-8');

            // 确定分割位置(优先选择换行符而非空格)
            $splitPos = $lastBreak !== false ? $lastBreak : $lastSpace;

            // 如果没有空格或换行符,强制在最大长度处分割
            if ($splitPos === false || $splitPos === 0) {
                $splitPos = $length;
            }

            // 将当前片段添加到消息数组
            $messages[] = mb_substr($current_message, 0, $splitPos, 'UTF-8');

            // 用剩余内容开始下一个片段
            $current_message = mb_substr($current_message, $splitPos, null, 'UTF-8');
        }
    }

    // 如果有剩余内容则添加
    if (!empty($current_message)) {
        $messages[] = $current_message;
    }

    // 清理:去除空白并过滤空字符串
    $messages = array_map('trim', $messages);
    $filtered_messages = array_filter($messages, function ($message) {
        return (bool) $message;
    });

    return array_values($filtered_messages);
}

工作原理

该算法遵循以下步骤:

  1. 标准化换行符 - 将 Windows (\r\n) 和旧版 Mac (\r) 换行符转换为 Unix 风格 (\n),以保持一致的处理。

  2. 逐字符遍历 - 使用多字节安全函数(mb_strlenmb_substr)来正确处理 Unicode 字符,如表情符号和带重音的字母。

  3. 智能边界检测 - 当达到限制时,向后查找最后一个空格或换行符。换行符优先,因为它们代表更自然的分割点。

  4. 边缘情况回退 - 如果不存在合适的分割点(例如,一个非常长的单词),则在精确限制处强制分割。

  5. 清理 - 去除每个片段的空白,并删除可能因分割而产生的空字符串。

使用示例

基本用法

php
$longMessage = "这是一条需要被分割的非常长的消息...";
$chunks = TextUtil::splitMessageByLength($longMessage, 100);

foreach ($chunks as $index => $chunk) {
    echo "第 " . ($index + 1) . " 部分: " . $chunk . "\n";
}

特定平台限制

php
class MessageSplitter
{
    const INSTAGRAM_DM = 1000;
    const TWITTER = 280;
    const SMS = 160;
    const WHATSAPP = 4096;
    const TELEGRAM = 4096;

    public static function forInstagram(string $message): array
    {
        return TextUtil::splitMessageByLength($message, self::INSTAGRAM_DM);
    }

    public static function forTwitter(string $message): array
    {
        return TextUtil::splitMessageByLength($message, self::TWITTER);
    }

    public static function forSMS(string $message): array
    {
        return TextUtil::splitMessageByLength($message, self::SMS);
    }
}

处理 AI 回复

php
// 获取 AI 回复(可能非常长)
$aiResponse = $anthropicService->chat($prompt);

// 为 Instagram 私信发送进行分割
$parts = TextUtil::splitMessageByLength($aiResponse, 1000);

foreach ($parts as $part) {
    $instagramApi->sendDirectMessage($userId, $part);
    // 添加延迟以保持消息顺序
    usleep(500000); // 500毫秒
}

平台字符限制参考

平台限制备注
Instagram 私信1,000每条消息
Instagram 标题2,200在信息流中截断为 125
Twitter/X280高级用户有更多
SMS160更长 = 多个分段
WhatsApp4,096每条消息
Telegram4,096每条消息
Facebook 帖子63,206但 40-80 字符最佳
LinkedIn3,000在信息流中截断为 140

最佳互动效果

研究表明,较短的消息能获得更好的互动。Twitter 上少于 100 个字符的帖子互动率高 17%。Instagram 标题在 138-150 个字符之间效果最佳。

值得考虑的增强功能

添加部分编号

php
public static function splitWithNumbers(string $message, int $length): array
{
    $chunks = self::splitMessageByLength($message, $length - 10); // 预留空间
    $total = count($chunks);

    if ($total === 1) {
        return $chunks;
    }

    return array_map(function ($chunk, $index) use ($total) {
        return $chunk . "\n\n(" . ($index + 1) . "/" . $total . ")";
    }, $chunks, array_keys($chunks));
}

保留段落结构

php
public static function splitByParagraphs(string $message, int $length): array
{
    $paragraphs = explode("\n\n", $message);
    $chunks = [];
    $current = '';

    foreach ($paragraphs as $para) {
        $test = $current ? $current . "\n\n" . $para : $para;

        if (mb_strlen($test, 'UTF-8') <= $length) {
            $current = $test;
        } else {
            if ($current) {
                $chunks[] = $current;
            }
            // 如果单个段落超过限制,使用字符分割
            if (mb_strlen($para, 'UTF-8') > $length) {
                $chunks = array_merge($chunks,
                    self::splitMessageByLength($para, $length));
                $current = '';
            } else {
                $current = $para;
            }
        }
    }

    if ($current) {
        $chunks[] = $current;
    }

    return $chunks;
}

表情符号注意事项

表情符号可能是 1-4 个字节,但根据平台不同可能算作 1-2 个字符。请使用包含大量表情符号的内容进行充分测试,以确保准确分割。

总结

分割长消息可能看起来微不足道,但正确地做到这一点——尊重单词边界、正确处理 Unicode、清理结果——这是专业应用程序和令人沮丧的用户体验之间的区别。

关键要点:

  • 始终使用多字节字符串函数以确保 Unicode 安全
  • 优先选择自然分割点(换行符 > 空格 > 强制截断)
  • 通过去除空白和过滤空片段来清理结果
  • 根据你的用例考虑特定平台的优化

当将 AI 服务集成到消息平台时,这个工具变得特别有价值,因为回复长度是不可预测的,而字符限制是严格的。