2011-03-09 46 views
6

¿Hay alguna manera de reemplazar un carácter en la posición N en una cadena en Lua.Modificación de un carácter en una cadena en Lua

Esto es lo que he encontrado hasta el momento:

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) 

no puedo utilizar ya sea como gsub que reemplazaría a cada toma, no sólo la captura en la posición N.

Respuesta

12

cadenas en Lua son inmutables. Eso significa que cualquier solución que reemplace texto en una cadena debe terminar construyendo una nueva cadena con el contenido deseado. Para el caso específico de reemplazar un solo personaje con otro contenido, deberá dividir la cadena original en una parte de prefijo y una parte de postfijo, y volver a concatenarlos alrededor del nuevo contenido.

Esta variación en su código:

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

es la traducción más directa con sencillo Lua. Probablemente sea lo suficientemente rápido para la mayoría de los propósitos. He arreglado el error que el prefijo deben ser los primeros pos-1 caracteres, y se han aprovechado del hecho de que si el último argumento de string.sub falta que se supone que es -1 que es equivalente a la final de la cadena.

Pero tenga en cuenta que se crea una serie de cadenas temporales que quedarse en la tienda de cuerda hasta que la recolección de basura se los come. Los temporales para el prefijo y el sufijo no se pueden evitar en ninguna solución. Pero esto también tiene que crear un temporal para que el primer operador .. sea consumido por el segundo.

Es posible que uno de los dos enfoques alternativos podrían ser más rápido. El primero es el solution offered by Paŭlo Ebermann, pero con un pequeño truco:

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

Este utiliza string.format para hacer el montaje del resultado con la esperanza de que puede adivinar el tamaño del búfer final sin necesidad de objetos adicionales temporales.

Pero tenga en cuenta que string.format es probable que tenga problemas con cualquier \0 caracteres en cualquier cadena que pase a través de su formato %s. Específicamente, ya que se implementa en términos de función estándar de C sprintf(), sería razonable esperar que para terminar la cadena sustituido en la primera aparición de \0. (Observado por el usuario Delusional Logic en un comentario.)

Una tercera alternativa que viene a la mente es la siguiente:

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

table.concat concatena eficientemente una lista de cadenas en un resultado final. Tiene un segundo argumento opcional que es texto para insertar entre las cadenas, que por defecto es "" que se adapta a nuestro propósito aquí.

Supongo que a menos que sus cadenas sean enormes y haga esta sustitución con frecuencia, no verá ninguna diferencia práctica de rendimiento entre estos métodos. Sin embargo, me ha sorprendido antes, así que perfile su aplicación para verificar que haya un cuello de botella y compare las posibles soluciones cuidadosamente.

+1

Gracias por la explicación en profundidad – dotminic

+1

Esto es viejo. Pero acabo de resolver un error menor en algún código que escribí. Resulta que el método '' replace_char2'' no inserta caracteres nulos ('' \ 0''). –

+0

@DelusionalLogic Buen punto. 'string.format' se basa sólidamente en la función' sprintf() 'de C estándar, y es probable que tenga problemas con los bytes NUL incrustados. – RBerteig

5

Usted debería usar pos dentro de su función en lugar de literal 1 y 3, pero aparte de esto se ve bien. Como las cuerdas de Lua son inmutables, no se puede hacer mucho mejor que esto.

Tal

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

es más eficiente que el operador .., pero lo dudo - si resulta ser un cuello de botella, medirlo (y luego decidir implementar esta función de sustitución en C).

+1

Sí, el operador '..' es la forma más lenta de concatenar cadenas ya que se crea una nueva cadena para cada' ..'. Los métodos más rápidos incluyen 'string.format' y' table.concat'. Sin embargo, esto no debería causar ningún efecto notable a menos que trabaje con cadenas muy grandes o muchas operaciones de concatenación. Por ejemplo, tenía un script que usaba más de 500MB de memoria para procesar un archivo de menos de 1MB usando alrededor de 5 '..' por línea de entrada mientras ordenaba y reconstruía la entrada como salida. Cambiarlo para almacenar cadenas en una tabla y 'table.concat' al final lo hizo tan rápido que ni siquiera me molesté en medirlo. – Arrowmaster

+1

@Arrowmaster: ¿Sabe usted que en 'a ... b .. c' hay dos (en vez de una) cadena nuevas creadas, o simplemente lo asume? En principio, esto podría ser optimizado por el compilador/intérprete para crear una sola cadena nueva, como se hace en Java para el operador '+'. Su ejemplo es otro caso, ya que allí realmente tiene que crear nuevas cadenas con cada instrucción. –

+0

@ Paŭlo Ebermann Sí, acabo de copiar el código, olvidé eliminar los literales. @Arrowmaster @ Paŭlo Ebermann Compararé el operador .. con el método de formato. Gracias por la visión. – dotminic

Cuestiones relacionadas