PHP:在UTF-8字符串中用最接近的7位ASCII等效字符替换umlauts

51
我想做的是从字符串中删除所有重音符号和分音符号,把"lärm"转换为"larm"或"andré"转换为"andre"。我尝试的方法是对字符串进行utf8_decode,然后在上面使用strtr,但由于我的源文件保存为UTF-8文件,我无法输入ISO-8859-15字符以处理所有的分音符号 - 编辑器会插入UTF-8字符。
显然,解决此问题的方法是使用一个包含ISO-8859-15文件的include,但肯定有比需要另一个必需的include更好的方法吧?
echo strtr(utf8_decode($input), 
           'ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ',
           'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');

更新: 或许我在表达我的意图时有些不准确:我并不是真的想要删除umlauts,而是要用它们最接近的“一个字符ASCII等效字符”来替换它们。


2
请记住,你生成的字符串不一定与原始字符串具有相同的含义,正如在这个类似的问题中所讨论的那样。这是一个可用的方法来清理文件名,但如果您计划将新字符串显示为文本,那么这可能不是您想要做的事情。 - Dave DuPlantis
2
谢谢你的提示。然而,如果“二分查找”失败,生成的字符串将用作搜索的简化版本回退。在此之后,还将应用更多简化-以使文盲仍然能够找到他们要找的内容 :) - BlaM
2
实际上,对于显示字符来说,确实有一个有效的理由。生成符合HTML 4.1标准的导航菜单id属性。例如,如果我有<h3>Für Elise</h3>并且我想在它上面生成一个id锚点,<a id="FurElise" />是我能做到的最好的,并且仍然符合html 4.1标准,这可能对一些旧浏览器很必要。 - Alice Wonder
8个回答

59
iconv("utf-8","ascii//TRANSLIT",$input);

扩展示例


4
我不得不添加 "setlocale(LC_ALL, 'en_US');"(遗憾的是我机器上似乎没有德国区域设置可用 :( ),但这样它就能正常工作了。太好了! :) - BlaM
16
为什么这个解决方案在我的电脑上把 "ö" 转换成了 "o",而在 php 参考手册 的示例中它会返回 "oe" - spikey
4
对于西里尔字母,此方法不起作用。它们会被转换成问号?代替。 - Zebooka
2
这个值为false,导致程序崩溃,并提示我遇到了非法字符。 - Matt
2
回复Spikey的评论:如果您将区域设置设置为de_*.UTF8(例如de_DE.UTF8,de_CH.UTF8等),那么会将umlauts转换为*e(ü->ue)。将其设置为en_US.UTF8以获得所需的效果。 - Michał Leon
显示剩余4条评论

33

一个小技巧,不需要设置区域设置或拥有巨大的翻译表:

function Unaccent($string)
{
    if (strpos($string = htmlentities($string, ENT_QUOTES, 'UTF-8'), '&') !== false)
    {
        $string = html_entity_decode(preg_replace('~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i', '$1', $string), ENT_QUOTES, 'UTF-8');
    }

    return $string;
}

它正常工作的唯一要求是将文件保存为UTF-8(如您应该已经这样做)。


非常适合匈牙利语。 - vinczemarton

9
你可以尝试这个。
$string = "Fóø Bår";
$transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Lower(); :: NFC;', Transliterator::FORWARD);
echo $normalized = $transliterator->transliterate($string);

但是您需要安装http://php.net/manual/zh/book.intl.php

,才能使用相关的IT技术。


1

1
尽管这并不是一个确切的答案,但我很感激这个答案,因为我正在使用WordPress。所以谢谢!;) - Vladan

1

好的,我自己找到了一个显而易见的解决方案,但是它在性能方面并不是最好的选择...

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

2
它在性能方面并不是最好的,而且还会产生错误的结果。像Œ、Æ等字母应该分解为两个字母,而不是一个字母。 - laurent
2
你错过了 žščřďťňů,而这只是我键盘上看到的一些。白名单已知字符并不是最好的解决方案。 - Piskvor left the building
如问题所述:我正在寻找最接近的“单字符ASCII”,因此对于我的用例来说,两个字母分解是不正确的。一个字母对于我要做的事情是正确的。 - BlaM

0

我发现这个在法语和德语中给出了最一致的结果。 使用标签设置为utf-8,我将其放置在一个函数中,以从单词数组中返回一行,它完美地工作。

htmlentities (  $line, ENT_SUBSTITUTE   , 'utf-8' ) 

这将返回HTML实体。例如,München将变为München。但是请求的结果应该是Muenchen。 - kirschkern

0

这是一种规范的方法:

  1. 获取文本的规范分解形式。请参阅https://unicode.org/reports/tr15/了解Unicode规范化形式。
  2. 删除非间隔标记。
  3. 获取剩余文本的规范组合形式。

https://unicode-org.github.io/icu/userguide/transforms/general/

例如,要删除字符的重音符号,请使用以下转换:
NFD; [:Nonspacing Mark:] Remove; NFC.
我有点不确定为什么他们给出了这个例子,因为页面还指出每个转换规则由两个冒号和一个转换名称组成。
所以我们会添加那些。您需要包装ICU库的intl扩展。
$t = \Transliterator::createFromRules(':: NFD; ::[:Nonspacing Mark:] Remove; :: NFC;');

示例
print $t->transliterate('أ');

这将U+0623(带有上方哈姆扎的阿拉伯字母Alef)转换为U+0627(阿拉伯字母Alef),即它也适用于非拉丁字母及其重音符号。
您可以将[:Nonspacing Mark:]替换为[:Mn:]

0

对于阿拉伯语和波斯语用户,我建议使用以下方法去除变音符号:

    $diacritics = array('َ','ِ','ً','ٌ','ٍ','ّ','ْ','ـ');
    $search_txt = str_replace($diacritics, '', $diacritics);

在阿拉伯键盘中打出变音符号,您可以使用这些Asci代码(这些代码是Asci而不是Unicode)在Windows编辑器中 直接输入变音符号或按住Alt +(输入变音符号的代码) 这些是代码

ـَ(0243)ـِ(0246)ـُ(0245)ـً(0240)ـٍ(0242)ـٌ(0241)ـْ(0250)ـّ(0248)ـ ـ(0220)


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