中文字符佔 2 個視覺單位,英文 1 個。直接用 strlen/mb_strlen 截斷會導致對齊錯亂。本文提供一個逐字符掃描、累加視覺寬度、超寬自動換行的實用函數。
/** * 按視覺寬度換行輸出中英文混排字符串 * @param string $str 輸入字符串(UTF-8) * @param int $len 每行最大視覺寬度(中文算 2,英文算 1) * @return string 包含
標籤的 HTML 字符串 */ function rep(string $str, int $len): string { $strlen = mb_strlen($str, 'UTF-8'); $finstr = ''; $pos = 0; // 當前行已佔視覺寬度 for ($i = 0; $i < $strlen; $i++) { $char = mb_substr($str, $i, 1, 'UTF-8'); $ord = ord($char); // 單字節情況下可用,多字節需 mb_ord (PHP 8.0+) // 簡易判斷:多字節字符視為中文(寬度 2) // 嚴謹做法請用 grapheme_strlen / IntlChar::charWidth $isMultiByte = (mb_strlen($char, 'UTF-8') > 1); $charWidth = $isMultiByte ? 2 : 1; // 處理控制字符 if ($char === "\n" || $char === "\r") { $finstr .= '
'; $pos = 0; continue; } if ($char === ' ') { $finstr .= ' '; $pos += 1; continue; } // 普通可見字符 $finstr .= htmlspecialchars($char); $pos += $charWidth; // 超寬換行 if ($pos >= $len) { $finstr .= '
'; $pos = 0; } } return $finstr; }
function repModern(string $str, int $len): string {
$finstr = '';
$pos = 0;
$graphemes = grapheme_str_split($str); // 正確分割字素簇
foreach ($graphemes as $g) {
$width = 1;
$codepoint = IntlChar::ord($g);
// 簡易:東亞寬度字符 (A/W/F) 算 2
$eaw = IntlChar::getIntPropertyValue($codepoint, IntlChar::PROPERTY_EAST_ASIAN_WIDTH);
if (in_array($eaw, [IntlChar::EAST_ASIAN_WIDTH_WIDE, IntlChar::EAST_ASIAN_WIDTH_FULLWIDTH, IntlChar::EAST_ASIAN_WIDTH_AMBIGUOUS])) {
$width = 2;
}
if ($g === "\n" || $g === "\r") {
$finstr .= '
';
$pos = 0;
continue;
}
$finstr .= htmlspecialchars($g);
$pos += $width;
if ($pos >= $len) {
$finstr .= '
';
$pos = 0;
}
}
return $finstr;
}
$text = "Hello 世界!這是一個測試 test string."; echo rep($text, 20); /* 輸出近似: Hello 世界!這是一
個測試 test
string. */
提示:現代 Web 開發首選 CSS `word-break`、`overflow-wrap`、Flex/Grid 布局 由瀏覽器自動換行,僅在無 CSS 環境時使用此類後端函數。