2011-03-09 8 views
6

C'è un modo per sostituire un carattere in posizione N in una stringa in Lua.Modifica di un carattere in una stringa in Lua

Questo è ciò che mi è venuta in mente finora:

function replace_char(pos, str, r) 
    return str:sub(pos, pos - 1) .. r .. str:sub(pos + 1, str:len()) 
end 

str = replace_char(2, "aaaaaa", "X") 
print(str) 

non posso usare gsub sia come sarebbe sostituire ogni acquisizione, non solo la cattura in posizione N.

risposta

12

Le stringhe in Lua sono immutabili. Ciò significa che qualsiasi soluzione che sostituisce il testo in una stringa deve finire per costruire una nuova stringa con il contenuto desiderato. Per il caso specifico di sostituzione di un singolo carattere con altri contenuti, è necessario suddividere la stringa originale in una parte di prefisso e una parte di postfix e concatenarli di nuovo attorno al nuovo contenuto.

Questa variazione sul vostro codice:

function replace_char(pos, str, r) 
    return str:sub(1, pos-1) .. r .. str:sub(pos+1) 
end 

è la traduzione più diretta per semplici Lua. Probabilmente è abbastanza veloce per la maggior parte degli scopi. Ho risolto il bug che il prefisso dovrebbe essere il primo pos-1 caratteri e ha approfittato del fatto che se manca l'ultimo argomento a string.sub si presume che sia -1 che equivale alla fine della stringa.

Ma si noti che crea un certo numero di stringhe temporanee che si bloccheranno nell'archivio di stringhe finché la raccolta di dati inutili non le mangerà. I provvisori per il prefisso e il suffisso non possono essere evitati in nessuna soluzione. Ma anche questo deve creare un temporaneo per il primo operatore .. da consumare dal secondo.

È possibile che uno dei due approcci alternativi possa essere più veloce. Il primo è il solution offered by Paŭlo Ebermann, ma con un piccolo ritocco:

function replace_char2(pos, str, r) 
    return ("%s%s%s"):format(str:sub(1,pos-1), r, str:sub(pos+1)) 
end 

Questo utilizza string.format per fare il montaggio del risultato, nella speranza che si può indovinare la dimensione del buffer finale senza bisogno di oggetti aggiuntivi temporanei.

Ma attenzione che è possibile che string.format abbia problemi con qualsiasi \0 caratteri in qualsiasi stringa che passa attraverso il suo formato %s. In particolare, poiché è implementato in termini di funzione sprintf() di C standard, è ragionevole aspettarsi che termini la stringa sostituita alla prima occorrenza di \0. (Annotato dall'utente Delusional Logic in un commento.)

Una terza alternativa che viene in mente è questa:

function replace_char3(pos, str, r) 
    return table.concat{str:sub(1,pos-1), r, str:sub(pos+1)} 
end 

table.concat concatena in modo efficiente una lista di stringhe in un risultato finale. Ha un secondo argomento opzionale che è il testo da inserire tra le stringhe, che per impostazione predefinita è "" che si adatta al nostro scopo qui.

La mia ipotesi è che a meno che le stringhe non siano enormi e si effettui spesso questa sostituzione, non si noteranno differenze di prestazioni pratiche tra questi metodi. Tuttavia, sono stato sorpreso prima, quindi profila la tua applicazione per verificare che ci sia un collo di bottiglia e per valutare attentamente le possibili soluzioni.

+1

Grazie per la spiegazione approfondita – dotminic

+1

Questo è vecchio. Ma ho appena finito di risolvere un bug minore in qualche codice che ho scritto. Risulta che il metodo '' replace_char2'' non inserisce caratteri null ('' \ 0''). –

+0

@DelusionalLogic Buon punto. 'string.format' si basa solidamente sulla funzione' sprintf() 'di C standard, ed è probabile che abbia problemi con i byte NUL incorporati. – RBerteig

5

È dovrebbe utilizzare pos all'interno della funzione anziché letterale 1 e 3, ma a parte questo sembra buono. Poiché le stringhe Lua sono immutabili, non puoi davvero fare molto meglio di questo.

Forse

"%s%s%s":format(str:sub(1,pos-1), r, str:sub(pos+1, str:len()) 

è più efficiente rispetto all'operatore .., ma ne dubito - se si scopre di essere un collo di bottiglia, misurare (e poi decidere di implementare questa funzione di sostituzione in C).

+1

Sì, l'operatore '..' è il modo più lento per concatenare stringhe poiché viene creata una nuova stringa per ogni' ..'. I metodi più veloci includono 'string.format' e' table.concat'. Ciò non dovrebbe tuttavia causare effetti evidenti a meno che non si stia lavorando con stringhe molto grandi o con molte operazioni di concatenazione. Ad esempio, avevo uno script che utilizzava oltre 500 MB di memoria per elaborare un file inferiore a 1 MB utilizzando circa 5 ".." per riga di input mentre si ordina e si ricostruisce l'input come output. Cambiarlo per immagazzinare le stringhe in una tabella e 'table.concat' alla fine lo ha reso così veloce che non mi sono nemmeno preoccupato di misurare. – Arrowmaster

+1

@Arrowmaster: Sai che in 'a .. b .. c' ci sono due (invece di una sola) nuove stringhe create, o lo si assume semplicemente? In linea di principio questo potrebbe essere ottimizzato dal compilatore/interprete per creare solo una nuova stringa, come avviene in Java per l'operatore '+'. Il tuo esempio è un altro caso, dal momento che devi veramente creare nuove stringhe con ogni affermazione. –

+0

@ Paŭlo Ebermann yeah Ho appena copiato il codice, ho dimenticato di rimuovere i letterali. @Arrowmaster @ Paŭlo Ebermann Confronto l'operatore .. con il metodo di formattazione. Grazie per l'intuizione. – dotminic