lua xử lý chuỗi theo từng byte trong khi chữ Việt mã hóa theo UTF8 chiếm từ 1 đến 4 byte. Giải pháp là tìm kiếm một thư viện hỗ trợ chữ Việt Unicode cho lua.
Có nhiều thư viện giúp lua xử lý chuỗi unicode được đặt tên utf8.lua, thuộc nhiều tác giả khác nhau nhưng nội dung gần như giống hệt nhau chỉ khác đôi chút ở cách trình bày. Trong số đó có thể thư viện đầy đủ nhất là ở link này
- Các hàm xử lý chuỗi có phiên bản UTF8:
len
sub
reverse
char
unicode
gensub
byte
find
match
gmatch
gsub
dump
format
caseless
lower
upper
rep
2. Riêng các hàm caseless, lower và upper cần phải có file utf8data.lua. Trong file này có hai bảng chuyển đổi hoa/thường của các ký tự unicode, nhiều hơn các nguyên âm chữ Việt, vì vậy có thể viết lại để giảm kích thước/bộ nhớ (2-3KB so với 33KB).
utf8data.lua
utf8_lc_uc = { ["a"] = "A", ["b"] = "B", ["c"] = "C", ["d"] = "D", ["e"] = "E", ["f"] = "F", ["g"] = "G", ["h"] = "H", ["i"] = "I", ["j"] = "J", ["k"] = "K", ["l"] = "L", ["m"] = "M", ["n"] = "N", ["o"] = "O", ["p"] = "P", ["q"] = "Q", ["r"] = "R", ["s"] = "S", ["t"] = "T", ["u"] = "U", ["v"] = "V", ["w"] = "W", ["x"] = "X", ["y"] = "Y", ["z"] = "Z", ["à"] = "À", ["á"] = "Á", ["ã"] = "Ã", ["ả"] = "Ả", ["ạ"] = "Ạ", ["ă"] = "Ă", ["ằ"] = "Ằ", ["ắ"] = "Ắ", ["ẳ"] = "Ẳ", ["ẵ"] = "Ẵ", ["ặ"] = "Ặ", ["â"] = "Â", ["ấ"] = "Ấ", ["ầ"] = "Ầ", ["ẩ"] = "Ẩ", ["ẫ"] = "Ẫ", ["ậ"] = "Ậ", ["è"] = "È", ["é"] = "É", ["ẽ"] = "Ẽ", ["ė"] = "Ė", ["ẹ"] = "Ẹ", ["ê"] = "Ê", ["ế"] = "Ế", ["ề"] = "Ề", ["ể"] = "Ể", ["ễ"] = "Ễ", ["ệ"] = "Ệ", ["ì"] = "Ì", ["í"] = "Í", ["ĩ"] = "Ĩ", ["ỉ"] = "Ỉ", ["ị"] = "Ị", ["ò"] = "Ò", ["ó"] = "Ó", ["õ"] = "Õ", ["ỏ"] = "Ỏ", ["ọ"] = "Ọ", ["ơ"] = "Ơ", ["ớ"] = "Ớ", ["ờ"] = "Ờ", ["ở"] = "Ở", ["ỡ"] = "Ỡ", ["ợ"] = "Ợ", ["ô"] = "Ô", ["ố"] = "Ố", ["ồ"] = "Ồ", ["ổ"] = "Ổ", ["ỗ"] = "Ỗ", ["ộ"] = "Ộ", ["ù"] = "Ù", ["ú"] = "Ú", ["ủ"] = "Ủ", ["ũ"] = "Ũ", ["ụ"] = "Ụ", ["ư"] = "Ư", ["ứ"] = "Ứ", ["ừ"] = "Ừ", ["ử"] = "Ử", ["ữ"] = "Ữ", ["ự"] = "Ự", ["ý"] = "Ý", ["ỳ"] = "Ỳ", ["ỷ"] = "Ỷ", ["ỹ"] = "Ỹ", ["ỵ"] = "Ỵ", ["đ"] = "Đ" } utf8_uc_lc = {}for key,value in pairs(utf8_lc_uc) do
utf8_uc_lc[value] = key
end
}
3. Trong ngôn ngữ lua, tìm kiếm chuỗi luôn phân biệt hoa/thường, và không có khái niệm tìm trọn từ (whole word). Chúng ta thêm vào file utf8.lua hàm caseless giúp tạo ra mẫu tìm kiếm không phân biệt hoa/thường.
local function utf8caseless (str) local ret = '' length = utf8len (str) for i=1, length do c = utf8sub (str, i, i) lo= utf8lower (c) up= utf8upper (c) if lo == up then ret = ret .. c else ret = ret .. '[' .. lo .. up .. ']' end end return ret end
và thêm vào gần cuối file dòng
utf8.caseless = utf8caseless
4. Tóm lại, để sử dụng chữ Việt unicode, chúng ta cần 2 file utf8.lua và utf8data.lua. Các file này có thể để trong thư mục tùy ý, thí dụ /mnt/tool/.lua và thông báo cho lua khi sử dụng
package.path = package.path .. ';/mnt/tool/.lua/?.lua' require "utf8data" utf8 = require "utf8" print (utf8.match ("Hôm nay tôi đi học!", '[%s%p](' .. utf8.caseless('Đi học') .. ')[%s%p]') ) --> đi học
Chú ý là chúng ta tìm không phân biệt hoa thường và tìm trọn từ. Mẫu tìm kiếm ‘ôi‘ hay ‘họ‘ sẽ cho kết quả nil
5. Trong utf8.lua có hàm binsearch (binary search), có thể chỉnh sửa đôi chút cho phù hợp để sử dụng cho nhiều dạng so sánh chuỗi khác nhau
function binsearch(sortedTable, item, comp) local head, tail = 1, #sortedTable local mid = math.floor((head + tail)/2) local s if type(comp) ~= "function" then comp = function(s) return s end end while (tail - head) > 1 do if comp(sortedTable[tonumber(mid)]) > item then tail = mid else head = mid end mid = math.floor((head + tail)/2) end if comp(sortedTable[tonumber(head)]) == item then s = tonumber(head) elseif comp(sortedTable[tonumber(tail)]) == item then s = tonumber(tail) else return nil end local e = s while s >= 1 do if comp(sortedTable[s]) == item then s = s - 1 else break end end while e <= #sortedTable do if comp(sortedTable[e]) == item then e = e + 1 else break end end return s+1, e-1 end
sortedTable là bảng đã được sắp xếp theo tiêu chuẩn so sánh comp. Thí dụ binsearch tìm kiếm theo độ dài chuỗi thì sortedTable xếp thứ tự theo độ dài chuỗi. Hàm binsearch trả lại index đầu và cuối nếu tìm thấy và nil nếu không tìm thấy.
table.sort (sortedTable, function (a, b) return utf8.len(a) < utf8.len(b) end) return binsearch (sortedTable, 5, function (a) return utf8.len(a) end)
Nếu danh sách keyword.csv lớn, để biết chuỗi str có nằm trong danh sách hay không, chúng ta dò tìm theo độ dài của str trước sau đó mới dò tìm từ khóa trong quảng từ khóa cùng độ dài với str.
UTF8 trong LUA 5.3.x
LUA version 5.3 trở đi đã có những hàm chuỗi hỗ trợ unicode utf8, tuy rằng chỉ có những hàm hết sức cơ bản.
Để có những công cụ cần thiết hỗ trợ utf8, cần phải viết thêm bằng ngôn ngữ lua hay C. Tuy viết bằng ngôn ngữ lua nhanh và dễ nhưng chạy chậm, thậm chí rất chậm thí dụ như khi viết hàm user function cho sqlite3.
Một thư viện utf8 viết bằng C, tương thích với lua 5.3, có thể tìm thấy ở đây. Điểm đặc biệt là có thể dùng file nguồn của thư viện này chép vào bộ nguồn của lua 5.3 và biên dịch, khi đó các hàm utf8 được bổ sung phong phú và chạy rất nhanh.