缩减一个 UTF-8 字符串以进行二进制比较
Reduce a UTF-8 string for binary comparison
我想快速检查 UTF-8 单词是否作为数组键存在。
词可能有:
- 不同大小写
- 是否有重音字符
- 不同的 Unicode 规范化形式
我可以使用 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()
函数(可能命名不当)执行这样的简化:
- 'TÈST' => 'test'
- 'Straße' => 'strasse'
我相信 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
选项。
我想快速检查 UTF-8 单词是否作为数组键存在。
词可能有:
- 不同大小写
- 是否有重音字符
- 不同的 Unicode 规范化形式
我可以使用 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()
函数(可能命名不当)执行这样的简化:
- 'TÈST' => 'test'
- 'Straße' => 'strasse'
我相信 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
选项。