缩减一个 UTF-8 字符串以进行二进制比较

Reduce a UTF-8 string for binary comparison

我想快速检查 UTF-8 单词是否作为数组键存在。

词可能有:

我可以使用 mb_strtolower() 将它们都设为小写,并使用 Normalizer::normalize() 将字符串标准化。这会检查前 2 个要点,但不处理重音符号:

'tést' !== 'test'

我可以用Collator来比较两个词:

$collator = new Collator('fr_FR');
$collator->setStrength(Collator::PRIMARY);
$collator->compare('tést', 'test'); // 0

这会检查我的 3 个要点,但现在我必须遍历我所有的词对来比较它们,当我希望能够执行二进制查找作为数组键时(我有很多查找要执行在一本大词典上)。

我想要的是:

function reduce($word) {
    // how?
}

// prepare the dictionary (once)

$dictionary = [];

foreach ($dictionaryWords as $dictionaryWord) {
    $dictionary[reduce($dictionaryWord)] = true;
}

// perform a lookup (many times)

if (isset($dictionary[reduce($lookupWord)])) {
    // it's a match!
}

基本上,我希望 reduce() 函数(可能命名不当)执行这样的简化:

我相信 MySQL 在内部为其文本索引做了类似的事情。

是否有一个 intl 函数可以做到这一点? list of intl classes and functions 很难消化。

看了你的问题,你似乎只对检查一个词是否作为唯一数组索引存在感兴趣。

您可以通过对单词进行加密散列并将散列用作索引来做到这一点。它会是这样的:

<?php
$word = 'TÈST';
$dictionary[sha1($word)] = TRUE;

或者使用一种对碰撞攻击更有弹性的算法,如果你担心的话。如果您需要该领域的任何指示,请详细说明您的问题。

更新

请查看下面的代码片段,它生成 "test, strasse"。

<?php

setlocale(LC_ALL, 'nl_NL.UTF-8');

$words = [ 'TÈST', 'Straße' ];

foreach ($words as $index => $word)
{
    echo ($index?', ':'') . strtolower(iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $word));
}

我要找的是 Transliterator class. An example can be found in this answer:

$string = "Fóø Bår";
$transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Lower(); :: NFC;', Transliterator::FORWARD);
echo $transliterator->transliterate($string); // foo bar

感谢@Pete评论中的指点。

这甚至适用于非欧洲字符:

echo $transliterator->transliterate('Fóø Bår 学中文'); foo bar xue zhong wen

其中 iconv 会在工作中失败:

echo iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', 'Fóø Bår 学中文'); // Foo Bar ???

当然,除非我遗漏了其他一些 iconv 选项。