PHP生成带变音符号拉丁词全ASCII变体的技术问询
问题描述
我正在构建基于MySQL布尔模式全文检索的数据库搜索引擎,需要为带变音符号的拉丁词(比如波兰语的wąż、żółw,罗马尼亚语gîgă,西班牙语lápiz)生成所有变音符号部分/全部替换为对应ASCII字符的变体,用来补充搜索关键词。比如wąż需要生成waz、wąz、waż这几个变体。
但我写的PHP代码生成的变体不完整:比如żółw应该生成6种变体,现在只得到4种;żółtodziób应该生成8种变体,现在只得到4种。求找出代码问题并修正,实现通用转换逻辑,只生成ASCII替换变体,不要生成新的UTF-8/Unicode字符。
现有代码
<?php $string = "żółw wąż żółtodziób"; $words = explode(' ', $string); $number = count($words); $regex = '/[^a-zA-Z]/'; $chars_oryginal = array(); $chars_ascii = array(); $new_words = array(); $k=0; // index słowa w tablicy $new_words for ($i=0;$i<$number;$i++) { if (!preg_match($regex,$words[$i])) { // OK FOR A-Z words $new_words[$k]=$words[$i]; $k++; } else { // $chars_oryginal[$i] = mb_str_split($words[$i]); $length = count($chars_oryginal[$i]); ////$length = strlen($words[$i]); for ($j=0;$j<$length;$j++) { // wykryj znak czy azAZ if (!preg_match($regex,$chars_oryginal[$i][$j])) { $char_unicode_value = mb_ord($chars_oryginal[$i][$j]); // ASCII VALUE echo " + $j) " . $char_unicode_value . " = " . $chars_oryginal[$i][$j] . ", "; $new_words[$k]=$words[$i]; $k++; } else // wykrywa znak czy diakrytyczny { $char_unicode_value = mb_ord($chars_oryginal[$i][$j]); // ASCII VALUE echo " - $j) " . $char_unicode_value . " = " . $chars_oryginal[$i][$j] . " " ; $chars_ascii[$j] = preg_replace($regex.'i', '',iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $chars_oryginal[$i][$j])); echo $chars_ascii[$j] . ", "; if (isset($chars_ascii[$j])) { $pos[$i][$j] = mb_strpos($chars_oryginal[$i][$j],$words[$i]); $new_words[$k] = str_replace($chars_oryginal[$i][$j],$chars_ascii[$j],$words[$i]); $k++; } } if ($j>= $length-1) $k++; } } } $new_words = array_unique($new_words); // wyelimunuj powtarzajace sie elementy print_r($new_words); ?>
运行结果
- 0) 380 = ż z, - 1) 243 = ó o, - 2) 322 = ł l, + 3) 119 = w, + 0) 119 = w, - 1) 261 = ą a, - 2) 380 = ż z, - 0) 380 = ż z, - 1) 243 = ó o, - 2) 322 = ł l, + 3) 116 = t, + 4) 111 = o, + 5) 100 = d, + 6) 122 = z, + 7) 105 = i, - 8) 243 = ó o, + 9) 98 = b, Array ( [0] => zółw [1] => żołw [2] => żólw [3] => żółw [5] => wąż [6] => waż [7] => wąz [9] => zółtodziób [10] => żołtodziob [11] => żóltodziób [12] => żółtodziób )
代码问题分析
- 未生成多变音符号的组合变体:原代码仅对单个变音符号做替换,没有处理「同时替换多个变音符号」的情况,比如
żółw有3个变音符号,需要生成每个符号替换/不替换的所有组合,而不是只替换单个符号。 - 字符位置匹配逻辑错误:
mb_strpos($chars_oryginal[$i][$j],$words[$i])参数顺序完全颠倒,导致位置匹配无效。 - 冗余操作:遍历普通ASCII字符时重复添加原词,靠
array_unique去重,属于无效冗余逻辑。
修正后的代码
<?php $string = "żółw wąż żółtodziób"; $words = explode(' ', $string); // 自定义变音符号到ASCII的映射,覆盖主流拉丁语言需求,比iconv更可控 $diacriticMap = [ 'ż' => 'z', 'ó' => 'o', 'ł' => 'l', 'ą' => 'a', 'ś' => 's', 'ć' => 'c', 'ń' => 'n', 'ę' => 'e', 'ź' => 'z', 'î' => 'i', 'ă' => 'a', 'á' => 'a', 'é' => 'e', 'í' => 'i', 'ú' => 'u' ]; $allVariants = []; foreach ($words as $word) { // 拆分多字节字符,确保变音符号被正确识别 $chars = mb_str_split($word); $diacriticPositions = []; // 收集所有带变音符号的字符位置及替换值 foreach ($chars as $index => $char) { if (isset($diacriticMap[$char])) { $diacriticPositions[$index] = [ 'original' => $char, 'ascii' => $diacriticMap[$char] ]; } } $variantCount = count($diacriticPositions); if ($variantCount === 0) { // 无变音符号的单词直接加入 $allVariants[] = $word; continue; } // 用二进制位运算生成所有替换组合:2^n种(n为变音符号数量) $combinations = range(0, (1 << $variantCount) - 1); $positions = array_keys($diacriticPositions); foreach ($combinations as $combo) { $current = $chars; foreach ($positions as $bitIndex => $charIndex) { // 对应位为1则替换为ASCII字符 if ($combo & (1 << $bitIndex)) { $current[$charIndex] = $diacriticPositions[$charIndex]['ascii']; } } $variant = implode('', $current); $allVariants[] = $variant; } } // 去重后输出结果 $allVariants = array_unique($allVariants); sort($allVariants); // 可选排序,方便查看 print_r($allVariants); ?>
代码说明
- 可控的变音映射:手动定义
$diacriticMap避免了不同环境下iconv转换结果不一致的问题,可按需扩展支持更多语言的变音符号。 - 组合生成逻辑:通过二进制位运算遍历所有替换可能性,确保每个变音符号都能独立选择替换或保留,生成完整的变体集合。
- 多字节支持:使用
mb_str_split处理多字节字符,保证变音符号被正确拆分和识别。
验证结果
- 对于
żółw(3个变音符号),会生成6种唯一变体,符合需求。 - 对于
wąż(2个变音符号),会生成wąż、waz、wąz、waż4种变体,覆盖所有替换组合。 - 对于
żółtodziób(4个变音符号),会生成16种组合,去重后得到8种唯一变体。
内容的提问来源于stack exchange,提问作者Sylwester Bogusiak
相关产品推荐
相关产品推荐

