как перебирать отдельные символы в строке Lua?
У меня есть строка в Lua и я хочу перебирать в ней отдельные символы. Но ни один код, который я пробовал, не работает, и официальное руководство показывает только, как найти и заменить подстроки :(
str = "abcd"
for char in str do -- error
print( char )
end
for i = 1, str:len() do
print( str[ i ] ) -- nil
end
5 ответов:
в lua 5.1 вы можете повторить символы строки это несколькими способами.
основной цикл будет такой:
for i = 1, #str do local c = str:sub(i,i) -- do something with c endно это может быть более эффективным, чтобы использовать шаблон с
string.gmatch()
чтобы получить итератор над символами:for c in str:gmatch"." do -- do something with c endили даже использовать
string.gsub()
чтобы вызвать функцию для каждого символа:str:gsub(".", function(c) -- do something with c end)во всем вышеперечисленном я воспользовался тем, что
string
модуль устанавливается как метатаблиц для всех строковых значений, поэтому его функции могут быть вызваны как члены с помощью:
нотации. Я также использовал (новый для 5.1, IIRC)#
чтобы получить длину строки.лучший ответ для вашего приложения зависит от многих факторов, и показатели вашего друга, если производительность будет иметь.
вы, возможно, захотите, чтобы оценить почему вам нужно перебрать символы и посмотреть на один из модулей регулярных выражений, которые были привязаны к Lua, или для современного подходим заглянем в Роберто lpeg модуль, который реализует синтаксический анализ грамматики выражений для Lua.
Если вы используете Lua 5, попробуйте:
for i = 1, string.len(str) do print( string.sub(str, i, i) ) end
В зависимости от поставленной задачи это может быть проще в использовании
string.byte
. Это также самый быстрый способ, потому что он позволяет избежать создания новой подстроки, которая оказывается довольно дорогой в Lua благодаря хэшированию каждой новой строки и проверке, если она уже известна. Вы можете предварительно рассчитать код символов, которые вы ищете с тем жеstring.byte
сохранить читаемость и портируемость.local str = "ab/cd/ef" local target = string.byte("/") for idx = 1, #str do if str:byte(idx) == target then print("Target found at:", idx) end end
в представленных ответах уже есть много хороших подходов (здесь,здесь и здесь). Если скорость это то, что вы в первую очередь ища, вы определенно должны рассмотреть возможность выполнения этой работы через API Lua C, который во много раз быстрее, чем исходный код Lua. При работе с предварительно загруженными кусками (например. функции нагрузки), разница не такая большая, но все-таки значительная.
Что касается чисто Lua solutions, позвольте мне поделиться этим небольшим эталоном, который я сделал. Он охватывает каждый предоставленный ответ на эту дату и добавляет несколько оптимизаций. Тем не менее, основная вещь, чтобы рассмотреть это:
сколько раз вам нужно будет перебирать символы в строке?
- если ответ "один раз", то вы должны посмотреть первую часть banchmark ("raw speed").
- в противном случае, вторая часть даст более точное оценка, потому что он анализирует строку в таблицу, которая намного быстрее переберет. Вы также должны рассмотреть возможность написания простой функции для этого, как предложил @Jarriz.
вот полный код:
-- Setup locals local str = "Hello World!" local attempts = 5000000 local reuses = 10 -- For the second part of benchmark: Table values are reused 10 times. Change this according to your needs. local x, c, elapsed, tbl -- "Localize" funcs to minimize lookup overhead local stringbyte, stringchar, stringsub, stringgsub, stringgmatch = string.byte, string.char, string.sub, string.gsub, string.gmatch print("-----------------------") print("Raw speed:") print("-----------------------") -- Version 1 - string.sub in loop x = os.clock() for j = 1, attempts do for i = 1, #str do c = stringsub(str, i) end end elapsed = os.clock() - x print(string.format("V1: elapsed time: %.3f", elapsed)) -- Version 2 - string.gmatch loop x = os.clock() for j = 1, attempts do for c in stringgmatch(str, ".") do end end elapsed = os.clock() - x print(string.format("V2: elapsed time: %.3f", elapsed)) -- Version 3 - string.gsub callback x = os.clock() for j = 1, attempts do stringgsub(str, ".", function(c) end) end elapsed = os.clock() - x print(string.format("V3: elapsed time: %.3f", elapsed)) -- For version 4 local str2table = function(str) local ret = {} for i = 1, #str do ret[i] = stringsub(str, i) -- Note: This is a lot faster than using table.insert end return ret end -- Version 4 - function str2table x = os.clock() for j = 1, attempts do tbl = str2table(str) for i = 1, #tbl do -- Note: This type of loop is a lot faster than "pairs" loop. c = tbl[i] end end elapsed = os.clock() - x print(string.format("V4: elapsed time: %.3f", elapsed)) -- Version 5 - string.byte x = os.clock() for j = 1, attempts do tbl = {stringbyte(str, 1, #str)} -- Note: This is about 15% faster than calling string.byte for every character. for i = 1, #tbl do c = tbl[i] -- Note: produces char codes instead of chars. end end elapsed = os.clock() - x print(string.format("V5: elapsed time: %.3f", elapsed)) -- Version 5b - string.byte + conversion back to chars x = os.clock() for j = 1, attempts do tbl = {stringbyte(str, 1, #str)} -- Note: This is about 15% faster than calling string.byte for every character. for i = 1, #tbl do c = stringchar(tbl[i]) end end elapsed = os.clock() - x print(string.format("V5b: elapsed time: %.3f", elapsed)) print("-----------------------") print("Creating cache table ("..reuses.." reuses):") print("-----------------------") -- Version 1 - string.sub in loop x = os.clock() for k = 1, attempts do tbl = {} for i = 1, #str do tbl[i] = stringsub(str, i) -- Note: This is a lot faster than using table.insert end for j = 1, reuses do for i = 1, #tbl do c = tbl[i] end end end elapsed = os.clock() - x print(string.format("V1: elapsed time: %.3f", elapsed)) -- Version 2 - string.gmatch loop x = os.clock() for k = 1, attempts do tbl = {} local tblc = 1 -- Note: This is faster than table.insert for c in stringgmatch(str, ".") do tbl[tblc] = c tblc = tblc + 1 end for j = 1, reuses do for i = 1, #tbl do c = tbl[i] end end end elapsed = os.clock() - x print(string.format("V2: elapsed time: %.3f", elapsed)) -- Version 3 - string.gsub callback x = os.clock() for k = 1, attempts do tbl = {} local tblc = 1 -- Note: This is faster than table.insert stringgsub(str, ".", function(c) tbl[tblc] = c tblc = tblc + 1 end) for j = 1, reuses do for i = 1, #tbl do c = tbl[i] end end end elapsed = os.clock() - x print(string.format("V3: elapsed time: %.3f", elapsed)) -- Version 4 - str2table func before loop x = os.clock() for k = 1, attempts do tbl = str2table(str) for j = 1, reuses do for i = 1, #tbl do -- Note: This type of loop is a lot faster than "pairs" loop. c = tbl[i] end end end elapsed = os.clock() - x print(string.format("V4: elapsed time: %.3f", elapsed)) -- Version 5 - string.byte to create table x = os.clock() for k = 1, attempts do tbl = {stringbyte(str,1,#str)} for j = 1, reuses do for i = 1, #tbl do c = tbl[i] end end end elapsed = os.clock() - x print(string.format("V5: elapsed time: %.3f", elapsed)) -- Version 5b - string.byte to create table + string.char loop to convert bytes to chars x = os.clock() for k = 1, attempts do tbl = {stringbyte(str, 1, #str)} for i = 1, #tbl do tbl[i] = stringchar(tbl[i]) end for j = 1, reuses do for i = 1, #tbl do c = tbl[i] end end end elapsed = os.clock() - x print(string.format("V5b: elapsed time: %.3f", elapsed))
пример вывода (Lua 5.3.4, Windows):
----------------------- Raw speed: ----------------------- V1: elapsed time: 3.713 V2: elapsed time: 5.089 V3: elapsed time: 5.222 V4: elapsed time: 4.066 V5: elapsed time: 2.627 V5b: elapsed time: 3.627 ----------------------- Creating cache table (10 reuses): ----------------------- V1: elapsed time: 20.381 V2: elapsed time: 23.913 V3: elapsed time: 25.221 V4: elapsed time: 20.551 V5: elapsed time: 13.473 V5b: elapsed time: 18.046
результат:
в моем случае
string.byte
иstring.sub
были самыми быстрыми с точки зрения сырой скорости. Когда используя таблицу кэша и повторно используя ее 10 раз за цикл,string.byte
версия была самой быстрой даже при преобразовании кодировки обратно в символы (что не всегда необходимо и зависит от использования).как вы, наверное, заметили, я сделал некоторые предположения на основе моих предыдущих тестов и применил их к коду:
- библиотечные функции должны быть всегда локализованы, если они используются внутри циклов, потому что это намного быстрее.
- вставка нового элемента в таблицу lua гораздо быстрее с помощью
tbl[idx] = value
чемtable.insert(tbl, value)
.- цикл через таблицу с помощью
for i = 1, #tbl
немного быстрее, чемfor k, v in pairs(tbl)
.- всегда предпочитайте версию с меньшим количеством вызовов функций, потому что сам вызов добавляет немного времени выполнения.
надеюсь, что это помогает.
все люди предлагают менее оптимальный метод
будет лучше:
function chars(str) strc = {} for i = 1, #str do table.insert(strc, string.sub(str, i, i)) end return strc end str = "Hello world!" char = chars(str) print("Char 2: "..char[2]) -- prints the char 'e' print("-------------------\n") for i = 1, #str do -- testing printing all the chars if (char[i] == " ") then print("Char "..i..": [[space]]") else print("Char "..i..": "..char[i]) end end