簡體   English   中英

PHP str_ireplace 是否帶有重音的相同單詞

[英]PHP str_ireplace same word with accent or not

希望這是一個更好的...

我設置“mysqli_set_charset($conn,"utf8");” 在執行標准查詢 SELECT-FROM-WHERE 之前。 此查詢執行重音不敏感比較。

str_ireplace(search, replace, text) 搜索進行重音敏感比較。 我需要搜索來進行口音不敏感比較。

我想突出顯示“Français”這個詞。 我將“Français”替換為

<mark>Français</mark>

但同時我想將“Francais”替換為

<mark>Francais</mark>

舊帖:

我使用一種簡單的方法來突出顯示一些文本:

$markReplace = "<mark>" . $wordToSearch . "</mark>";
$fullText = str_ireplace($wordToSearch, $markReplace, $fullText);
echo $fullText;

它工作正常,問題是有時相同的 $wordToSearch 可能有重音或沒有重音。 例如“huître-huitre”、“Francais-Français”、“echo-écho”,因為拼寫錯誤。 與 MySql 不同,str_ireplace 不會將帶有重音的字母檢測為沒有重音的相同字母。

$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 時,我需要保留原詞。

想不出解決辦法。

謝謝...安迪

好的,首先,根據一些規則將一組字符[例如:重音]轉換為等效形式[例如:無重音]稱為“音譯”。

intl 擴展提供了一個方便的音譯類,我們可以簡單地調用它:

$translit = Transliterator::create('Latin-ASCII;');
$foo = $translit->transliterate('Français'); // Francais

因此,沒有必要煞費苦心地維護“不需要的”字符及其替換的列表。

其次,重音字符並不總是單個代碼點, ç可以由統一代碼點或由純c和表示重音的組合標記組成的雙代碼點序列表示。

包含單個視覺字形的單元稱為字形。

第三,您的要求 [不區分大小寫不區分重音] 本質上要求我們必須構建自己的自定義字符串匹配過程。

首先,我們需要一個 GraphemeIterator 來正確遍歷 UTF8 字符串。 intl 的IntlBreakIterator::createCharacterInstance()完成了繁重的工作,但返回了字節偏移量,因此讓我們將其包裝在另一個實際彈出字素的迭代器中:

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();
    }
}

現在我們需要一些可以在應用一些任意比較之后比較兩個字符串的東西:

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);
    }
}

以及一個可以使用它們來定位搜索字符串出現的函數:

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;
}

最后是一個可以執行實際轉換的函數:

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]);
}

我們最終可以把它放在一起:

$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);
    }
);

輸出:

lorem ipsum <mark>Français</mark> dolor sit <mark>français</mark> amet adsplicing dit <mark>francais</mark>

是的,我被書呆子狠狠地狙擊了這個。


編輯:既然您提到了排序規則,我突然想到 intl 有一個Collator類,看起來TransformingComparator現在不再相關,可以替換為:

$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);
    }
)

這也可能會更快一些,因為它可能使用查找而不是運行所有轉換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM