J'ai une chaîne dans Lua et je veux y parcourir des caractères individuels. Mais aucun code que j'ai essayé ne fonctionne et le manuel officiel montre seulement comment trouver et remplacer des sous-chaînes :(
str = "abcd"
for char in str do -- error
print( char )
end
for i = 1, str:len() do
print( str[ i ] ) -- nil
end
Dans Lua 5.1, vous pouvez effectuer une itération des caractères d’une chaîne de deux manières différentes.
La boucle de base serait:
pour i = 1, #str do local c = str: sous (i, i) - faire quelque chose avec c end
Mais il peut être plus efficace d’utiliser un modèle avec string.gmatch()
pour obtenir un itérateur sur les caractères:
pour c dans str: gmatch "." faire - faire quelque chose avec c end
Ou même utiliser string.gsub()
pour appeler une fonction pour chaque caractère:
str: gsub (".", fonction (c) - faire quelque chose avec c end)
Dans tout ce qui précède, j'ai tiré parti du fait que le module string
est défini en tant que métatable pour toutes les valeurs de chaîne. Ses fonctions peuvent donc être appelées en tant que membres à l'aide de la notation :
. J'ai aussi utilisé le #
(nouveauté 5.1, IIRC) pour obtenir la longueur de la chaîne.
La meilleure réponse pour votre application dépend de nombreux facteurs. Les critères de référence sont votre meilleur ami si la performance compte.
Vous voudrez peut-être évaluer pourquoi vous devez parcourir les caractères et regarder l'un des modules d'expression régulière liés à Lua, ou une approche moderne dans le module lpeg de Roberto qui implémente Grammers d'expression syntaxique pour Lua.
Si vous utilisez Lua 5, essayez:
for i = 1, string.len(str) do
print( string.sub(str, i, i) )
end
En fonction de la tâche à accomplir, il sera peut-être plus facile d’utiliser string.byte
. C'est aussi le moyen le plus rapide, car cela évite de créer une nouvelle sous-chaîne qui devient assez chère en Lua, grâce au hachage de chaque nouvelle chaîne et à la vérification de sa présence. Vous pouvez pré-calculer le code des symboles que vous recherchez avec le même string.byte
afin de maintenir la lisibilité et la portabilité.
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
Il y a déjà beaucoup de bonnes approches dans les réponses fournies ( ici , ici et ici ). Si la vitesse est ce que vous recherchez principalement, vous devez absolument envisager de le faire via l'API C de Lua, qui est bien plus rapide que le code Lua brut. Lorsque vous travaillez avec des morceaux préchargés (par exemple, fonction de chargement ), la différence n’est pas si grande, mais elle reste quand même considérable.
En ce qui concerne les solutions pure Lua, permettez-moi de vous présenter ce petit repère que j'ai déjà réalisé. Il couvre chaque réponse fournie à cette date et ajoute quelques optimisations. Cependant, la chose fondamentale à considérer est:
_ {Combien de fois vous aurez besoin de parcourir les caractères de la chaîne?} _
_ {Voici le code complet: _
-- 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))
Exemple de sortie _ (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
Résultat:
Dans mon cas, le string.byte
et le string.sub
étaient les plus rapides en termes de vitesse brute. Lors de l'utilisation de la table de cache et de sa réutilisation 10 fois par boucle, la version string.byte
était la plus rapide, même lors de la reconversion des codes en caractères (ce qui n'est pas toujours nécessaire et dépend de l'utilisation).
Comme vous l'avez probablement remarqué, j'ai émis des hypothèses sur la base de mes références précédentes et les ai appliquées au code:
tbl[idx] = value
que table.insert(tbl, value)
.for i = 1, #tbl
est un peu plus rapide que for k, v in pairs(tbl)
.J'espère que ça aide.
Tout le monde suggère une méthode moins optimale
Sera le meilleur:
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