Expresiones regulares en Kotlin con ejemplos: en la práctica
\n\nLas expresiones regulares (regex) son una herramienta poderosa para trabajar con cadenas. En Kotlin, están implementadas basándose en las clases de Java Pattern y Matcher, pero el lenguaje añade sus propias extensiones convenientes y azúcar sintáctico. En este artículo, analizaremos cómo usar expresiones regulares en Kotlin con ejemplos reales: desde la búsqueda simple hasta el reemplazo y la división de cadenas.
Fundamentos de sintaxis: creación y compilación
\n\nEn Kotlin, una expresión regular se puede crear de dos maneras: a través de la clase Regex o usando una cadena con un patrón. La primera forma es preferible, ya que proporciona más métodos.
// Forma 1: a través de la clase Regex\nval regex = Regex("\\\\d+") // uno o más dígitos\n\n// Forma 2: a través de una cadena (no recomendado para expresiones complejas)\nval pattern = "\\\\d+".toRegex()\n\nObserva la doble barra invertida de escape: \\\\d — esto es un escape para Java/Kotlin, para obtener el carácter \\d en la cadena. Para simplificar, Kotlin admite cadenas raw (triples comillas """), donde no se requiere escape:
val regex = Regex("""\\d+""") // cadena raw — no es necesario duplicar las barras invertidas\n\nTambién se pueden añadir banderas, por ejemplo, RegexOption.IGNORE_CASE:
val regex = Regex("kotlin", RegexOption.IGNORE_CASE)\n\nBúsqueda y verificación de coincidencias
\n\nLas operaciones más comunes son verificar si una cadena contiene una subcadena según un patrón y encontrar todas las coincidencias.
\n\nVerificación de existencia de coincidencia: containsMatchIn
\n\nval text = "Mi número: 123-45-678"\nval digitRegex = Regex("""\\d+""")\n\nif (digitRegex.containsMatchIn(text)) {\n println("¡El texto contiene dígitos!")\n}\n\nBúsqueda de la primera coincidencia: find
\n\nval result = digitRegex.find(text)\nprintln(result?.value) // Imprime: 123\n\nBúsqueda de todas las coincidencias: findAll
\n\nval allMatches = digitRegex.findAll(text)\nallMatches.forEach { match ->\n println("Encontrado: ${match.value}") // 123, 45, 678\n}\n\nEl método findAll devuelve una secuencia de objetos MatchResult. Cada uno de estos objetos contiene no solo la coincidencia en sí (.value), sino también su posición en la cadena (.range).
Trabajo con grupos de captura
\n\nLos grupos de captura son partes del patrón encerradas entre paréntesis. Permiten extraer fragmentos de datos específicos. Por ejemplo, analicemos un email en nombre de usuario y dominio:
\n\nval email = "user@example.com"\nval emailRegex = Regex("""(.+)@(.+)""")\nval matchResult = emailRegex.find(email)\n\nif (matchResult != null) {\n val (username, domain) = matchResult.destructured\n println("Nombre de usuario: $username") // user\n println("Dominio: $domain") // example.com\n}\n\nLa construcción destructured descompone convenientemente los grupos en variables. Si hay más grupos, se puede acceder por índice:
val fullMatch = matchResult.value // user@example.com\nval group1 = matchResult.groupValues[1] // user\nval group2 = matchResult.groupValues[2] // example.com\n\nGrupos con nombre (Kotlin 1.7+)
\n\nA partir de Kotlin 1.7, se pueden usar grupos con nombre:
\n\nval namedRegex = Regex("""(?<name>\\w+)@(?<domain>\\w+\\.\\w+)""")\nval match = namedRegex.find("contact@kotlin.org")\nif (match != null) {\n println(match.groups["name"]?.value) // contact\n println(match.groups["domain"]?.value) // kotlin.org\n}\n\nReemplazo de texto con replace
\n\nEl método replace permite reemplazar todas las coincidencias con una nueva cadena. En el patrón de reemplazo, se puede hacer referencia a los grupos de captura con