Что нового

<SQLite.au3> Регистронезависимый поиск на русском

over7

Новичок
Сообщения
11
Репутация
2
Коллеги, доброго времени суток!

Печаль-беда на меня напала. Не могу в скрипте наладить работу поиска в БД SQLite.
Сижу отлаживаю на примере, мозх оплавился.

Проблема.
AutoIT в связке <SQLite.au3>+ <SQLite.dll.au3> резко реагируют на регистр букв русского алфавита, считая, что "LIKE '%ы%'" и "LIKE '%Ы%'" совершенно два разных запроса.

Пример.
Код:
#include <SQLite.au3>
#include <SQLite.dll.au3>

_SQLite_Startup()
If @error > 0 Then
	MsgBox(16, "SQLite Ошибка", "SQLite.dll Не может быть загружен!")
	Exit
EndIf



_SQLite_Open(Default,Default,$SQLITE_ENCODING_UTF8)       ; варианты используемых кодировок (их всего три)
;~ _SQLite_Open(Default,Default,$SQLITE_ENCODING_UTF16)
;~ _SQLite_Open(Default,Default,$SQLITE_ENCODING_UTF16be)


_SQLite_Exec(-1, "CREATE TABLE tablo (id INTEGER PRIMARY KEY AUTOINCREMENT, Name varchar(256) );")
_SQLite_Exec(-1, "INSERT INTO tablo (Name) VALUES ('Андрей'), ('Борис'), ('Владимир'), ('Григорий'), ('Дмитрий'), ('Иван') ;")


$sOut = ""

$sQuery="SELECT Name FROM tablo WHERE Name LIKE '%и%';"
$sOut &= "! " & $sQuery & @CR
$sOut &= " ---->" & _SQLQuery($sQuery," ","; ") & "<----" & @CR

$sQuery="SELECT Name FROM tablo WHERE UPPER(Name) LIKE '%И%';"
$sOut &= "! " & $sQuery & @CR
$sOut &= " ---->" & _SQLQuery($sQuery," ","; ") & "<----" & @CR


;~ MsgBox(0,"",$sOut)
ConsoleWrite($sOut)



_SQLite_Close()
_SQLite_Shutdown()
Exit


Func _SQLQuery($sQuery, $sColDelemeter = @TAB, $sRowDelemeter = @CRLF)
	Local $hQuery
	Local $aRow
	Local $sRes = ""
	Local $i = 0

	_SQLite_Query(-1, $sQuery, $hQuery)
	While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK
		If $sRes <> "" Then $sRes &= $sRowDelemeter
		For $i = 0 To UBound($aRow) - 1
			If $i > 0 Then $sRes &= $sColDelemeter
			$sRes &= $aRow[$i]
		Next
	WEnd
	Return $sRes
EndFunc   ;==>_SQLQuery


Результат работы примера
! SELECT Name FROM tablo WHERE Name LIKE '%и%';
---->Борис; Владимир; Григорий; Дмитрий<----
! SELECT Name FROM tablo WHERE UPPER(Name) LIKE '%И%';
---->Иван<----

Стоит отметить, что с английским проблем нет..
Буду рад помощи.. :(


Пользую AutoIt v3.3.12.0
 

КашаК

Новичок
Сообщения
15
Репутация
0
А что насчет "костыля" в виде опускания (поднимания) всех букв?
Первым заходом шерстим базу и приравниваем "М=м", а вторым уже делаем выборку...
 

joiner

Модератор
Локальный модератор
Сообщения
3,556
Репутация
628
судя по комментам в сети sqlite3 так и не стала до конца "юникодной"


Добавлено:
Сообщение автоматически объединено:

можно писать все в одной строке запроса
Код:
$sQuery="SELECT Name FROM tablo WHERE Name LIKE '%и%' Or UPPER(Name) LIKE '%И%' Or UPPER(Name) LIKE '%А%';"
$sOut &= "! " & $sQuery & @CR
$sOut &= " ---->" & _SQLQuery($sQuery," ","; ") & "<----" & @CR
 

SyDr

Сидра
Сообщения
651
Репутация
158
Это стандартное поведение SQLite (http://www.sqlite.org/faq.html#q18)
То есть Upper не поможет вообще.

Самый простой вариант (хоть и не очень красивый) - завести дополнительный столбец, по которому и производить выборку (БОРИС, ИВАН).
 
Автор
O

over7

Новичок
Сообщения
11
Репутация
2
Спасибо за участие.
По совету SyDr решил проблему выделением доп.столбца, в котором храню регистры букв ;D

Кому интересно, выглядит это примерно так:
Код:
#include <SQLite.au3>
#include <SQLite.dll.au3>


_SQLite_Startup()
If @error > 0 Then
	MsgBox(16, "SQLite Ошибка", "SQLite.dll Не может быть загружен!")
	Exit
EndIf
_SQLite_Open()
_SQLite_Exec(-1, "CREATE TABLE tablo (id INTEGER PRIMARY KEY AUTOINCREMENT, Name varchar(256), LetterCase varchar(64));")


; Вычисляю для каждого имени "бинарную маску регистра букв" ("абвГДЕ123" -> "000111000") и сохраняю ее отдельно в столбце БД LetterCase
; Для экономии сжимаю двочную маску в Hex ("000111000" -> "1C0")
; сами данные завожу в базу в строчном виде.

$sNames = "Андрей|БоРИс|ВлАдИмир|ГриГОРий|ДмитриЙ|Иван"
$aNames = StringSplit($sNames, "|")
For $i = 1 To $aNames[0]
	_SQLite_Exec(-1, "INSERT INTO tablo (Name, LetterCase) VALUES ('" & StringLower($aNames[$i]) & "', '" & _BiToHex(_SaveLetterCase($aNames[$i])) & "');")
Next

;поисковая строка
$sSearch = "И"

; выполняю поиск и для результатов поиска восстанавливаю оригинальный регистр букв из столбца LetterCase
$sQuery = "SELECT Name, LetterCase FROM tablo WHERE Name LIKE '%" & StringLower($sSearch) & "%';"
$sQRes = _SQLQuery($sQuery, @CR, @CR)
Local $aQRes , $sRes =""
If $sQRes <> "" Then
	$aQRes = StringSplit($sQRes, @CR)
	For $i = 1 To $aQRes[0] Step 2
		$sRes &= _RestoreLetterCase($aQRes[$i], _HexToBi($aQRes[$i + 1])) & "; "
	Next
EndIf

; кратка сводка работы скрипта
$sOut =  "Данные   "&@TAB&": '" & StringReplace($sNames,"|",", " ) & "'" & @CR
$sOut &= "Поиск    "&@TAB&": '" & $sSearch & "'" & @CR
$sOut &= "Результат"&@TAB&": '" & $sRes    & "'" & @CR & @CR
$sOut &= " БД   ---------------------------------------------------" & @CR
$sOut &= _SQLQuery("SELECT * FROM tablo", @TAB & @TAB) & @CR
$sOut &= " --------------------------------------------------------" & @CR
MsgBox(0, "", $sOut)
ConsoleWrite($sOut)


_SQLite_Close()
_SQLite_Shutdown()
Exit


Func _SQLQuery($sQuery, $sColDelemeter = @TAB, $sRowDelemeter = @CRLF)
	Local $hQuery
	Local $aRow
	Local $sRes = ""
	Local $i = 0

	_SQLite_Query(-1, $sQuery, $hQuery)
	While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK
		If $sRes <> "" Then $sRes &= $sRowDelemeter
		For $i = 0 To UBound($aRow) - 1
			If $i > 0 Then $sRes &= $sColDelemeter
			$sRes &= $aRow[$i]
		Next
	WEnd
	Return $sRes
EndFunc   ;==>_SQLQuery


Func _SaveLetterCase($sText)
	Local $sBi = ""
	Local $i = 0
	Local $a = StringSplit($sText, "")

	For $i = 1 To $a[0]
		$sBi &= StringCompare(StringLower($a[$i]), $a[$i], 1)
	Next

	Return $sBi
EndFunc   ;==>_SaveLetterCase


Func _RestoreLetterCase($sText, $sBi)
	Local $i = 0
	Local $aText = StringSplit($sText, "")
	Local $aBi = StringSplit($sBi, "")
	Local $sRes = ""

	For $i = 1 To $aText[0]
		If $i <= $aBi[0] Then
			If $aBi[$i] = "1" Then
				$sRes &= StringUpper($aText[$i])
			Else
				$sRes &= $aText[$i]
			EndIf
		Else
			$sRes &= $aText[$i]
		EndIf
	Next

	Return $sRes
EndFunc   ;==>_RestoreLetterCase


Func _BiToHex($sBi)
	Local $sHex = ""
	Local $sFourBits = ""

	Do
		$sFourBits = StringLeft($sBi, 4)
		$sBi = StringTrimLeft($sBi, 4)
		If StringLen($sFourBits) < 4 Then $sFourBits = StringLeft($sFourBits & "0000", 4)
		If $sFourBits = "0000" Then $sHex &= "0"
		If $sFourBits = "0001" Then $sHex &= "1"
		If $sFourBits = "0010" Then $sHex &= "2"
		If $sFourBits = "0011" Then $sHex &= "3"
		If $sFourBits = "0100" Then $sHex &= "4"
		If $sFourBits = "0101" Then $sHex &= "5"
		If $sFourBits = "0110" Then $sHex &= "6"
		If $sFourBits = "0111" Then $sHex &= "7"
		If $sFourBits = "1000" Then $sHex &= "8"
		If $sFourBits = "1001" Then $sHex &= "9"
		If $sFourBits = "1010" Then $sHex &= "A"
		If $sFourBits = "1011" Then $sHex &= "B"
		If $sFourBits = "1100" Then $sHex &= "C"
		If $sFourBits = "1101" Then $sHex &= "D"
		If $sFourBits = "1110" Then $sHex &= "E"
		If $sFourBits = "1111" Then $sHex &= "F"
	Until $sBi = ""

	Return $sHex
EndFunc   ;==>_BiToHex


Func _HexToBi($sHex)
	Local $i
	Local $sBi = ""
	Local $a = StringSplit($sHex, "")

	For $i = 1 To $a[0]
		If $a[$i] = "0" Then $sBi &= "0000"
		If $a[$i] = "1" Then $sBi &= "0001"
		If $a[$i] = "2" Then $sBi &= "0010"
		If $a[$i] = "3" Then $sBi &= "0011"
		If $a[$i] = "4" Then $sBi &= "0100"
		If $a[$i] = "5" Then $sBi &= "0101"
		If $a[$i] = "6" Then $sBi &= "0110"
		If $a[$i] = "7" Then $sBi &= "0111"
		If $a[$i] = "8" Then $sBi &= "1000"
		If $a[$i] = "9" Then $sBi &= "1001"
		If $a[$i] = "A" Then $sBi &= "1010"
		If $a[$i] = "B" Then $sBi &= "1011"
		If $a[$i] = "C" Then $sBi &= "1100"
		If $a[$i] = "D" Then $sBi &= "1101"
		If $a[$i] = "E" Then $sBi &= "1110"
		If $a[$i] = "F" Then $sBi &= "1111"
	Next

	Return $sBi
EndFunc   ;==>_HexToBi
 

SyDr

Сидра
Сообщения
651
Репутация
158
Имхо, не стоит реализовывать в таком виде. Получается, что перекодировка нужна и при сохранении, и при загрузке данных.

По мне лучше такой вариант:
SQL:
CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, Name varchar(256), NameLower varchar(256));
INSERT INTO t (Name, NameLower) VALUES ('Иван', 'иван');
INSERT INTO t (Name, NameLower) VALUES ('Владимир', 'владимир');
SELECT Name FROM t WHERE NameLower LIKE '%и%';
 
Верх