Busca y reemplaza cadenas con expresiones regulares en PHP
Spanish (Español) translation by Ana Paulina Figueroa (you can also view the original English article)
PHP viene con una gran variedad de funciones para trabajar con cadenas. Puedes utilizar estas funciones incorporadas de PHP para recortar cadenas, unirlas o reemplazarlas. Las funciones de cadena en PHP son bastante poderosas por sí mismas. Puedes usarlas para hacer cambios que no distingan entre mayúsculas y minúsculas o para realizar varios reemplazos a la vez. Sin embargo, aún requieren que especifiques las cadenas exactas que quieras reemplazar.
En ocasiones eso no es suficiente. Te encontrarás con muchas situaciones en las que estos reemplazos y coincidencias deben basarse en un patrón en lugar de en alguna cadena específica. Aquí es en donde las expresiones regulares juegan un papel muy importante.
En este tutorial aprenderás cómo encontrar coincidencias, buscar y reemplazar cadenas con expresiones regulares en PHP.
Comencemos por encontrar una coincidencia.
Uso de preg_match() de PHP para buscar patrones en cadenas en dicho lenguaje de programación
La función preg_match() de PHP se usa para encontrar una coincidencia dentro de cadenas según el patrón que especifiques. Tiene dos parámetros obligatorios: el patrón y la cadena en la que queramos encontrar una coincidencia.
Ten en cuenta que esta función solo devuelve 1, 0 o false, dependiendo de si hubo una coincidencia, si no hubo coincidencia o si se produjo un error. Sin embargo, puedes especificar un arreglo como tercer parámetro opcional para rellenarlo con información relevante sobre la coincidencia.
1 |
<?php
|
2 |
|
3 |
$pattern = '/[A-Z][a-z]+/'; |
4 |
|
5 |
$text = 'Adam, Andrew, or Monty will answer all your questions if you call us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
6 |
|
7 |
$names = []; |
8 |
|
9 |
$is_match = preg_match($pattern, $text, $names); |
10 |
|
11 |
echo $is_match; |
12 |
// Output: 1
|
13 |
|
14 |
var_dump($names); |
15 |
/*
|
16 |
array(1) {
|
17 |
[0]=>
|
18 |
string(4) "Adam"
|
19 |
*/
|
20 |
?>
|
Los nombres escritos en el idioma inglés generalmente comienzan con una letra mayúscula y contienen letras de la "a" a la "z". Usamos esta regla para crear un patrón muy básico y encontrar coincidencias de diferentes nombres en un texto dado.
El uso de preg_match(), que puedes ver arriba, devolvió 1 porque encontró una coincidencia. Recuerda que esta función no devuelve el número total de coincidencias ni la posición en la que que se encontró dicha coincidencia. Simplemente devuelve 1 si se encontró la coincidencia, y 0 si no se encontró ninguna.
El arreglo $names contiene un solo elemento, que supuestamente es la cadena que coincide totalmente. Cualquier elemento posterior corresponderá a todo aquello que coincidió con el subpatrón respectivo. Aquí puedes ver un ejemplo:
1 |
<?php
|
2 |
|
3 |
$pattern = '/([A-Z])([a-z]+)/'; |
4 |
|
5 |
$text = 'Adam, Andrew or Monty will answer all your questions if you call us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
6 |
|
7 |
$names = []; |
8 |
|
9 |
$is_match = preg_match($pattern, $text, $names); |
10 |
|
11 |
echo $is_match; |
12 |
// 1
|
13 |
|
14 |
var_dump($names); |
15 |
/*
|
16 |
array(3) {
|
17 |
[0]=>
|
18 |
string(4) "Adam"
|
19 |
[1]=>
|
20 |
string(1) "A"
|
21 |
[2]=>
|
22 |
string(1) "dam"
|
23 |
}
|
24 |
*/
|
25 |
|
26 |
?>
|
La función preg_match_all() funciona igual que preg_match(), pero devuelve el número de coincidencias del patrón completo en caso de éxito, o false en caso de error. Una cosa que debes recordar es que la búsqueda de las siguientes coincidencias continúa a partir de la posición final de la coincidencia anterior.
1 |
<?php
|
2 |
|
3 |
$pattern = '/[A-Z][a-z]+/'; |
4 |
|
5 |
$text = 'Adam, Andrew or Monty will answer all your questions if you call us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
6 |
|
7 |
$names = []; |
8 |
|
9 |
$match_count = preg_match_all($pattern, $text, $names); |
10 |
|
11 |
echo $match_count; |
12 |
// 3
|
13 |
|
14 |
var_dump($names); |
15 |
/*
|
16 |
array(1) {
|
17 |
[0]=>
|
18 |
array(3) {
|
19 |
[0]=>
|
20 |
string(4) "Adam"
|
21 |
[1]=>
|
22 |
string(6) "Andrew"
|
23 |
[2]=>
|
24 |
string(5) "Monty"
|
25 |
}
|
26 |
}
|
27 |
*/
|
28 |
|
29 |
|
30 |
?>
|
Los lectores perspicaces quizá hayan notado que preg_match_all() devolvió un arreglo multidimensional. Esto se debe a que el primer arreglo contiene una lista de todas las coincidencias exactas, y supuestamente los otros arreglos contienen una lista de grupos de captura subsecuentes, si existen.
1 |
<?php
|
2 |
|
3 |
$pattern = '/([A-Z])([a-z]+)/'; |
4 |
|
5 |
$text = 'Adam, Andrew or Monty will answer all your questions if you call us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
6 |
|
7 |
$names = []; |
8 |
|
9 |
$match_count = preg_match_all($pattern, $text, $names); |
10 |
|
11 |
echo $match_count; |
12 |
// 3
|
13 |
|
14 |
var_dump($names); |
15 |
/*
|
16 |
array(3) {
|
17 |
[0]=>
|
18 |
array(3) {
|
19 |
[0]=>
|
20 |
string(4) "Adam"
|
21 |
[1]=>
|
22 |
string(6) "Andrew"
|
23 |
[2]=>
|
24 |
string(5) "Monty"
|
25 |
}
|
26 |
[1]=>
|
27 |
array(3) {
|
28 |
[0]=>
|
29 |
string(1) "A"
|
30 |
[1]=>
|
31 |
string(1) "A"
|
32 |
[2]=>
|
33 |
string(1) "M"
|
34 |
}
|
35 |
[2]=>
|
36 |
array(3) {
|
37 |
[0]=>
|
38 |
string(1) "dam"
|
39 |
[1]=>
|
40 |
string(1) "ndrew"
|
41 |
[2]=>
|
42 |
string(1) "onty"
|
43 |
}
|
44 |
}
|
45 |
*/
|
46 |
|
47 |
?>
|
Como quizá hayas observado, preg_match_all() solamente devuelve el número de coincidencias del patrón completo.
Uso de preg_replace() para buscar y reemplazar en PHP
Puedes usar la función preg_replace() para buscar patrones en una cadena y luego reemplazarlos. Esta función acepta tres parámetros obligatorios. El primero es el patrón que quieras encontrar. El segundo es el reemplazo y el tercer parámetro es la cadena en la que quieras realizar estos reemplazos. Todos estos parámetros pueden ser un solo valor o un arreglo.
Estos son algunos ejemplos del uso de preg_replace() para ayudarte a comprender cómo funciona con diferentes tipos de parámetros.
1 |
<?php
|
2 |
|
3 |
$pattern = '/\d{10}/'; |
4 |
$replacement = 'contact@test.com'; |
5 |
|
6 |
$text = 'Adam, Andrew or Monty will answer all your questions if you contact us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
7 |
|
8 |
$new_text = preg_replace($pattern, $replacement, $text); |
9 |
|
10 |
var_dump($new_text); |
11 |
/*
|
12 |
string(128) "Adam, Andrew or Monty will answer all your questions if you contact us at contact@test.com on weekdays between 9:00AM to 5:00PM."
|
13 |
*/
|
14 |
|
15 |
?>
|
Aquí, $pattern y $replacement eran valores de una sola cadena. Nuestra intención era reemplazar el número telefónico con una dirección de correo electrónico con la que las personas puedan comunicarse con nosotros. El uso de una expresión regular garantiza que podamos reemplazar todos los diferentes números telefónicos de 10 dígitos a la vez.
1 |
<?php
|
2 |
|
3 |
$patterns = ['/([A-Z][a-z]+, )+[A-Z][a-z]+/', '/\d{10}/']; |
4 |
$replacements = ['Amanda, Ashley, Susan', 'contact@test.com']; |
5 |
|
6 |
$text = 'Adam, Andrew, Shaun or Monty will answer all your questions if you contact us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
7 |
|
8 |
$new_text = preg_replace($patterns, $replacements, $text); |
9 |
|
10 |
var_dump($new_text); |
11 |
/*
|
12 |
string(137) "Amanda, Ashley, Susan or Monty will answer all your questions if you contact us at contact@test.com on weekdays between 9:00AM to 5:00PM."
|
13 |
*/
|
14 |
|
15 |
?>
|
En esta ocasión proporcionamos los patrones y los reemplazos como un arreglo. En el ejemplo puedes ver que las partes que coinciden con el primer patrón fueron reemplazadas por la primera cadena de reemplazo en $replacements.
Habrá ocasiones en las que quieras que los patrones coincidentes sean parte de la cadena reemplazada en PHP. Esto puede lograrse fácilmente con grupos de captura. Este es un ejemplo en el que agregamos el conjunto de nombres nuevos a la lista anterior en lugar de reemplazarlo.
1 |
<?php
|
2 |
|
3 |
$patterns = ['/(([A-Z][a-z]+, )+[A-Z][a-z]+)/', '/\d{10}/']; |
4 |
$replacements = ['${1}, Amanda, Ashley, Susan', 'contact@test.com']; |
5 |
|
6 |
$text = 'Adam, Andrew, Shaun or Monty will answer all your questions if you contact us at 1234567890 on weekdays between 9:00AM to 5:00PM.'; |
7 |
|
8 |
$new_text = preg_replace($patterns, $replacements, $text); |
9 |
|
10 |
var_dump($new_text); |
11 |
/*
|
12 |
string(158) "Adam, Andrew, Shaun, Amanda, Ashley, Susan or Monty will answer all your questions if you contact us at contact@test.com on weekdays between 9:00AM to 5:00PM."
|
13 |
*/
|
14 |
|
15 |
?>
|
Usamos paréntesis alrededor de nuestro primer patrón para convertirlo en un grupo de captura único. Este se convierte en nuestro primer grupo de captura, y el uso de ${1} en la cadena de reemplazo antepone la lista de nombres anterior a los nombres nuevos.
1 |
<?php
|
2 |
|
3 |
$patterns = ['/(([A-Z][a-z]+, )+[A-Z][a-z]+)/', '/\d{10}/']; |
4 |
$replacements = ['Amanda, Ashley, Susan', 'contact@test.com']; |
5 |
|
6 |
$texts = ['Adam, Andrew, Shaun or Monty will answer all your questions if you contact us at 1234567890 on weekdays between 9:00AM to 5:00PM.', 'John, Monty and Jessica can also be contacted at 0123456789 on weekends.']; |
7 |
|
8 |
$new_text = preg_replace($patterns, $replacements, $texts); |
9 |
|
10 |
var_dump($new_text); |
11 |
/*
|
12 |
array(2) {
|
13 |
[0]=>
|
14 |
string(137) "Amanda, Ashley, Susan or Monty will answer all your questions if you contact us at contact@test.com on weekdays between 9:00AM to 5:00PM."
|
15 |
[1]=>
|
16 |
string(88) "Amanda, Ashley, Susan and Jessica can also be contacted at contact@test.com on weekends."
|
17 |
}
|
18 |
*/
|
19 |
|
20 |
?>
|
En nuestro último ejemplo proporcionamos varias cadenas a preg_replace(), dentro de las cuales queríamos llevar a cabo reemplazos. Como puedes ver, todos los reemplazos se aplican a cada fragmento de texto individualmente.
Uso de preg_replace_callback() para buscar y reemplazar mediante el uso de funciones de devolución de llamada en PHP
En la sección anterior vimos cuán poderosas pueden ser las expresiones regulares para reemplazar cadenas que coincidan con un patrón específico. Incluso podemos usar grupos de captura para reutilizar dentro la cadena de reemplazo la coincidencia que hayamos encontrado en otro lugar.
En ocasiones necesitamos aún más control para modificar el grupo de captura como mejor nos parezca antes de hacer reemplazos. El uso de la función preg_replace_callback() será la elección perfecta para eso.
Hasta ahora, en todos nuestros ejemplos hemos reemplazado todos los diferentes números telefónicos con una sola dirección de correo electrónico. Sin embargo, es más probable que una compañía con distintos números telefónicos también tenga diferentes direcciones de correo electrónico dedicadas. La función preg_replace_callback() nos permite llevar a cabo estos reemplazos caso por caso, para que podamos hacer los cambios apropiados en la cadena de reemplazo según el contenido del valor coincidente.
1 |
<?php
|
2 |
|
3 |
function update_email($matches) { |
4 |
$name_lower = strtolower($matches[1]); |
5 |
$email_address = $name_lower.'@test.com'; |
6 |
return $matches[1].' ('.$matches[2].' - '.$email_address.')'; |
7 |
}
|
8 |
|
9 |
$pattern = '/([A-Z][a-z]+) \((\d{10})\)/'; |
10 |
|
11 |
$text = 'Adam (1234567890), Andrew (0567894321) or Monty (9876501234) will answer all your questions if you contact us on weekdays between 9:00AM to 5:00PM.'; |
12 |
|
13 |
$new_text = preg_replace_callback($pattern, "update_email", $text); |
14 |
|
15 |
var_dump($new_text); |
16 |
/*
|
17 |
string(198) "Adam (1234567890 - adam@test.com), Andrew (0567894321 - andrew@test.com) or Monty (9876501234 - monty@test.com) will answer all your questions if you contact us on weekdays between 9:00AM to 5:00PM."
|
18 |
*/
|
19 |
?>
|
En este ejemplo, cada miembro del equipo tiene un número telefónico diferente, y tuvimos que reemplazar el número telefónico por una combinación de número de teléfono y direcciones de correo electrónico. Esto puede lograrse de manera muy sencilla con preg_replace_callback().
Comenzamos definiendo nuestra propia función update_email(), que recibe $matches como entrada. El arreglo $matches es el mismo que discutimos cuando hablamos de preg_match(). El primer elemento en $matches será la coincidencia completa, y los otros elementos contendrán los subpatrones subsecuentes.
La expresión regular de nuestro patrón contiene dos grupos de captura para obtener el nombre y el número telefónico como elementos separados. Usamos el nombre de la persona para obtener nuestra dirección de correo electrónico convirtiéndolo a minúsculas mediante el uso de strtolower(), y luego agregamos @test.com. Finalmente, devolvemos nuestro texto de reemplazo.
La versión más reciente de nuestra cadena principal ahora contiene tanto el número telefónico como la dirección de correo electrónico de los empleados.
Uso de preg_split() para dividir cadenas en PHP
La función preg_split() es útil cuando quieres dividir una cadena principal en varias subcadenas en base a patrones específicos. Esto es como tener una versión extrema de explode().
Esta función acepta cuatro parámetros. Los primeros dos son necesarios en cada llamada a la función, y especifican el patrón que debemos buscar y la cadena principal. Los siguientes dos parámetros son opcionales, y especifican cuántas cadenas deben devolverse y si los valores devueltos deben ser modificados con banderas.
Este es un ejemplo que te muestra cómo obtener nombres individuales de una cadena que contiene una lista grande de nombres. Los nombres están separados ya sea por uno o más espacios, una o más comas y uno o más caracteres - o _.
1 |
<?php
|
2 |
|
3 |
$pattern = '/[\s,\-_]/'; |
4 |
|
5 |
$text = 'Adam, Amanda Susan-Holly, Andrew---Monty--Shaun'; |
6 |
|
7 |
$all_names = preg_split($pattern, $text, -1, PREG_SPLIT_NO_EMPTY); |
8 |
|
9 |
var_dump($all_names); |
10 |
/*
|
11 |
array(7) {
|
12 |
[0]=>
|
13 |
string(4) "Adam"
|
14 |
[1]=>
|
15 |
string(6) "Amanda"
|
16 |
[2]=>
|
17 |
string(5) "Susan"
|
18 |
[3]=>
|
19 |
string(5) "Holly"
|
20 |
[4]=>
|
21 |
string(6) "Andrew"
|
22 |
[5]=>
|
23 |
string(5) "Monty"
|
24 |
[6]=>
|
25 |
string(5) "Shaun"
|
26 |
}
|
27 |
*/
|
28 |
?>
|
Establecemos el valor del tercer parámetro en -1 para obtener todas las divisiones posibles. Luego, la bandera PREG_SPLIT_NO_EPMTY se usa para deshacernos de las cadenas vacías.
Pensamientos finales
En este tutorial aprendimos cómo usar algunas funciones útiles y comunes relacionadas con las expresiones regulares en PHP. Comenzamos aprendiendo cómo usar preg_match() o preg_match_all() para obtener todas las cadenas que coinciden con un patrón específico. Después de eso, aprendimos cómo usar preg_replace() y preg_replace_callback() para reemplazar los patrones coincidentes con alguna otra cadena.
Las expresiones regulares nos proporcionan una herramienta muy poderosa pero frágil para manipular cadenas en PHP. Debes tener cuidado al definir tus patrones, pero ahorran mucho tiempo si se usan correctamente.











