PHP如何使用 Unicode 标准化移除字符串中的重音符号

在处理国际化文本、生成 URL 别名(Slug)或进行文本搜索时,我们经常需要将带有重音符号的字符(如 Áñü)转换为其基础的 ASCII 字符(如 Anu)。

本文档将通过一个 PHP 示例函数 remove_accents,详细解释如何利用 Symfony 的 Polyfill 库和 Unicode 标准化(Normalization)来实现这一功能。

1. 代码示例

以下是核心功能的 PHP 代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

require 'vendor/autoload.php'; // 确保加载了 Composer 自动加载文件

use Symfony\Polyfill\Intl\Normalizer\Normalizer;

/**
* 移除字符串中的重音符号
*
* @param string $string 输入的包含重音字符的字符串
* @return array|string|null 返回去除重音后的字符串
*/
function remove_accents($string): array|string|null
{
// 步骤 1: Unicode 标准化 (分解模式)
// 将字符分解为 基字符 + 组合字符 (例如: 'Á' -> 'A' + '◌́')
$normalized = Normalizer::normalize($string, Normalizer::FORM_D);

// (可选) 打印标准化后的中间结果用于调试
// 注意:在终端中可能看起来没有变化,但底层字节已经改变
echo "Normalized (NFD): " . $normalized . PHP_EOL;

// 步骤 2: 使用正则表达式移除组合字符 (重音符号)
// \p{Mn} 匹配任何 "Mark, Nonspacing" (非间距标记)
// /u 修饰符开启 UTF-8 模式
return preg_replace('/\p{Mn}+/u', '', $normalized);
}

// 测试用例
$text = "Árbol rascador para Gatos";

echo "Original: " . $text . PHP_EOL;
echo "Result: " . remove_accents($text) . PHP_EOL;

预期输出:

1
2
Original: Árbol rascador para Gatos
Result: Arbol rascador para Gatos

2. 原理解析

这个函数的工作原理可以分为两个关键步骤:标准化 (Normalization) 和 **过滤 (Filtering)**。

步骤 1: Unicode 标准化 (Normalization Form D - NFD)

1
$normalized = Normalizer::normalize($string, Normalizer::FORM_D);
  • 什么是 NFD? Unicode 允许用两种方式表示同一个带重音的字符:
    1. **预组合字符 (Precomposed Character)**:例如 Á (U+00C1),它是一个单独的代码点。
    2. **分解字符 (Decomposed Characters)**:例如 A (U+0041) + ´ (U+0301,结合锐音符)。这两个代码点组合在一起显示时,看起来也是 Á
  • Normalizer::FORM_D 的作用: 它将字符串中的所有“预组合字符”强制分解为“基字符”加上“组合字符”。
    • 输入:Á
    • NFD 处理后:A + ◌́ (非间距重音符)

步骤 2: 正则替换 (Regular Expression Replacement)

1
preg_replace('/\p{Mn}+/u', '', $normalized);
  • **\p{Mn}**:这是一个 Unicode 属性转义。
    • p 代表 “Property” (属性)。
    • Mn 代表 “Mark, Nonspacing” (标记,非间距)。这正是 NFD 分解出来的那些重音符号、变音符所属的类别。
  • **+**:匹配一个或多个连续的非间距标记。
  • **/u**:这是一个非常重要的修饰符,告诉 PHP 的 PCRE 引擎将模式字符串视为 UTF-8 编码,这对于处理多字节的 Unicode 字符是必须的。

结果: 通过将所有属于 Mn 类别的字符替换为空字符串,我们就只剩下了纯净的基字符(例如 A),从而实现了“去除重音”的效果。

3. 依赖项

此脚本使用了 Symfony\Polyfill\Intl\Normalizer\Normalizer。这是一个 Polyfill 库,用于在系统缺少原生 PHP intl 扩展时提供 Normalizer 类。

如果你没有安装它,可以通过 Composer 安装:

1
composer require symfony/polyfill-intl-normalizer

如果你的环境已经安装并启用了 PHP intl 扩展


PHP如何使用 Unicode 标准化移除字符串中的重音符号
https://kingjem.github.io/2025/12/04/php-处理重读字符/
作者
Ruhai
发布于
2025年12月4日
许可协议