如何去除重音并将字母转换为“普通” ASCII 字符?

48

如何最有效地从字符串中去除重音符号,例如 ÈâuÑ 变为 Eaun

是否有一种简单的内置方法或正则表达式可以实现这个功能?


7
@Peeps:告诉用户去谷歌搜索是违反 Stack Overflow 的礼仪的。如果问题在网站上不存在,最好还是提问,即使 OP 已经知道答案,因为这将增加我们非重复问题的数量。也许下一次有人用谷歌搜索时,他们会发现这个问题,然后我们就会有一个新用户了。 - Andreas Bonini
@Andreas 说得好。然而,这肯定是一个SO的重复问题,所以Peeps有点道理 :) 不过我现在太懒了,不想去搜索它。 - Pekka
5个回答

57

我找到了一个解决方案,这个方案在我所有的测试用例中都有效(从http://php.net/manual/en/transliterator.transliterate.php复制得来):

var_dump(transliterator_transliterate('Any-Latin; Latin-ASCII; [\u0080-\u7fff] remove',
    "A æ Übérmensch på høyeste nivå! И я люблю PHP! есть. fi ¦"));
// string(50) "A ae Ubermensch pa hoyeste niva! I a lublu PHP! est. fi "

查看: http://www.php.net/normalizer

编辑: 这个解决方案与使用 setlocale() 设置的区域设置无关。与 iconv() 相比的另一个好处是,即使非拉丁字符也不会被忽略。

编辑2: 我发现,一些字符并没有被我最初发布的音译方式所覆盖。 Any-Latin 将西里尔字母 ь 翻译为一个不适合于拉丁字符集的字符:ʹ (http://en.wikipedia.org/wiki/Prime_%28symbol%29)。我添加了 [\u0100-\u7fff] remove 来删除所有这些非拉丁字符。我还在文本中添加了一个测试 ;)

我建议,他们在这里所提到的“Latin”指的是拉丁字母表,而不是拉丁字符集之一。但无论如何 - 在我看来,他们应该将其转换为某些 ASCII 字符,然后使用 Latin-ASCII ...

编辑3: 对于此处的更改再次表示抱歉。我不得不将字符降至 u0080 而不是 u0100,以获得仅 ASCII 字符作为输出。上面的测试已更新。


4
需要 php_intl.dll 扩展被启用。 - Oriol
我同意,这也是我最喜欢的函数!(而且我尝试了很多) - lokers
非常好的解决方案,非常易于使用,并且比其他使用str_replace的解决方案更有用。 - Baptiste Donaux
4
需要注意的是,这不仅仅是像OP要求的那样转换文本,还将删除一些字符,例如欧元符号 € 将会被删除。为了保留这些字符,只需将 'Any-Latin; Latin-ASCII;' 作为第一个参数传递即可。如果需要,您可以随后使用 iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $str) 将 "€" 转换为 "EUR"。 - Skacc

57
如果您安装了iconv,可以尝试以下内容(该示例假设输入字符串为UTF-8):
echo iconv('UTF-8', 'ASCII//TRANSLIT', $string);

iconv是一种库,用于在各种编码之间进行转换;它高效且默认情况下包含在许多PHP发行版中。最重要的是,它绝对比尝试自己制作解决方案更容易且更可靠(你知道有一个“带卷曲的拉丁字母N”吗?我也不知道)。


11
抢先我了。这应该是最好的选择。但请注意,如果输入中有无效字符(使用"ASCII//TRANSLIT//IGNORE"可以帮助),则往往会失败,而且像往常一样,如果遇到问题,可以阅读用户贡献的注释。http://www.php.net/manual/en/function.iconv.php - Pekka
5
由于某些原因,有时我无法让它正常工作。请参见http://codepad.viper-7.com/SUufA4 但在另一台机器上,我得到了“`E^au~N”。虽然不是期望的结果。 - Artefacto
1
这个inconv有一些冲突,所以我会问一个类似的问题。 - Mark Lalor
6
起初这个方法对我没用,重音符号只显示成问号。根据PHP手册页面上一个iconv()函数的评论,我首先运行了:setlocale(LC_ALL,'en_CA.utf8'); 然后一切都完美地工作了。'en_CA.utf8'是我系统的默认语言环境。可以尝试输入“locale -a”来查看可用的语言环境列表。 - Professor Falken
7
这对我解决了问号问题。 `setlocale(LC_ALL, "en_US.utf8");$string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);` 注:该代码用于将字符串中的非ASCII字符转换为ASCII字符,同时忽略无法转换的字符,以解决一些显示问号的问题。 - Josh Bernfeld
显示剩余4条评论

19

应 @palantir 的请求重新发布此问题...

我发现iconv非常不可靠,而我也不喜欢使用preg_replace解决方案和大型数组...因此,在所有方法中,我最喜欢的方式(也是我找到的唯一可靠的方法)是...

function toASCII( $str )
{
    return strtr(utf8_decode($str), 
        utf8_decode(
        'ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'),
        'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
}

1
你还应该加入以下字母:őŐűŰ。谢谢 :) - Sk8erPeter
15
这不是一个可靠的方法。对于波兰口音字符(如ŻŹĆŃĄŚŁĘÓżźćńąśłęó)无效。尝试 var_dump(strtr(utf8_decode('qqqqŻŹĆŃĄŚŁĘÓżźćńąśłęóqqq'), utf8_decode('ŻŹĆŃĄŚŁĘÓżźćńąśłęó'),'ZZCNASLEOzzcnasleo')); 我得到了 string(25) "qqqqeeeeeeeeOeeeeeeeeoqqq"。使用 iconv 更加可靠,例如 var_dump(iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', 'qqqqŻŹĆŃĄŚŁĘÓżźćńąśłęóqqq'));,我得到了 string(25) "qqqqZZCNASLEOzzcnasleoqqq" - piotrekkr
2
将“Горловка”转换为“YYYYYYYY”,不好。 - Tebe
它在性能方面并不是最好的,而且还会产生错误的结果。像Œ、Æ等字母应该分解为两个字母,而不是一个字母。 - Hasnat Safder

13
你可以使用iconv将字符转换为普通的US-ASCII,然后使用正则表达式删除非字母字符:
preg_replace('/[^a-z]/i', '', iconv("UTF-8", "US-ASCII//TRANSLIT", $text))

另一种方法是使用Normalizer来将文本规范化为规范化形式KD(NFKD),然后删除标记字符:

preg_replace('/\p{Mn}/u', '', Normalizer::normalize($text, Normalizer::FORM_KD))

“ISO-8859-1”?你确定吗?这样做不会留下至少ÄÖÜ(它们的8859-1对应项)吗? - Pekka
1
下投票的原因是什么? - Gumbo
1
踩负评不是我的。但是,楼主并没有要求删除非字母字符,对吧? - Pekka
这是我的。现在你已经修复了,它被还原了。 - Artefacto
2
@Pekka:使用 iconv 轉寫 ÈâuÑ 得到 \E^au~N`。這就是為什麼要使用以下清理的原因。 - Gumbo
显示剩余6条评论

12
注:我是从另一个类似的问题中转载过来,希望对其他人有所帮助。
我最终编写了一个PHP库,基于Django项目中的URLify.js,因为我发现iconv()太不完整了。您可以在此处找到它: https://github.com/jbroadway/urlify 它可以处理拉丁字符以及希腊语、土耳其语、俄语、乌克兰语、捷克语、波兰语和拉脱维亚语。

这个类在我所有的测试用例中都能正常工作,而所有基于iconv的解决方案都无法满足我的需求。谢谢! - Jens Wegar
感谢您的这门课程。在2017年,该项目仍然存在,并且该类在PHP7中运行得非常完美。 - Juan Antonio Tubío

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接