Publi

Sustituyendo texto con expresiones regulares en EMACS

regular_expression

Una de las herramientas más utilizadas (por mí al menos) es la de reemplazar texto. En EMACS la podemos encontrar con:

M-x replace-string

Con esta orden podemos cambiar un texto por otro dentro de un buffer o una selección. Hasta aquí bien. Pero alguna vez nos podemos encontrar con un texto que debemos reemplazar por otro, y aunque no es exactamente igual en todos los reemplazos que tenemos que hacer sigue una cierta lógica.

Imaginemos que tenemos este código en PHP (Gracias Antonio):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  switch ($pregunta) {
    case 0: $preg="<br><br>¿Qué significa PHP?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. Power H Processor</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. PHP: Hypertext Preprocessor</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. Para Hacer Poco</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. PHP: Harvard Preprocessor</a><br>";
            break;

    case 1: $preg="<br><br>¿Dónde se ejecuta PHP?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. En el Cliente</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. En algún lugar de la Mancha...</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. En el Servidor</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. Ninguna de las anteriores</a><br>";
            break;

    case 2: $preg="<br><br>¿Cuál de las siguientes variables NO existe como predefinida en PHP?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. PTTS_BOOK</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. PHPSESSID</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. REQUEST_METHOD</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. REMOTE_ADDR</a><br>";
            break;

    case 3: $preg="<br><br>¿Quién creó PHP?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. Rasmus Lerdorf</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. Obi One Kenobi</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. Bill Gates</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. Andrew S. Tanenbaum</a><br>";
            break;

    case 4: $preg="<br><br>¿Con qué <i>marca</i> habrá un salto de línea en el código fuente?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. \\t</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. \\n</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. &lt;br&gt;</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. \\g</a><br>";
            break;

    case 5: $preg="<br><br>¿Cuál de las siguientes funciones NO se puede acceder al servidor de datos MySQL?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. mysql_query</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. mysql_connect</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. mssql_connect</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. mysql_pconnect</a><br>";
            break;

    case 6: $preg="<br><br>¿Cuál es la frase correcta?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. setcookie() puede ser llamada desde cualquier parte del documento HTML</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. Las cookies sólo se pueden mandar antes de mandar cualquier otra cabecera</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. Las cookies no son parte de la cabecera HTTP</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. El perro de San Roque no tiene rabo, porque el Chiquito se lo ha robado</a><br>";
            break;

    case 7: $preg="<br><br>¿La función ftp_get?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. Sube un fichero al servidor FTP</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. Establece una conexión FTP</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. Descarga un fichero del servidor FTP</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. Devuelve el nombre del directorio actual</a><br>";
            break;
}

Y queremos sustituirlo por este otro:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    addPregunta($pregresp, "¿Qué significa PHP?", "a. Power H Processor", "b. PHP: Hypertext Preprocessor", "c. Para Hacer Poco", "d. PHP: Harvard Preprocessor");

    addPregunta($pregresp, "¿Dónde se ejecuta PHP?", "a. En el Cliente", "b. En algún lugar de la Mancha...", "c. En el Servidor", "d. Ninguna de las anteriores");

    addPregunta($pregresp, "¿Cuál de las siguientes variables NO existe como predefinida en PHP?", "a. PTTS_BOOK", "b. PHPSESSID", "c. REQUEST_METHOD", "d. REMOTE_ADDR");

    addPregunta($pregresp, "¿Quién creó PHP?", "a. Rasmus Lerdorf", "b. Obi One Kenobi", "c. Bill Gates", "d. Andrew S. Tanenbaum");

    addPregunta($pregresp, "¿Con qué <i>marca</i> habrá un salto de línea en el código fuente?", "a. \\t", "b. \\n", "c. &lt;br&gt;", "d. \\g");

    addPregunta($pregresp, "¿Cuál de las siguientes funciones NO se puede acceder al servidor de datos MySQL?", "a. mysql_query", "b. mysql_connect", "c. mssql_connect", "d. mysql_pconnect");

    addPregunta($pregresp, "¿Cuál es la frase correcta?", "a. setcookie() puede ser llamada desde cualquier parte del documento HTML", "b. Las cookies sólo se pueden mandar antes de mandar cualquier otra cabecera", "c. Las cookies no son parte de la cabecera HTTP", "d. El perro de San Roque no tiene rabo, porque el Chiquito se lo ha robado");

    addPregunta($pregresp, "¿La función ftp_get?", "a. Sube un fichero al servidor FTP", "b. Establece una conexión FTP", "c. Descarga un fichero del servidor FTP", "d. Devuelve el nombre del directorio actual");

    addPregunta($pregresp, "¿Con qué función se puede eliminar una variable de sesión?", "a. session_unregister", "b. session_destroy", "c. session_register", "d. jam_session");

    addPregunta($pregresp, "¿Con que harías un buen bocadillo...?", "a. Con pavo de ese que tiene trozos de colores", "b. Con un milagro", "c. Con habichuelas", "d. Con las manos");

El switch podemos quitarlo a mano, y en este ejemplo, tal vez para 7 preguntas, podríamos hacerlo a mano, aunque si fuera alguna más, nos pensaríamos este nuevo método.

Bien, en EMACS, tenemos la siguiente herramienta:

M-x regexp-builder

Que nos seleccionará el texto que corresponde con la expresión regular que estemos construyendo, y nos ayudará a generar una expresión más compleja, en definitiva queremos que la expresión cubra todos los cases, para continuar la lectura del post, nos fijamos en el primero:

1
2
3
4
5
6
    case 0: $preg="<br><br>¿Qué significa PHP?<br>";
            $resp="<a href=".$_SERVER['PHP_SELF']."?respuesta=1>a. Power H Processor</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=2>b. PHP: Hypertext Preprocessor</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=3>c. Para Hacer Poco</a><br>".
                  "<a href=".$_SERVER['PHP_SELF']."?respuesta=4>d. PHP: Harvard Preprocessor</a><br>";
            break;

Para construir la expresión, hay mucho copy-paste, además, desde Linux podemos seleccionar texto y pegarlo con el botón central del ratón, lo que nos facilita mucho la vida. Por otra parte, hay que tener en cuenta que tenemos que escapar muchos caracteres (como «, [, (, ), ], ?), de todas formas, nos vamos dando cuenta de eso al construir la expresión, si EMACS no selecciona un carácter como correspondiente a la expresión, lo más normal es que tengamos que escaparlo, también, tenemos que tener en cuenta que un espacio en blanco se representa aquí como [:space:]. Con todo esto, construimos la siguiente expresión:

«case [0-9]: $preg=\»<br><br>[a-zA-Z_.\<\>\/\¿\?áéíóú[:space:]]*
\»;
[[:space:]]*$resp=\»<a href=\».\$_SERVER\\[‘PHP_SELF’\\].\»\\?respuesta=[0-9]>[\\_()a-zA-Z,\:áéíóú&;[:space:]\.]+</a><br>\».
[[:space:]]*\»<a href=\».\$_SERVER\\[‘PHP_SELF’\\].\»\\?respuesta=[0-9]>[\\_()a-zA-Z,&áéíóú\:;[:space:]\.]+</a><br>\».
[[:space:]]*\»<a href=\».\$_SERVER\\[‘PHP_SELF’\\].\»\\?respuesta=[0-9]>[\\_()a-zA-Z,&áéíóú\:;[:space:]\.]+</a><br>\».
[[:space:]]*\»<a href=\».\$_SERVER\\[‘PHP_SELF’\\].\»\\?respuesta=[0-9]>[\\_()a-zA-Z,&áéíóú\:;[:space:]\.]+

Se seleccionarán todos los case, ahora copiamos la expresión para luego pegarla para reemplazarla. Aunque antes, tenemos que fijarnos en una cosa, en cada línea que queremos sustituir hay fragmentos de texto que queremos que figuren en la cadena sustituida, queremos hacerlo también automáticamente, eso lo conseguimos poniendo entre paréntesis en la expresión anterior las cadenas que vamos a aprovechar del texto que vamos a sustituir. La dejamos así:

case [0-9]: $preg=\»<br><br>\([a-zA-Z_.\<\>\/\¿\?áéíóú[:space:]]*\)<br>\»;
[[:space:]]*$resp=\»<a href=\».\$_SERVER\[‘PHP_SELF’\].\»\?respuesta=[0-9]>\([\\_()a-zA-Z,\:áéíóú&;[:space:]\.]+\)</a><br>\».
[[:space:]]*\»<a href=\».\$_SERVER\[‘PHP_SELF’\].\»\?respuesta=[0-9]>\([\\_()a-zA-Z,&áéíóú\:;[:space:]\.]+\)</a><br>\».
[[:space:]]*\»<a href=\».\$_SERVER\[‘PHP_SELF’\].\»\?respuesta=[0-9]>\([\\_()a-zA-Z,&áéíóú\:;[:space:]\.]+\)</a><br>\».
[[:space:]]*\»<a href=\».\$_SERVER\[‘PHP_SELF’\].\»\?respuesta=[0-9]>\([\\_()a-zA-Z,&áéíóú\:;[:space:]\.]+\)</a><br>\».
[[:space:]]*break;

Ahora llamamos a la siguiente orden:

M-x replace-regexp

Introducimos como expresión la última que hemos hecho (la de los paréntesis, marqué en negrita los textos que queremos conservar). Y la sustituimos por lo siguiente:

addPregunta($pregresp, «\1», «\2», «\3», «\4», «\5»);

Vemos que es una función a la que le pasamos una variable y después cinco textos, el primer texto (\1), coincidirá con la pregunta (en el código de los cases), y desde el segundo (\2) hasta el quinto (\5) coincidirá con cada una de las cuatro posibles respuestas.

Una vez hecho esto se sustituirán todo el código de los cases, por el de la función addPregunta() con los textos correspondientes.

Tal vez este ejemplo haya sido un poco grande, pero las expresiones regulares son muy útiles, seguro que casi a diario encontráis la necesidad de hacer algo parecido a esto en vuestro editor de texto.

Referencias:
Text Pattern Matching in EMACS
Regexp Replace – GNU Emacs

Foto: fdecomite (Flickr)

También podría interesarte....

There are 3 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

Leave a Reply