PHP CLI应用含ANSI转义码文本的可靠换行方案问询
解决含ANSI转义码的CLI文本按实际长度换行问题
核心问题在于wordwrap()和strlen()会把非打印的ANSI转义码计入字符长度,导致换行位置偏移。要解决这个问题,需要分离样式代码与可见文本,基于纯文本长度计算换行位置,同时保证样式在换行后能正确延续。
实现思路
- 将带ANSI样式的文本拆分为「ANSI样式段」和「纯文本段」两类片段;
- 跟踪当前生效的所有ANSI样式;
- 逐段处理纯文本,累计可见长度,达到80字符时插入换行,并将当前样式自动附加到新行开头;
- 最后输出剩余内容。
具体代码实现
首先实现一个辅助函数,拆分ANSI文本为样式和纯文本片段:
function splitAnsiText(string $text): array { $segments = []; // 匹配ANSI控制序列(CSI,即\x1b[开头的样式码) $pattern = '/(\x1b\[[0-9;]*m)|([^\x1b]+)/'; preg_match_all($pattern, $text, $matches, PREG_SET_ORDER); foreach ($matches as $match) { if (isset($match[1]) && $match[1] !== '') { $segments[] = ['type' => 'ansi', 'content' => $match[1]]; } elseif (isset($match[2]) && $match[2] !== '') { $segments[] = ['type' => 'text', 'content' => $match[2]]; } } return $segments; }
然后修改你的line()方法,实现基于可见长度的换行逻辑:
public function line(string $text): void { $segments = splitAnsiText($text); $currentLine = ''; $currentVisibleLength = 0; $currentAnsiStyles = []; // 记录当前生效的所有ANSI样式 foreach ($segments as $segment) { if ($segment['type'] === 'ansi') { // 保存当前样式,并追加到当前行 $currentAnsiStyles[] = $segment['content']; $currentLine .= $segment['content']; continue; } // 处理纯文本片段,逐段填充到当前行 $remainingText = $segment['content']; $remainingLength = strlen($remainingText); while ($remainingLength > 0) { $availableSpace = 80 - $currentVisibleLength; // 当前行已满,换行并保留样式 if ($availableSpace <= 0) { $this->rawLine($currentLine); $currentLine = implode('', $currentAnsiStyles); $currentVisibleLength = 0; $availableSpace = 80; } // 截取能放入当前行的文本长度 $takeLength = min($remainingLength, $availableSpace); $takeText = substr($remainingText, 0, $takeLength); $currentLine .= $takeText; $currentVisibleLength += $takeLength; // 更新剩余文本 $remainingText = substr($remainingText, $takeLength); $remainingLength = strlen($remainingText); } } // 输出最后一行剩余内容 if ($currentLine !== '') { $this->rawLine($currentLine); } }
说明
- 该方法会自动处理样式延续:比如文本中途设置了红色加粗,换行后新行的文本会继续保持红色加粗,直到遇到重置样式的ANSI码(如
\x1b[0m); - 正则表达式匹配了常见的ANSI CSI序列,覆盖大多数文本样式需求(颜色、加粗、下划线等);
- 不需要依赖第三方库,纯原生PHP实现,适合轻量CLI应用。
内容的提问来源于stack exchange,提问作者NightHawk
相关产品推荐
相关产品推荐

