true, 'fullwidth_space' => true, 'collapse_spaces' => true, 'remove_zwsp' => true, 'comma_cjk' => true, 'comma_latin' => true, 'period_cjk' => true, 'bracket_latin' => false, 'decode_html_entities' => false, 'english_journal' => false, ], $options); if (!empty($o['english_journal'])) { if (!array_key_exists('comma_cjk', $options)) { $o['comma_cjk'] = false; } if (!array_key_exists('period_cjk', $options)) { $o['period_cjk'] = false; } } if (!empty($o['decode_html_entities'])) { $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); } if (!empty($o['line_endings'])) { $text = str_replace(["\r\n", "\r"], "\n", $text); } if (!empty($o['fullwidth_space'])) { $text = str_replace("\u{3000}", ' ', $text); } if (!empty($o['remove_zwsp'])) { // 零宽空格、零宽非断空格、BOM、软连字符等(不改变可见字符) $text = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}\x{00AD}]/u', '', $text); } if (!empty($o['collapse_spaces'])) { $text = preg_replace('/[ \t]{2,}/u', ' ', $text); } $han = self::$han; if (!empty($o['comma_cjk'])) { // 汉字 , 汉字 → 汉字 , 汉字 $text = preg_replace('/(?<=[' . $han . ']),(?=[' . $han . '])/u', ',', $text); } if (!empty($o['comma_latin'])) { // 字母/数字 , 字母/数字 → , $text = preg_replace('/(?<=[0-9A-Za-z]),(?=[0-9A-Za-z])/u', ',', $text); } if (!empty($o['period_cjk'])) { // 汉字后的全角英文句点 FF0E → 中文句号 。 $text = preg_replace('/(?<=[' . $han . '])./u', '。', $text); } if (!empty($o['bracket_latin'])) { // ( 仅 ASCII + 常见标点 + 空格 ) $text = preg_replace_callback( '/(([0-9A-Za-z\s\.,;:\-\+/=]+))/u', static function ($m) { return '(' . $m[1] . ')'; }, $text ); } return $text; } /** * 对 HTML 片段做符号校对:只替换「标签外」的文本,不修改标签名与属性值。 * * 实现:按 `<...>` 切分,对偶数段(文本)调用 normalize(),奇数段(标签)原样保留。 * 注意:畸形 HTML、属性值中含未转义 `<` 时可能误判,复杂场景请先抽纯文本再校对。 * * @param string $html * @param array $options 同 normalize() * @return string */ public static function normalizeHtml($html, array $options = []) { $html = (string)$html; if ($html === '') { return ''; } $parts = preg_split('/(<[^>]*>)/u', $html, -1, PREG_SPLIT_DELIM_CAPTURE); if ($parts === false) { return self::normalize($html, $options); } $out = ''; foreach ($parts as $i => $chunk) { if ($chunk === '') { continue; } // 偶数索引为文本,奇数索引且以 < 开头为标签 if ($i % 2 === 1 && isset($chunk[0]) && $chunk[0] === '<') { $out .= $chunk; } else { $out .= self::normalize($chunk, $options); } } return $out; } /** * Abstract 专用:先 HTML 实体解码(> → > 等),再执行与普通正文相同的符号校对。 * * 适用于摘要字段在库中/接口中以 htmlspecialchars 形式存储的场景。 * 若摘要内本身含真实 HTML 标签且需保留标签结构,请改用 normalizeHtml() 并自行传入 decode_html_entities。 * * @param string $abstract * @param array $options 同 normalize(),默认会合并 decode_html_entities=true(可被显式 false 覆盖) * @return string */ public static function normalizeAbstract($abstract, array $options = []) { $opts = array_merge(['decode_html_entities' => true], $options); return self::normalize($abstract, $opts); } /** * 带 HTML 标签的摘要:仅在「标签外文本」中做实体解码 + 符号校对,不改动标签与属性。 * * @param string $html * @param array $options 同 normalize(),默认 decode_html_entities=true * @return string */ public static function normalizeAbstractHtml($html, array $options = []) { $opts = array_merge(['decode_html_entities' => true], $options); return self::normalizeHtml($html, $opts); } /** * 英文期刊 Abstract:实体解码 + 符号校对,且默认关闭中文专用标点规则。 */ public static function normalizeEnglishAbstract($abstract, array $options = []) { return self::normalizeAbstract($abstract, array_merge(['english_journal' => true], $options)); } /** * 英文期刊、带 HTML 的摘要(标签外文本):实体解码 + 符号校对,且默认关闭中文专用规则。 */ public static function normalizeEnglishAbstractHtml($html, array $options = []) { return self::normalizeAbstractHtml($html, array_merge(['english_journal' => true], $options)); } }