🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Regex en Excel — el objeto RegExp y por qué tu coincidencia vuelve vacía

|

VBA Regex en Excel — el objeto RegExp y por qué tu coincidencia vuelve vacía

TL;DR — No hay una palabra clave Regex en VBA. Creas el objeto VBScript RegExp, ya sea con enlace tardío mediante CreateObject("VBScript.RegExp") o con enlace temprano vía una referencia. No hace nada hasta que fijas sus propiedades: .Pattern, y las dos que vienen con el valor poco útil por defecto — .Global = False (solo la primera coincidencia) y .IgnoreCase = False (distingue mayúsculas). Luego elige el verbo correcto: .Test devuelve un Boolean, .Replace devuelve una cadena (las retrorreferencias son $1, no \1), y .Execute devuelve una colección de coincidencias cuyos grupos capturados viven en .SubMatches — no en .Value.

Sub RegexBasics()
    Dim re As Object
    Set re = CreateObject("VBScript.RegExp")   ' enlace tardio - no necesita referencia
    re.Pattern = "PO-(\d+)"                     ' un grupo captura los digitos
    re.Global = True                            ' encuentra TODAS las coincidencias, no solo la primera
    re.IgnoreCase = True                        ' sin distinguir mayusculas

    Dim m As Object
    For Each m In re.Execute("po-17 and PO-9004")
        Debug.Print m.Value             ' PO-17    luego PO-9004  (la coincidencia completa)
        Debug.Print m.SubMatches(0)     ' 17       luego 9004     (el grupo capturado)
    Next m
End Sub

Lo que aprenderás

  • El modelo mental — regex es un objeto que creas, no una palabra clave
  • Enlace tardío frente a añadir la referencia, y el error de compilación que sigue
  • Por qué Global = False significa que solo obtienes la primera coincidencia
  • Qué devuelve cada uno de Test, Replace y Execute
  • Por qué extraer un valor significa ir más allá de Match.Value hasta SubMatches
  • Qué admite el sabor de patrones de VBScript, y qué no

El modelo mental: regex es un objeto que creas, no una palabra clave

Viniendo de casi cualquier otro lenguaje, esperas un operador o una función regex. VBA no tiene ninguno. Lo que tiene es un componente COM — la biblioteca VBScript Regular Expressions — que instancias, conectas con propiedades y luego llamas. Así que el modelo mental no es «llamar a una función» sino «configurar un objeto y luego hacerle una pregunta». Cada frustración de abajo es en realidad «se me olvidó fijar una propiedad antes de preguntar».

Eso también te dice cuándo vale la pena usarlo. RegExp es el peso pesado en lo alto de la escalera, por encima del operador Like y del simple InStr. Pagas un coste de preparación — crearlo, fijar el patrón, accionar los interruptores — y a cambio obtienes recuentos de repetición, alternancia y lo único que los otros no pueden hacer: sacar las piezas capturadas de la coincidencia.

Cómo prepararlo: enlace tardío frente a una referencia

Dos formas de obtener el objeto, y la elección tiene consecuencias reales:

' Enlace tardio - portatil, no necesita referencia, se resuelve en tiempo de ejecucion
Dim re As Object
Set re = CreateObject("VBScript.RegExp")
' Enlace temprano - anade primero la referencia, luego esto compila
' Tools -> References -> "Microsoft VBScript Regular Expressions 5.5"
Dim re As New RegExp

El enlace temprano te da IntelliSense y llamadas ligeramente más rápidas, pero solo compila si la referencia está marcada — y si envías el libro a alguien a quien le falta la referencia, obtiene un error de compilación User-defined type not defined en la línea As New RegExp antes de que se ejecute una sola instrucción. El enlace tardío (As Object + CreateObject) no tiene nada de esa fragilidad: se resuelve en tiempo de ejecución y viaja sin una referencia. Para cualquier cosa que distribuyas, prefiere el enlace tardío — la portabilidad vale más que perder IntelliSense.

Trampa 1: Global está apagado, así que solo obtienes la primera coincidencia

.Global vale False por defecto, y ese único valor por defecto causa los dos informes de «regex está roto» más comunes:

re.Pattern = "\d+"
re.Global = False                       ' el valor por defecto
Debug.Print re.Replace("a1 b2 c3", "#") ' "a# b2 c3"  <- solo se reemplaza la PRIMERA serie
Debug.Print re.Execute("a1 b2 c3").Count ' 1          <- solo se encuentra la PRIMERA coincidencia

re.Global = True
Debug.Print re.Replace("a1 b2 c3", "#") ' "a# b# c#"  <- ahora todas
Debug.Print re.Execute("a1 b2 c3").Count ' 3

Si un reemplazo solo tocó la primera aparición, o una búsqueda encontró una coincidencia cuando puedes ver tres, esta es casi siempre la causa. Fija .Global = True siempre que quieras decir «en todas partes». Su vecina .IgnoreCase vale False por defecto de la misma manera, así que un patrón que claramente debería coincidir con texto de distinto caso y no lo hace suele ser uno al que le falta re.IgnoreCase = True.

Trampa 2: Test, Replace y Execute devuelven tres cosas distintas

El objeto expone tres verbos, y confundir sus tipos de retorno es el segundo gran tropiezo:

  • re.Test(s) devuelve un Boolean — ¿coincide el patrón en algún lugar? Úsalo como validador, el primo con potencia de regex de Like.
  • re.Replace(s, replacement) devuelve una String — la entrada con cada coincidencia (si Global) reemplazada. Las retrorreferencias en el reemplazo usan un signo de dólar: $1 es el primer grupo capturado. Escribir \1 inserta una barra invertida y un uno literales, un fallo frecuente para quien llega de otros lenguajes.
  • re.Execute(s) devuelve una MatchCollection — una colección de objetos Match que recorres para inspeccionar o extraer.
re.Pattern = "(\d{4})-(\d{2})"
Debug.Print re.Test("2026-09")                 ' True
Debug.Print re.Replace("2026-09", "$2/$1")     ' "09/2026"  - $1, $2 son los grupos

Elige el verbo por la respuesta que necesitas: un sí o no es Test, una cadena transformada es Replace, y cualquier cosa donde quieres el texto coincidente o sus piezas es Execute.

Trampa 3: para extraer, ve más allá de Value hasta SubMatches

Esta es la que hace a la gente dar vueltas. Cada Match de .Execute tiene un .Value — el texto coincidente completo — y una colección .SubMatches que contiene los grupos capturados, las partes que envolviste en paréntesis:

re.Pattern = "Invoice #(\d+) dated (\d{4}-\d{2}-\d{2})"
re.Global = True
Dim m As Object
For Each m In re.Execute("Invoice #4471 dated 2026-09-04")
    Debug.Print m.Value          ' Invoice #4471 dated 2026-09-04   (la coincidencia completa)
    Debug.Print m.SubMatches(0)  ' 4471                              (primer grupo)
    Debug.Print m.SubMatches(1)  ' 2026-09-04                        (segundo grupo)
    Debug.Print m.FirstIndex     ' 0    (posicion de inicio base cero en la entrada)
Next m

Si quieres el número, quieres m.SubMatches(0), no m.Value — leer .Value y preguntarte por qué sigue conteniendo el texto que lo rodea es el fallo de extracción número uno. SubMatches es de base cero, y un grupo que no participó en la coincidencia vuelve como una cadena vacía, así que protégete con re.Test(s) (o el .Count de la colección) antes de indexar en él.

El sabor VBScript: qué admite y qué no el lenguaje de patrones

El regex de VBA es el sabor VBScript (RegExp 5.5), no PCRE, y las carencias hacen tropezar a cualquiera con experiencia de regex en otro sitio. Admite el kit de todos los días: \d \w \s y sus negaciones, los cuantificadores * + ? {n} {n,m}, la alternancia a|b, los grupos ( ), las clases de caracteres [ ] y los anclajes ^ $ (con .MultiLine decidiendo si esos significan línea o cadena entera). No admite lookbehind, grupos de captura con nombre ni grupos atómicos o posesivos — los patrones que se apoyan en esos simplemente no compilarán como esperas.

Un alivio específico de VBA: como la barra invertida no es un carácter de escape dentro de los literales de cadena de VBA, escribes los patrones directamente — "\d{4}", no "\\d{4}". Nada de doble escape, a diferencia de C#, Java o JSON. El único doblado que necesitas es para una comilla literal, que es el propio "" de VBA.

La opinión: regex vale la ceremonia solo para estructura de verdad

RegExp es la herramienta de texto más potente de VBA y la que más a menudo se usa demasiado pronto. Crear el objeto, fijar el patrón y acordarse de Global e IgnoreCase es ceremonia de verdad, y para una forma fija o un simple sí o no es ceremonia que no necesitas — Like es una línea legible e InStr es más rápido para «contiene este literal».

La línea que hay que trazar es estructura que debes desmontar. Cuando el patrón tiene repetición variable, alternancia de verdad o — la decisiva — un grupo que necesitas extraer, regex es la única herramienta de la caja que lo hace, y se gana su sueldo: un número de pedido sacado de una línea de asunto, las partes de una marca de tiempo desordenada, cada correo en un bloque de texto. Úsalo ahí, prefiere el enlace tardío para que viaje, fija Global e IgnoreCase a propósito, y lee SubMatches para las piezas. En cualquier punto por debajo de eso, baja de nuevo por la escalera.

Cuando el análisis es el objetivo — describe el trabajo en su lugar

Incluso cuando regex es la herramienta correcta, el patrón rara vez es el objetivo — «saca cada número de factura y su fecha de esta columna y divídelos en dos campos» lo es. Y un patrón VBScript con grupos de captura, más el bucle sobre Execute, más la contabilidad de SubMatches, más los casos borde donde una fila no coincide en absoluto, es mucha maquinaria frágil alrededor de una sola intención. ExcelMaster te deja describir el resultado en español llano — «extrae el número de factura y la fecha de la columna B a las columnas C y D, marca las filas sin coincidencia» — y genera Python que hace la extracción con un motor de regex de verdad, respalda tu archivo primero e informa de las filas que no pudo analizar. Tú describes qué sacar; él se encarga del patrón, los grupos y los fallos.

Preguntas frecuentes

¿Cómo uso regex en VBA?

Crea el objeto VBScript RegExp, fija sus propiedades y luego llama a un método. La forma portable es el enlace tardío: Set re = CreateObject("VBScript.RegExp"), luego re.Pattern = "\d+", re.Global = True, re.IgnoreCase = True, y por último re.Test(s), re.Replace(s, repl) o re.Execute(s) según quieras un Boolean, una cadena nueva o las coincidencias.

¿Por qué mi regex de VBA solo reemplaza la primera coincidencia?

Porque .Global vale False por defecto. Con Global apagado, tanto .Replace como .Execute se detienen tras la primera coincidencia. Fija re.Global = True antes de llamarlos para actuar sobre cada coincidencia de la cadena.

¿Cómo extraigo un grupo capturado en regex de VBA?

Recorre re.Execute(s) y lee Match.SubMatches. SubMatches(0) es el primer grupo entre paréntesis, SubMatches(1) el segundo, y así (de base cero). Match.Value es el texto coincidente completo, no el grupo — leer .Value cuando querías un grupo es el error de extracción más común.

¿Necesito añadir una referencia para usar regex en VBA?

Solo para el enlace temprano. Dim re As New RegExp requiere la referencia «Microsoft VBScript Regular Expressions 5.5» en Tools -> References, y no compila, con User-defined type not defined, si falta. El enlace tardío — Dim re As Object con CreateObject("VBScript.RegExp") — no necesita ninguna referencia y es la mejor opción para los libros que compartes.

¿Qué sabor de regex usa VBA, y admite lookbehind?

VBA usa el sabor VBScript (RegExp 5.5). Admite \d \w \s, cuantificadores, alternancia, grupos, clases de caracteres y anclajes ^ $, y retrorreferencias de reemplazo con $1. No admite lookbehind ni grupos de captura con nombre. Además, como la barra invertida no es especial dentro de los literales de cadena de VBA, escribes "\d{4}" directamente sin doble escape.

Probado en

Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 04/09/2026.

Guías relacionadas: VBA Like · VBA IsNumeric · VBA InStr · VBA Replace · VBA Split