Как игнорировать акцент в запросе SQLite (Android)
Я новичок в Adnroid, и я работаю над запросом в SQLite, но проблема в том, когда я использую акцент в моей строке. Пример:
- aaÃ
- aaÃ
- aaÃ
- aaÃ
- ааа
- AAA
Если я сделаю:
SELECT * FROM TB_MOVIE WHERE MOVIE_NAME LIKE '%a%' ORDER BY MOVIE_NAME;
Это возвращение:
- AAA
- ааа (игнорирует остальных)
Но если я сделаю:
SELECT * FROM TB_MOVIE WHERE MOVIE_NAME LIKE '%à%' ORDER BY MOVIE_NAME;
Это возвращение:
- ааа (игнорируя заголовок "ААА")
Я хочу выбрать строки в БД SQLite, не обращая внимания на акценты и регистр. Пожалуйста помоги.
3 ответа
Как правило, сравнения строк в SQL контролируются столбцом или выражением COLLATE
правила. В Android предварительно определены только три последовательности сопоставления: BINARY (по умолчанию), LOCALIZED и UNICODE. Ни один из них не подходит для вашего случая использования, и, к сожалению, API C для установки новых функций сопоставления не представлен в API Java.
Чтобы обойти это:
- Например, добавьте еще один столбец в таблицу.
MOVIE_NAME_ASCII
Сохраните значения в этом столбце с удаленными знаками ударения. Вы можете удалить акценты, нормализуя строки в Unicode Normal Form D (NFD) и удаляя кодовые точки, отличные от ASCII, так как NFD представляет акцентированные символы примерно как простые ASCII + комбинирующие маркеры акцента:
String asciiName = Normalizer.normalize(unicodeName, Normalizer.Form.NFD) .replaceAll("[^\\p{ASCII}]", "");
Выполните поиск текста в этом нормализованном ASCII-столбце, но отобразите данные из исходного столбца Unicode.
В Android sqlite, LIKE
а также GLOB
игнорировать оба COLLATE LOCALIZED
а также COLLATE UNICODE
(они работают только на ORDER BY
). Однако есть решение без добавления дополнительных столбцов в таблицу. Как объясняет @asat в этом ответе, вы можете использовать GLOB
с шаблоном, который заменит каждую букву всеми доступными альтернативами этой буквы. В Java:
public static String addTildeOptions(String searchText) {
return searchText.toLowerCase()
.replaceAll("[aáàäâã]", "\\[aáàäâã\\]")
.replaceAll("[eéèëê]", "\\[eéèëê\\]")
.replaceAll("[iíìî]", "\\[iíìî\\]")
.replaceAll("[oóòöôõ]", "\\[oóòöôõ\\]")
.replaceAll("[uúùüû]", "\\[uúùüû\\]")
.replace("*", "[*]")
.replace("?", "[?]");
}
И потом (не буквально так, конечно):
SELECT * from table WHERE lower(column) GLOB "*addTildeOptions(searchText)*"
Таким образом, например, на испанском языке пользователь, выполняющий поиск по mas или más, преобразует результаты поиска в m[aáàäâã], возвращая оба результата.
Важно отметить, что GLOB
игнорируемых COLLATE NOCASE
Вот почему я преобразовал все в нижний регистр как в функции, так и в запросе. Обратите внимание также, что lower()
Функция в sqlite не работает с не-ASCII символами - но, опять же, это те, которые вы уже заменяете!
Функция также заменяет оба GLOB
подстановочные знаки, *
а также ?
, с "сбежавшими" версиями.
Вы можете использовать Android NDK для перекомпиляции исходного кода SQLite, включая требуемый ICU (международные компоненты для Unicode). Объяснено на русском языке здесь: http://habrahabr.ru/post/122408/
Процесс компиляции SQLilte с исходным кодом с помощью ICU объясняется здесь:
Как скомпилировать sqlite с ICU?
К сожалению, вы получите разные APK для разных процессоров.
Вы должны смотреть на них не как на акцентированные символы, а как на совершенно других персонажей. Вы можете также искать a, b или c. При этом я бы попробовал использовать для этого регулярное выражение. Это будет выглядеть примерно так:
SELECT * from TB_MOVIE WHERE MOVIE_NAME REGEXP '.*[aAàÀ].*' ORDER BY MOVIE_NAME;