Buscar y reemplazar (a)
Los procesadores de texto incluyen dos funcionalidades que resultan de mucha utilidad para analizar el contenido de un texto en términos cuantitativos y para modificar dicho contenido. Me refiero a Buscar y Buscar y reemplazar (1). En OOo Basic podemos desarrollar la primera funcionalidad haciendo uso de las [funciones Left(), Right() y Mid()], pero no disponemos de funciones Built-In para reemplazar subcadenas dentro de un texto.
La forma de superar esta carencia es utilizar funciones construidas por la comunidad de usuarios, algo que, por suerte, está a nuestro alcance: tanto en [wiki.openoffice.org] como en [wiki.open-office.es] podemos encontrar funciones que cumplen con este propósito.
Vamos a empezar por la primera (Replace()), disponible en [wiki.openoffice.org], que tiene por objetivo facilitar la búsqueda y el reemplazo de una subcadena, pero como limitación, que sólo lo hace con la primera aparición de dicha subcadena. Reproduzco primero su sintaxis y la explico a continuación:
Function Replace(Source As String, Search As String, NewPart As String)
CurrentPos = InStr(StartPos, Source, Search)
Result = Result + Mid(Source, StartPos, (CurrentPos - StartPos))
Result = Result + NewPart
StartPos = CurrentPos + Len(Search)
Result = Result + Mid(Source, StartPos, Len(Source))
La función Replace() (reemplazar) recibe tres parámetros: la cadena sobre la que trabaja (Source, fuente), la palabra que deseamos reemplazar (Search, buscar) y la palabra con la que deseamos reemplazar a la que buscamos (NewPart); como vemos, los tres de tipo String.
Tiene, además, tres variables privadas, necesarias para el funcionamiento de la función (Result,StartPos y CurrentPos), la primera como variable a la que referenciar el resultado de la función, obviamente de tipo String, y las otras dos de tipo numérico: StartPos (iniciar desde la posición...) y CurrentPos (posición actual) (2). Estas variables se inicializan a continuación con valores mínimos.
Lo primero que hace la función es controlar un posible error: detectar si la palabra buscada (Search) es en realidad una cadena (subcadena) vacía; en ese caso, mediante un condicional (If Search = "" Then), nos devolverá la cadena originaria como respuesta (Result = Source); en caso contrario (Else) se desarrolla lo nuclear de la función.
Esta parte, que es la fundamental de Replace(), contiene un bucle (Do While) que contiene a su vez un segundo condicional (If...End If), que parten ambas de un valor <>0 para la variable local CurrentPos (Do While CurrentPos <> 0 -> If CurrentPos <> 0 Then), condición que inicialmente se cumple, ya que CurrentPos se a inicializado con valor 1 (CurrentPos = 1).
Dentro de esta estructura se llama o hace uso de funciones Built-In (InStr(), Mid() y Len()) que destaco en negrita para facilitar su localización en el código (3).
La primera de la que hacemos uso mientras se mantiene la condición antes indicada (Do While CurrentPos <> 0), es la función InStr(), que recibe tres parámetros y queda modifica el valor inicial de CurrentPos (CurrentPos = InStr(StartPos, Source, Search)). Esos parámetros son, respectivamente, la variable privada StartPos (posición desde la que iniciar la búsqueda, cuyo valor se ha establecido inicialmente en 1 -> StartPos = 1), la cadena sobre la que se trabajo, pasada como parámetro a la función (Source) y la palabra a buscar, también pasada como parámetro (Search). Como ya sabemos, InStr()) nos devolverá, en CurrentPos, la posición en la que inicia su presencia la palabra que buscamos (Search) (4).
Este proceso se mantendrá activo (gracias al bucle) mientras que cumpla la condición (CurrentPos <> 0), lo cual dejará de suceder cuando, gracias a la función InStr(), la subcadena buscada ya no se encuentre en el segmento de la cadena originaria sobre la que trabajamos, dado que InStr() devuelve 0 cuando se da esta condición (5).
También en función de la condición que rige el bucle (Do While CurrentPos <> 0 -> If CurrentPos <> 0 Then), el condicional anidado desarrolla una serie de operaciones que implican el uso de las funciones Mid() [ver aquí] y Len() [ver aquí].
La primera operación (Result = Result + Mid(Source, StartPos, (CurrentPos - StartPos))) permite asignar a la variable Result el contenido previo de la misma más el resultado de la función Mid() (6), esto es: una subcadena.
La segunda operación (Result = Result + NewPart) concatena Result con la subcadena que obtuvimos con la primera sobre la propia variable Result.
Y la tercera (StartPos = CurrentPos + Len(Search)) modifica el valor de la variable StartPos (recordemos, la que marca la posición de inicio de la búsqueda), sumando al valor actual de la variable CurrentPos el tamaño de la subcadena que buscamos, obtenido mediante la función Len() (Len(Search)).
Cuando la condición inicial (If CurrentPos <> 0 ) deja de cumplirse entra en juego la segunda parte del condicional (Else), que desarrolla la operación final sobre la variable Result (Result = Result + Mid(Source, StartPos, Len(Source))), que consiste en devolver como resultado la concatenación de la subcadena Result con lo que devuelve de nuevo la función Mid() trabajando los parámetros cadena original (Source), el valor que corresponde a la variable StartPos (que ha sido modificada previamente por la instrucción StartPos = CurrentPos + Len(Search)) y la longitud de la cadena inicial obtenida mediante la función Len() (Len(Source)).
Al finalizar este condicional, una vez que deja de cumplir la condición de partida (CurrentPos <> 0), se cierra el condicional y el propio bucle y ya sólo resta devolver la nueva cadena (esto es, la original modificada mediante la sustitución de la palabra seleccionada para la búsqueda por la palabra que la sustituye) como resultado de la función (Replace = Result).
Si lo llevamos a la práctica, dado el script...
Dim sCad1Ini As String, sCad2Fin As String
Dim sResultaBusca As Integer
sCad1Ini = "Aquí hay una cadena"
sCad2Fin = Replace (sCad1Ini,"hay", "está")
MsgBox sCad1Ini & Chr(13) & sCad2Fin
... la llamada a la función Replace() (sCad2Fin = Replace (sCad1Ini,"hay", "está")) nos permite obtener como resultado la cadena Aquí está una cadena, referenciada en la variable sCad2Fin; siendo que hemos solicitado que la subcadena (la palabra) "hay" (segundo parámetro) sea sustituida por la subcadena "está" (tercer parámetro) en la cadena originaria "Aquí hay una cadena" (primer parámetro), referenciada por la variable sCad1Ini.
Esta nada sencilla función Replace() (7) no es la única opción para reemplazar palabras. Los colaboradores de [wiki.open-office.es] han desarrollado una alternativa, la función ReplaceAll(), que es también muy interesante. Dado lo extenso de esta entrada he considerado postponer para una [nueva entrada] el estudio de esta segunda función.
NOTAS
(1) En LO-Writer [Editar | Buscar] y [Editar | Buscar y Reemplazar] respectivamente. No veo necesario explicar ni el funcionamiento de estas funcionalidades ni su interés para el usuario, ya que forman parte de sus conocimientos básicos. Otra cosa es que su uso no sea ni todo lo frecuente que podría ser ni se obtenga de ellas la utilidad que se podría obtener. Pero estas son otras cuestiones.
(2) En el ejemplo se definen como Long, pero pueden definirse en la mayoría de los casos como Integer, ya que no son necesarios números tan grandes para que Replace() cumpla su objetivo.
(3) Este uso de funciones dentro de funciones, sean las primeras de tipo Built-In o creadas, es usado muy frecuentemente, ya que incrementa la potencia de procesamiento de las funciones.
(4) Para más información sobre InStr(), consultar [esta entrada] (5) Esto explica que utilicemos el parámetro de inicio de búsqueda en la función InStr(). (InStr(StartPos, Source, Search)) y que asignemos el resultado que devuelve a la variable que condiciona el funcionamiento del bucle. Dicho de otra forma, InStr() es, en realidad, el controlador que nos permite salir de un bucle que, en caso contrario, sería infinito, ya que CurrentPos tiene, de partida, un valor diferente de 0 (CurrentPos = 1) y sólo vale 0 cuando InStr() devuelve ese valor.
(6) que trabaja con estos tres parámetros: la cadena sobre la que trabajamos (Source), la posición de inicio (StartPos) y la diferencia entre el valor de la variable CurrentPos menos el de la posición de inicio (CurrentPos - StartPos)
(7) Desde el punto de vista didáctico, esta función es de gran utilidad, ya que permite comprobar el resultado que podemos obtener combinando estructuras y utilizando diferentes funciones Built-In.