登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP Normalizer 怎样读取字符的原始分解映射

来源:17golang原创

时间:2026-10-30 18:42:38 137浏览 收藏

PHP 里要查看某个字符的原始分解映射,直接调用 Normalizer::getRawDecomposition() 即可。它面向的是一个单独的 UTF-8 码点:如果 Unicode 字符数据库里存在 Decomposition_Mapping,返回对应的字符串;如果该字符没有映射,返回 null;如果输入不是一个合法的单码点,则应结合 intl_get_error_code() 和 intl_get_error_message() 读取错误原因。

官方地址:https://www.php.net/manual/en/normalizer.getrawdecomposition.php

要点速览
  • 方法属于 PHP intl 扩展,输入契约是一个 UTF-8 编码的单个码点。
  • 返回字符串表示存在原始分解映射,null 表示没有映射,不等于输入一定出错。
  • 空字符串、多码点字符串或非法 UTF-8 应通过 intl 错误码单独处理。

先明确 getRawDecomposition 读取的是什么

getRawDecomposition() 读取的是 Unicode Character Database 中的 Decomposition_Mapping 属性,而不是把一整段文本直接规范化。调用者需要把一个字符作为 UTF-8 字符串传入;字符串里如果包含两个可见字符,即使它们都能正常显示,也不再符合这个方法的单码点输入契约。

PHP Normalizer 单个 UTF-8 码点、Unicode 字符记录与 Decomposition_Mapping 返回值的静态关系图
图1:PHP Normalizer 原始分解映射的静态关系图,不是运行截图。

这里的“原始”很重要:它描述字符记录上的分解映射。若业务目标是把一段文本转成 NFC、NFD、NFKC 或 NFKD,应使用 Normalizer::normalize();若目标是查看某个码点是否声明了映射,才使用这个方法。两者都属于规范化相关 API,但用途不同。

最小写法:接收字符串或 null

先确认服务器启用了 intl 扩展,然后用严格的 null 判断接收结果。不要用真假判断代替 $decomposition === null,因为这里要区分“没有映射”和后续格式化逻辑。

第二个参数可以显式写出,也可以使用默认值。示例里的 FORM_C 让调用意图更清楚,但不要把它理解成“先把输入规范化再返回结果”;方法返回的仍然是字符记录的分解映射。

用返回值和 intl 错误码判断结果

遇到 null 时,下一步要看 intl 的错误状态。一个合法、但没有映射的字符可以返回 null 且错误码为 U_ZERO_ERROR;空字符串、多个码点和非法 UTF-8 则属于输入问题。下面把三类结果放到同一段处理逻辑中。

PHP Normalizer 有映射、无映射和非法输入三种结果边界的静态说明图
图2:Normalizer 输入与结果边界说明图,不是运行截图。
 'mapped',
            'value' => $value,
            'hex' => bin2hex($value),
            'error_code' => $errorCode,
            'error_message' => $errorMessage,
        ];
    }

    if ($errorCode !== U_ZERO_ERROR) {
        // null 加上非零错误码,表示输入或 ICU 处理过程出现错误。
        return [
            'status' => 'error',
            'value' => null,
            'hex' => null,
            'error_code' => $errorCode,
            'error_message' => $errorMessage,
        ];
    }

    // null 且没有错误时,是合法字符但没有 Decomposition_Mapping。
    return [
        'status' => 'unmapped',
        'value' => null,
        'hex' => null,
        'error_code' => $errorCode,
        'error_message' => $errorMessage,
    ];
}

$result = inspectDecomposition("Å");
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

代码把状态分成 mapped、unmapped 和 error,这样调用方不必猜测 null 的含义。对于面向用户的页面,可以把 error_message 记录到日志而不是原样展示;对于内部调试工具,十六进制字段有助于确认返回字符串的 UTF-8 字节。

输入先做单码点边界检查

官方方法本身会检查输入,但在表单、文件或接口数据进入业务层时,提前判断可以让错误更靠近数据入口。mb_strlen($value, 'UTF-8') === 1 只适用于判断一个 Unicode 码点;组合字符可能由多个码点组成,不能把“用户看到的一个字形”简单等同于一个码点。

 'invalid-input',
            'message' => '请输入一个 UTF-8 码点',
        ];
    }

    $value = Normalizer::getRawDecomposition($input);
    if ($value === null) {
        // 读取错误状态,避免把真正的 ICU 输入错误误报成“没有映射”。
        $code = intl_get_error_code();
        if ($code !== U_ZERO_ERROR) {
            return [
                'status' => 'error',
                'message' => intl_get_error_message(),
            ];
        }
        return [
            'status' => 'unmapped',
            'message' => '该码点没有原始分解映射',
        ];
    }

    return [
        'status' => 'mapped',
        'value' => $value,
    ];
}

如果输入可能包含损坏的字节,最好在更早的边界上统一做 UTF-8 解码策略;不要只依赖长度检查。长度检查解决的是“一个还是多个码点”,并不替代编码有效性处理。

循环读取时保留原始字符和映射的对应关系

批量处理字符表时,可以为每个码点保留输入、状态、映射和十六进制值。这样后续做搜索索引、排序规则分析或兼容性报告时,能知道某个结果来自哪个原始字符,而不是只留下一个难以追溯的分解字符串。

这里没有把 null 替换成空字符串,因为空字符串会掩盖“无映射”和“映射结果为空”的语义差异。输出给 JSON 时也建议保留 status 字段,前端或下游任务可以据此决定显示提示、跳过记录或记录错误。

和 normalize() 的用途不要混在一起

目标推荐 API返回重点
查看一个码点的原始分解映射getRawDecomposition()映射字符串或 null
把字符串转换到指定规范化形式normalize()规范化后的字符串或失败结果
判断字符串是否已经处于某种形式isNormalized()布尔值

如果你是在做用户名比较、搜索键生成或数据库归一化,先定义业务需要的是“查看 Unicode 属性”还是“生成规范化文本”。前者使用原始映射读取;后者应围绕 normalize() 设计,并明确选择 NFC、NFD、NFKC 或 NFKD。不要因为两者都属于 Normalizer 就把返回值直接互换。

相关问题

为什么 getRawDecomposition() 返回 null?

合法字符没有 Decomposition_Mapping 时就会返回 null。先检查 intl_get_error_code();如果错误码为零,通常应把它归类为“无映射”,而不是异常。

一个中文字符可以直接传给这个方法吗?

可以,只要字符串代表一个合法的 UTF-8 码点。一个用户看到的字形可能由多个码点组合而成,遇到组合字符时应按业务规则拆分或拒绝,而不是把视觉上的一个字当成一个码点。

读取原始映射前必须先调用 normalize() 吗?

不需要。读取映射和规范化文本是两个不同任务;如果先规范化,可能已经改变了你想观察的输入字符。

如何查看映射结果对应的字节?

对非 null 的返回字符串调用 bin2hex() 即可。它只适合调试和记录,不要把十六进制文本当成真正的字符内容保存。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>