Сравнение строк в PHP так же, как это делает MySQL

Я храню varchar в таблице MySQL utf8 и использую сопоставление utf8_general_ci. У меня есть уникальный индекс на varchar. Я хотел бы сделать сравнение строк в PHP, которое эквивалентно тому, что MySQL будет делать с индексом.

Конкретным примером является то, что я хотел бы иметь возможность определить, что «a» считается эквивалентным «À» в PHP, прежде чем это произойдет:

mysql> insert UniTest (str) values ('a');                                   
Query OK, 1 row affected (0.00 sec)

mysql> insert UniTest (str) values ('À');                                   
ERROR 1062 (23000): Duplicate entry 'À' for key 1

person twk    schedule 22.01.2009    source источник


Ответы (5)


Сопоставление не имеет ничего общего с хранилищем. Вам нужно установить кодировку, чтобы определить кодировку хранилища. Сопоставление определяет, как должно происходить сравнение и сортировка. Сопоставление должно быть осведомлено о кодировке, но в противном случае оно не имеет ничего общего с кодировкой.

Чтобы ответить на ваш вопрос, вы можете использовать iconv для транслитерации текста, а затем сравнить его. Например:

function compare($s1, $s2) {
  return strcmp(
    iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $s1),
    iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $s2));
}

Это в основном то, что MySql сделает за вас, хотя он, вероятно, быстрее и может иметь немного другую таблицу сопоставления, чем ISO-8859-1//TRANSLIT. Не совсем уверен в этом.

Вероятно, было бы проще использовать базу данных, как уже предлагали другие.

person troelskn    schedule 23.01.2009
comment
Я отредактировал вопрос, чтобы точно отразить сопоставление и кодировку для хранения. Можете ли вы привести пример использования mb_string для реализации этого? Я не вижу mb_strcmp в документах. - person twk; 23.01.2009
comment
Я сделал ошибку — вам нужно использовать iconv, а не mb_string. Я отредактировал свой ответ. - person troelskn; 24.01.2009
comment
Это становится ближе. трюк //TRANSLIT хорош, но, как вы упомянули, ISO-8859-1 — это не то, что мне нужно. MySQL использует сортировку, которая выглядит следующим образом, которую мне нужно было бы как-то эмулировать: сопоставление-charts.org/mysql60/ - person twk; 24.01.2009
comment
Если вас не устраивает довольно ограниченный функционал Iconv, вы можете попробовать это расширение: derickrethans.nl/translit. php или это (обычный php): sitepoint.com/blogs/2006/03/03/ - person troelskn; 25.01.2009
comment
Оба позволяют вам использовать определяемую пользователем базу данных транслитерации. Вы можете скопировать MySqls, если вам нужно точное совпадение. Но, вероятно, было бы проще просто использовать базу данных в первую очередь;) - person troelskn; 25.01.2009
comment
Также изучите форму нормализации юникода c (iirc). Недавно я сделал это для чего-то на питоне. Это разобьет символ на составные части, а затем вы сможете удалить не-ascci. ‹ударение A› =› A‹ударение в юникоде› =› A - person Richard Levasseur; 04.02.2009
comment
Ссылка на сайт, размещенная @troelskn, уже не работает. Вот замена: sitepoint.com/us-ascii-transliterations- текста Unicode - person Jpsy; 09.10.2012

Почему бы вам просто не позволить MySQL решить, существует ли уже запись с таким же ключом?

Вы можете запустить запрос SELECT, чтобы узнать, существует ли уже запись с этим атрибутом:

SELECT 1
FROM UniTest
WHERE str = "À"

Или просто попробуйте вставить новую запись и использовать функции mysql_error () и mysql_errno(), чтобы увидеть если произошла ошибка.

person Gumbo    schedule 22.01.2009

Было бы разумно просто позволить MySQL выполнить эту работу, отправив запрос в MySQL, например:

SELECT CASE WHEN '$a' = '$b' THEN 1 ELSE 0 END


РЕДАКТИРОВАТЬ уточнение сообщения:

Вы можете один раз перебрать весь интересующий декартово набор символов, соединенный с самим собой, и построить стандартный ассоциативный массив php наборов эквивалентности.

    for each $char1 in $charset {  
        for each $char2 in $charset {  
            $charmatch[$char1][$char2] = mysqlTestMatch($char1, $char2));  
        }  
    }  

Затем вам нужно будет проверить каждую строку символ за символом, чтобы убедиться, что а) они одинаковы, а если нет, б) они эквивалентны.

person dkretz    schedule 22.01.2009
comment
Я делаю это с очень большим количеством строк по разным причинам, поэтому я хотел бы избежать базы данных. - person twk; 29.01.2009

Итак, если я правильно понял, вы хотите сделать аналогичное сравнение в PHP, которое вы бы получили при проверке общего индекса UTF-8 в MySQL?

Проще всего было бы создать вспомогательную функцию, которая будет преобразовывать строку в соответствии с правилами utf8_general_ci, используемыми MySSQL, которые в основном предназначены для преобразования определенных букв в базовую букву.

Правила для этой сортировки MySQL перечислены здесь:

http://www.collation-charts.org/mysql60/mysql604.utf8_general_ci.european.html

Например, если вы прокрутите немного вниз до «золотой А» слева, вы увидите все символы, которые преобразуются в эту А.

Имея вспомогательную функцию, называемую, например, utf8g_to_ascii(), вы можете написать функцию:

function utf8_compare($s1, $s2) {
   $a = utf8g_to_ascii($s1);
   $b = utf8g_to_ascii($s2);
   return strcmp( $a, $b );
}

Я бы смоделировал свой код после:

http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
person alphadogg    schedule 04.02.2009

Используйте Collator или Transliterator intl.

$s1 = 'a';
$s2 = 'À';

var_dump(
    is_same_string($s1, $s2),
    $s1 === transliterator_transliterate('Any-Latin; Latin-ASCII; Lower()', $s2)
);

function is_same_string($str, $str2, $locale = 'en_US')
{
    $coll = collator_create($locale);
    collator_set_strength($coll, Collator::PRIMARY);  
    return 0 === collator_compare($coll, $str, $str2);
}
person masakielastic    schedule 19.09.2013