Saltar al contenido
🗺️ Gratis: Mapa de México por municipio en Power BI — plantilla lista para descargar Descargar gratis →

Expresiones regulares en Power Query: Text.RegexTest, Extract y Replace

Expresiones regulares en Power Query: Text.RegexTest, Extract y Replace

Expresiones regulares en Power Query: Text.RegexTest, Extract y Replace

TL;DR: Power Query no tiene soporte nativo de REGEX. La solución más completa es usar Web.Page() para ejecutar JavaScript dentro de M, lo que permite implementar Text.RegexTest, Text.RegexExtract y Text.RegexReplace como funciones reutilizables con la misma sintaxis que Excel 365. Requiere acceso a internet en el Gateway o Desktop para ejecutar Web.Page.


No hemos podido validar tu suscripción.
Tu PDF va en camino — revisa tu correo

Tu guía de Power Query gratis

Excel ya tiene REGEXTEST, REGEXEXTRACT y REGEXREPLACE para Microsoft 365 desde 2024. Power Query todavía no. Para quienes procesan texto complejo —emails, códigos de producto, números de teléfono, patrones en campos de texto libre— esto es una limitación real.

La solución que la comunidad desarrolló usa un truco elegante: Web.Page() puede ejecutar HTML con JavaScript embebido, y JavaScript tiene soporte REGEX nativo completo.

Por qué Web.Page() para REGEX

Web.Page(html) renderiza HTML y devuelve el contenido resultante como tabla. Si ese HTML contiene un <script> que escribe el resultado de una operación regex con document.write(), podés capturar el output en M y usarlo como cualquier otro valor.

No es una solución oficial de Microsoft, pero funciona en Power BI Desktop y en Gateway cuando hay acceso a internet. Para ambientes offline o con restricciones de red, existe la alternativa de Python (ver al final).

Text.RegexTest — probar si un texto cumple un patrón

Devuelve true si el texto coincide con el patrón, false si no.

let
    Text.RegexTest = (text as text, pattern as text, optional caseSensitivity as number) =>
    let
        caseSensitivityValue = if caseSensitivity = null then 0 else caseSensitivity,
        regexModifier = if caseSensitivityValue = 0 then "" else "i",
        regexExpression = "/" & pattern & "/" & regexModifier,
        htmlContent =
            "<script>" &
            "var matches = """ & text & """.match(" & regexExpression & ");" &
            "document.write(matches ? 'true' : 'false');" &
            "</script>",
        resultText = Web.Page(htmlContent)[Data]{0}[Children]{0}[Children]{1}[Text]{0},
        resultValue = if resultText = "true" then true else false
    in
        resultValue
in
    Text.RegexTest

Parámetros:text — el texto a evaluar – pattern — expresión regular (sin barras delimitadoras, igual que en Excel) – caseSensitivity0 = sensible a mayúsculas (default), 1 = insensible

Ejemplo de uso:

// ¿Es un email válido?
Text.RegexTest("usuario@empresa.com", "[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}", 1)
// → true

// ¿Empieza con código de producto (3 letras + guion + números)?
Text.RegexTest("ABC-12345", "^[A-Z]{3}-\d+$", 0)
// → true

Text.RegexExtract — extraer texto que coincide con el patrón

Extrae la parte del texto que coincide con el patrón.

let
    Text.RegexExtract = (
        text as text,
        pattern as text,
        optional returnMode as number,
        optional caseSensitivity as number
    ) =>
    let
        returnModeValue = if returnMode is null then 0 else returnMode,
        caseSensitivityValue = if caseSensitivity = null then 0 else caseSensitivity,
        regexModifier = if caseSensitivityValue = 1 then "g" else "gi",
        regexExpression = "/" & pattern & "/" & regexModifier,
        javascriptLogic = if returnModeValue = 0 then
            "var match = str.match(regex); var res = match ? match[0] : null;"
        else if returnModeValue = 1 then
            "var res = str.match(regex);"
        else
            "var match = regex.exec(str); res = match ? match.slice(1) : null;",
        htmlContent = Text.Combine({
            "<script>",
            "var regex = ", regexExpression, ";",
            "var str = """, text, """;",
            javascriptLogic,
            "document.write(res)",
            "</script>"
        }),
        resultText = Web.Page(htmlContent)[Data]{0}[Children]{0}[Children]{1}[Text]{0}
    in
        resultText
in
    Text.RegexExtract

Ejemplos:

// Extraer el dominio de un email
Text.RegexExtract("usuario@empresa.com", "(?<=@)[^.]+\.[a-z]+")
// → "empresa.com"

// Extraer solo los dígitos de un código
Text.RegexExtract("ABC-12345-XY", "\d+")
// → "12345"

Text.RegexReplace — reemplazar partes del texto

Reemplaza las coincidencias del patrón con el texto indicado.

let
    Text.RegexReplace = (
        text as text,
        pattern as text,
        replacement as text,
        optional occurrence as number,
        optional caseSensitivity as number
    ) =>
    let
        occurrenceValue = if occurrence = null then 0 else occurrence,
        caseSensitivityValue = if caseSensitivity = null then 0 else caseSensitivity,
        regexModifier = if caseSensitivityValue = 0 then "g" else "gi",
        regexExpression = "/" & pattern & "/" & regexModifier,
        javascriptCode =
            "var regex = " & regexExpression & ";" &
            "var str = '" & text & "';" &
            "var replacement = '" & replacement & "';" &
            "var occ = " & Text.From(occurrenceValue) & ";" &
            "var matches = [];" &
            "var match;" &
            "while ((match = regex.exec(str)) !== null) {" &
            "  matches.push({index: match.index, length: match[0].length});" &
            "}" &
            "if (occ === 0) {" &
            "  document.write(str.replace(regex, replacement));" &
            "} else {" &
            "  var targetIndex = occ > 0 ? occ - 1 : matches.length + occ;" &
            "  if (targetIndex < 0 || targetIndex >= matches.length) {" &
            "    document.write(str);" &
            "  } else {" &
            "    var target = matches[targetIndex];" &
            "    document.write(str.substring(0, target.index) + replacement + str.substring(target.index + target.length));" &
            "  }" &
            "}",
        htmlContent = "<script>" & javascriptCode & "</script>",
        resultText = Web.Page(htmlContent)[Data]{0}[Children]{0}[Children]{1}[Text]{0}
    in
        resultText
in
    Text.RegexReplace

Ejemplos:

// Enmascarar número de teléfono
Text.RegexReplace("Cel: 099-555-1234", "\d{4}$", "****")
// → "Cel: 099-555-****"

// Normalizar separadores en fechas
Text.RegexReplace("2026/06/14", "/", "-")
// → "2026-06-14"

Cómo usarlas en una consulta real

Lo más práctico es definir las funciones como consultas auxiliares en el panel de consultas y referenciarlas desde otras consultas:

  1. Nueva consulta en blanco → pegar el código de cada función → renombrar como fnRegexTest, fnRegexExtract, fnRegexReplace.
  2. En la consulta de datos, agregar una columna personalizada que llame a la función:
= Table.AddColumn(
    Origen,
    "Es email válido",
    each fnRegexTest([Correo], "[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}", 1),
    type logical
)

Limitaciones y alternativa offline

Limitación principal: Web.Page() requiere conexión a internet. En entornos con Gateway sin acceso a internet o con políticas de seguridad restrictivas, las funciones fallan.

Alternativa para entornos offline: activar Python en Power BI Desktop y usar re de la librería estándar:

import pandas as pd
import re

dataset['tiene_codigo'] = dataset['campo'].apply(
    lambda x: bool(re.match(r'^[A-Z]{3}-\d+$', str(x))) if pd.notna(x) else False
)

Esta alternativa requiere Python instalado y configurado en opciones de Power BI Desktop, y no funciona en servicio sin un Gateway configurado con Python.

¿Cuándo conviene usar REGEX en Power Query?

  • Validar formatos: emails, códigos, RUT, CUIT, números de teléfono
  • Extraer fragmentos de texto libre: fechas en comentarios, montos en descripciones
  • Limpiar datos heterogéneos de fuentes externas (ERPs, SharePoint, CRM)
  • Normalizar campos de texto antes de cargar al modelo

Para transformaciones simples (quitar caracteres específicos, reemplazar texto fijo), Text.Remove, Text.Replace y Text.Select son más rápidas y no dependen de Web.Page. REGEX conviene cuando el patrón es dinámico o complejo.


Si trabajás con texto en Power BI y necesitás limpiar HTML además de aplicar patrones, combiná estas funciones con Html.Table que vimos en Convertir HTML a texto en Power Query: primero limpiás las etiquetas, después aplicás REGEX al texto resultante.

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *