首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PHP str_ireplace相同的单词是否有重音

PHP str_ireplace相同的单词是否有重音
EN

Stack Overflow用户
提问于 2022-06-27 17:31:08
回答 1查看 96关注 0票数 0

希望这是个更好的..。

在执行标准查询SELECT-WHERE之前,我设置了"mysqli_set_charset($conn,“utf8”)。此查询执行重音比较。

Str_ireplace(搜索,替换,文本)搜索做重音敏感的比较。我需要搜索来做口音比较。

我要强调“弗朗索瓦”一词。将“Fran ais”改为

代码语言:javascript
复制
<mark>Français</mark>

但与此同时,我想把“法兰西”改为

代码语言:javascript
复制
<mark>Francais</mark>

旧员额:

我用一种简单的方式突出一些文本:

代码语言:javascript
复制
$markReplace = "<mark>" . $wordToSearch . "</mark>";
$fullText = str_ireplace($wordToSearch, $markReplace, $fullText);
echo $fullText;

它工作得很好,问题是有时相同的$wordToSearch可能有重音或没有重音。例如,“胡特-惠特雷”、“法国-弗朗索瓦”、“回声-回声”等,都是由于错误而造成的。与MySql相反的是,str_ireplace无法检测出带有重音的字母与没有重音的字母相同。

代码语言:javascript
复制
$unwanted_array = array('Š'=>'S', 'š'=>'s', 'Ž'=>'Z', 'ž'=>'z', 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
                        'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U',
                        'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss', 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c',
                        'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o',
                        'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'þ'=>'b', 'ÿ'=>'y' );
$str = strtr( $str, $unwanted_array );

使用这种方法的解决方案不起作用,因为它将改变$fullText中的所有口音。当我回音$fullText时,我需要保留原来的单词。

找不出解决办法。

谢谢..。安迪

EN

回答 1

Stack Overflow用户

发布于 2022-06-27 22:11:19

好的,首先,把一组字符(例如:重音)转换成一个等价的形式,例如:按照某些规则没有重音,就叫做“音译”。

intl扩展提供了一个方便的音译类,我们可以用它简单地调用:

代码语言:javascript
复制
$translit = Transliterator::create('Latin-ASCII;');
$foo = $translit->transliterate('Français'); // Francais

因此,辛苦地维护一张“不想要的”字符及其替换的列表是不必要的。

第二,重音字符并不总是单码点,ç可以用统一的编码点表示,也可以用由普通c和代表重音的组合标记组成的两码点序列表示。

所述单元包括单个可视字形,称为字形。

第三,您的需求大小写-无意义和重音-不敏感本质上要求我们必须构建我们自己的自定义字符串匹配过程。

首先,我们需要一个GraphemeIterator来正确遍历UTF8字符串。intl的IntlBreakIterator::createCharacterInstance()执行繁重的任务,但返回字节偏移,因此让我们将其封装到另一个实际弹出图形的迭代器中:

代码语言:javascript
复制
class GraphemeIterator implements \Iterator {
    protected $i, $string, $offset;
    
    public function __construct($string) {
        $this->string = $string;
        
        $i = IntlBreakIterator::createCharacterInstance();
        $i->setText($string);
        $this->i = $i->getIterator();
        
        $this->init();
    }
    
    protected function init() {
        $this->offset = $this->i->current();
        $this->i->next();
    }
    
    public function length() {
        return grapheme_strlen($this->string);
    }
    
    public function tell() {
        return [ $this->offset, $this->i->current()];
    }
    
    // Iterator Interface functions
    public function current(): mixed {
        return substr($this->string, $this->offset, $this->i->current() - $this->offset);
    }
    
    public function key(): mixed {
        return $this->i->key();
    }
    
    public function next(): void {
        $this->offset = $this->i->current();
        $this->i->next();
    }
    
    public function rewind(): void {
        $this->i->rewind();
        $this->init();
    }
    
    public function valid(): bool {
        return $this->i->valid();
    }
}

现在,我们需要在应用一些任意比较之后可以比较两个字符串的东西:

代码语言:javascript
复制
class TransformingComparator {
    protected $transforms = [];
    
    public function __construct(array $transforms) {
        foreach($transforms as $transform) {
            $this->addTransform($transform);
        }
    }
    
    protected function addTransform(callable $transform) {
        $this->transforms[] = $transform;
    }
    
    protected function transform($input) {
        $output = $input;
        foreach($this->transforms as $transform) {
            $output = $transform($output);
        }
        return $output;
    }
    
    public function compare($a, $b) {
        return $this->transform($a) <=> $this->transform($b);
    }
}

以及一个函数,该函数可以使用这些函数定位搜索字符串的出现:

代码语言:javascript
复制
function findAllInGraphemeString($needle, $haystack, $comparator) {
    $t_it = new GraphemeIterator($haystack);
    $s_it = new GraphemeIterator($needle);
    
    $s = 0;
    $sl = $s_it->length();
    
    $out = [];
    $cur = [];
    
    for( $t=0, $tl=$t_it->length(); $t<$tl; ++$t ) {
        if( $comparator($t_it->current(), $s_it->current()) === 0 ) {
            if( empty($cur) ) {
                $cur[] = $t_it->tell()[0];
            }
            if( ++$s >= $sl ) {
                $cur[] = $t_it->tell()[1];
                $out[] = $cur;
                $cur = [];
                $s = 0;
                $s_it->rewind();
            } else {
                $s_it->next();
            }
            $t_it->next();
        } else {
            // on aborted partial match restart from current
            if( count($cur) != 0 ) {
                $s = 0;
                $cur=[];
                --$t;
            } else {
                $t_it->next();
            }
            $s_it->rewind();
        }
    }
    
    return $out;
}

最后,可以执行实际转换的函数:

代码语言:javascript
复制
function transformSubstrings(string $text, array $boundaries, callable $transform) {
    $output = '';
    $offset = 0;
    
    foreach($boundaries as $bound) {
        $output .= substr($text, $offset, $bound[0]-$offset);
        $output .= $transform(substr($text, $bound[0], $bound[1]-$bound[0]));
        $offset = $bound[1];
    }
    return $output . substr($text, $bound[1]);
}

最后,我们可以把这一点放在一起:

代码语言:javascript
复制
$translit = Transliterator::create('Latin-ASCII;');
$transforms = [
    [$translit, 'transliterate'], // remove accents
    'mb_strtolower'
];
$tc = new TransformingComparator($transforms);

$text = 'lorem ipsum frFrançais dolor sit français amet adsplicing dit';
$search = 'Francais';

echo transformSubstrings(
    $text,
    findAllInGraphemeString($search, $text, [$tc, 'compare']),
    function($a){
        return sprintf('<mark>%s</mark>', $a);
    }
);

输出:

代码语言:javascript
复制
lorem ipsum <mark>Français</mark> dolor sit <mark>français</mark> amet adsplicing dit <mark>francais</mark>

是的,我在这个问题上很难接受书呆子狙击

编辑:既然您已经提到了排序规则,我就会想到intl有一个Collator类,看起来TransformingComparator现在更重要了,可以替换如下:

代码语言:javascript
复制
$col = new Collator('fr-ca'); // or whatever locale you're using
$col->setStrength(Collator::PRIMARY);
// ...
transformSubstrings(
    $text,
    findAllInGraphemeString($search, $text, [$col, 'compare']),
    function($a){
        return sprintf('<mark>%s</mark>', $a);
    }
)

这可能也会更快一些,因为它可能使用查找而不是运行所有的转换。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72776187

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档