Excel

El fin de RegEx en VBA ¡no tan rápido!

Hace poco, Mike Wolfe mencionó que Microsoft había anunciado que iban a eliminar gradualmente y retirar VBScript.

Y las implicaciones de esto podrían ser de largo alcance y afectar todo tipo de cosas, en particular la posible pérdida de:

  • Objeto del sistema de archivos (FSO)
  • Expresiones regulares (RegEx)
  • y más

Lo único que se ha dicho oficialmente es

A partir de octubre de 2023, VBScript quedará obsoleto. En futuras versiones de Windows, VBScript estará disponible como una función a pedido antes de su eliminación del sistema operativo.Microsoft
VBScript estará disponible como una función a pedido antes de ser retirada en futuras versiones de Windows. Inicialmente, la función VBScript a pedido estará preinstalada para permitir un uso ininterrumpido mientras se prepara para la retirada de VBScript.Microsoft

Ahora seamos claros aquí, todos somos… aún Estamos esperando a que Microsoft aclare el tema para saber al 100% qué se perderá cuando esto ocurra y qué bibliotecas desaparecerán. Hasta ahora todo son suposiciones lógicas basadas en lo que sabemos de las bibliotecas, pero hasta la fecha no se han dado detalles oficiales.Y sí, mucha gente ha pedido aclaraciones, pero hasta ahora no se ha dado ninguna.)

Después de años de esperar a que Microsoft brindara información pública sobre diversos temas y nunca se materializó o tardó demasiado, decidí no esperar en este asunto. Quería intentar prepararme lo mejor posible por si ocurrían cosas así. ¡Planifique ahora en lugar de entrar en pánico más tarde!

Por eso he decidido compartir mi trabajo aquí con la esperanza de que pueda ayudar a otros.

Inicialmente, decidí concentrarme en ver si no había otro enfoque que pudiera usarse en VBA para proporcionar la funcionalidad RegEx sin el uso de la biblioteca RegulareExpression de VBScript (CrearObjeto(“VBScript.RegExp”)) Para ello recurrí a mi experiencia en desarrollo web.

Las expresiones regulares son comunes en el desarrollo web y sabía que probablemente podría hacer algo a través de los controles del navegador web, pero no quería tener que requerir algún formulario oculto… Eso simplemente no sería lo ideal. Luego recordé haber hecho algo de JS a través del código cuando quise extraer la URL base de una URL completa que empleaba el uso del control de secuencia de comandos.

Ahora bien, no vi el control de script (msscript.ocx) mencionado en el artículo de Mike, por lo que espero que no sea parte de la desuso de VB Script.

Con este conocimiento en la mano, pude obtener funcionalidades de RegEx sin la necesidad de las bibliotecas mencionadas en el artículo de Mike, ni usar ningún objeto de base de datos para que todo esto sucediera. Este enfoque debería funcionar en cualquier entorno VBA (Access, Excel, Outlook, PowerPoint, Word, etc.).

Validación

Si queremos realizar validaciones RegEx, ahora podemos hacer algo como:

'---------------------------------------------------------------------------------------
' Procedure : JSRegEx_Validate
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Performs RegEx validation via the Script Control ocx instead of the
'               VBScript RegulareExpression.
'               It returns True if it is valid, otherwise it returns False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Micrsoft Script Control X.X
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern      : The RegEx pattern to test the object against
' sPatternFlags : The RegEx pattern flags to use
' vInput        : The Object to validate against the specified pattern
'
' Usage:
' ~~~~~~
' Validate URL
' ? JSRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.google.ca")
'   Returns => True
'
' ? JSRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.googleca")
'   Returns => False
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2024-03-02              Initial Release
'---------------------------------------------------------------------------------------
Public Function JSRegEx_Validate(ByVal sPattern As String, _
                                 ByVal sPatternFlags As String, _
                                 ByVal vInput As Variant) As String
    On Error GoTo Error_Handler
    #Const ScriptControl_EarlyBind = False    'Should normally be in the Module header
    #If ScriptControl_EarlyBind = True Then
        Dim oSC               As MSScriptControl.ScriptControl

        Set oSC = New ScriptControl
    #Else
        Static oSC            As Object

        Set oSC = CreateObject("ScriptControl")
    #End If
    Dim sOutput

    If Not oSC Is Nothing Then
        With oSC
            .Language = "JScript"
'            '? JSRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.google.ca")
'            .AddCode "function regEx_Pattern_Test(myInput) {" & _
'                     "  var regEx_Pattern = " & sPattern & sPatternFlags & ";" & _
'                     "  return regEx_Pattern.test(myInput);" & _
'                     "}"
'            sOutput = .Run("regEx_Pattern_Test", vInput)
            '? JSRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.google.ca")
            .AddCode "function regEx_Pattern_Test(myPattern, myPatternFlags, myInput) {" & _
                     "  var regEx_Pattern = eval(myPattern + myPatternFlags);" & _
                     "  return regEx_Pattern.test(myInput);" & _
                     "}"
            sOutput = .Run("regEx_Pattern_Test", sPattern, sPatternFlags, vInput)
        End With
    End If
    JSRegEx_Validate = sOutput

Error_Handler_Exit:
    On Error Resume Next
    Set oSC = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: JSRegEx_Validate" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl  0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

o

'---------------------------------------------------------------------------------------
' Procedure : JSRegEx_Validate
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Performs RegEx validation via the Script Control ocx instead of the
'               VBScript RegulareExpression.
'               It returns True if it is valid, otherwise it returns False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Micrsoft Script Control X.X
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern      : The RegEx pattern to test the object against
' sPatternFlags : The RegEx pattern flags to use
' vInput        : The Object to validate against the specified pattern
'
' Usage:
' ~~~~~~
' Validate URL
' ? JSRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.google.ca")
'   Returns => True
'
' ? JSRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.googleca")
'   Returns => False
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2024-03-02              Initial Release
'---------------------------------------------------------------------------------------
Public Function JSRegEx_Validate(ByVal sPattern As String, _
                                 ByVal sPatternFlags As String, _
                                 ByVal vInput As Variant) As String
    On Error GoTo Error_Handler
    '#Const ScriptControl_EarlyBind = False    'Should normally be in the Module header
    #If ScriptControl_EarlyBind = True Then
        Dim oSC               As MSScriptControl.ScriptControl

        Set oSC = New ScriptControl
    #Else
        Static oSC            As Object

        Set oSC = CreateObject("ScriptControl")
    #End If
    Dim sOutput

    If Not oSC Is Nothing Then
        With oSC
            .Language = "JScript"
            '? JSRegEx_Validate("^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$", "igm", "www.google.ca")
            .AddCode "function regEx_Pattern_Test(myPattern, myPatternFlags, myInput) {" & _
                     "  var regEx_Pattern = new RegExp(myPattern, myPatternFlags);" & _
                     "  return regEx_Pattern.test(myInput);" & _
                     "}"
            sOutput = .Run("regEx_Pattern_Test", sPattern, sPatternFlags, vInput)
        End With
    End If
    JSRegEx_Validate = sOutput

Error_Handler_Exit:
    On Error Resume Next
    Set oSC = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: JSRegEx_Validate" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl  0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Validar una URL

Podrías utilizar lo anterior haciendo:

? JSRegEx_Validate("^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$", "igm", "www.google.ca")
     which will return True
? JSRegEx_Validate("^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$", "igm", "www.googleca")
     which will return False

También podríamos crear una función auxiliar de contenedor para varias pruebas comunes: URL, correo electrónico, teléfono, etc., para simplificar la codificación y el uso. Por ejemplo, para validar URL, podríamos hacer lo siguiente:

Public Function JSRegEx_Validate_URL(ByVal sURL As Variant) As Boolean
    Const sPattern = "/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/"
    Const sPatternFlags = "igm"
    JSRegEx_Validate_URL = JSRegEx_Validate(sPattern, sPatternFlags, sURL)
End Function

y luego la validación se vuelve mucho más sencilla y solo requiere:

? JSRegEx_Validate_URL("www.google.ca") 'Returns True
? JSRegEx_Validate_URL("www.googleca") 'Returns False

Extrayendo coincidencias

De manera similar a lo anterior, podemos desarrollar fácilmente una función RegEx Match universal que utilice el ocx de control de secuencia de comandos para buscar y extraer segmentos de una cadena más grande que coincidan con un patrón específico. Por lo tanto, no solo se prueba si se cumple, sino que se extrae el segmento coincidente.

Podemos hacer algo como:

'---------------------------------------------------------------------------------------
' Procedure : JSRegEx_Matches
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Uses Script Control ocx RegEx to extract the matches of the defined
'               pattern.
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Micrsoft Script Control X.X
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern      : The RegEx pattern to test the object against and extract macthes of
' sPatternFlags : The RegEx pattern flags to use
' vInput        : The Object to validate against the specified pattern
' sDelimiter    : Delimiter to be used to separate the matches in the returned string
'                   If omitted , is used
'
' Usage:
' ~~~~~~
' Extract E-mail addresses from a string
' ? JSRegEx_Matches("((a-zA-ZF0-9\u00C0-\u017F._-)+@(a-zA-Z0-9\u00C0-\u017F._-)+\.(a-zA-Z0-9\u00C0-\u017F_-)+)", _
'                   "gmi", _
'                   "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
'       Returns => dani@ccc.com, marth@marth.ru, carl@hotmail.com
'
' ? JSRegEx_Matches("((a-zA-ZF0-9\u00C0-\u017F._-)+@(a-zA-Z0-9\u00C0-\u017F._-)+\.(a-zA-Z0-9\u00C0-\u017F_-)+)", _
'                   "gmi", _
'                   "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .", _
'                   "~")
'       Returns => dani@ccc.com~marth@marth.ru~carl@hotmail.com
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2024-03-02              Initial Release
'---------------------------------------------------------------------------------------
Public Function JSRegEx_Matches(ByVal sPattern As String, _
                                ByVal sPatternFlags As String, _
                                ByVal vInput As Variant, _
                                Optional sDelimiter As String = ", ") As String
    On Error GoTo Error_Handler
    '#Const ScriptControl_EarlyBind = False    'Should normally be in the Module header
    #If ScriptControl_EarlyBind = True Then
        Dim oSC               As MSScriptControl.ScriptControl

        Set oSC = New ScriptControl
    #Else
        Static oSC            As Object

        Set oSC = CreateObject("ScriptControl")
    #End If
    Dim sScript               As String
    Dim sOutput               As String

    If Not oSC Is Nothing Then
        With oSC
            .Language = "JScript"
            sScript = "function patternTest(myPattern, myPatternFlags, myInput, myDelimiter) {" & _
                      "  var reg = new RegExp(myPattern, myPatternFlags);" & _
                      "  var output="";" & _
                      "  var matches = myInput.match(reg);" & _
                      "  if(matches && matches.length != 0){" & _
                      "     for (var i = 0; i  0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Extracción de direcciones de correo electrónico

A continuación se muestra un ejemplo sencillo de cómo se podría utilizar esto para extraer todas las direcciones de correo electrónico de una cadena.

? JSRegEx_Matches("((a-zA-ZF0-9\u00C0-\u017F._-)+@(a-zA-Z0-9\u00C0-\u017F._-)+\.(a-zA-Z0-9\u00C0-\u017F_-)+)", _
                    "gmi", _
                    "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")

que luego volvería

dani@ccc.com, marth@marth.ru, carl@hotmail.com

Al igual que en la sección anterior, podríamos simplificar el uso frecuente creando funciones auxiliares de envoltura simples como:

Public Function JSRegEx_Extract_Emails(ByVal sInput As String) As String
    Const sPattern = "((a-zA-ZF0-9\u00C0-\u017F._-)+@(a-zA-Z0-9\u00C0-\u017F._-)+\.(a-zA-Z0-9\u00C0-\u017F_-)+)"
    Const sPatternFlags = "igm"
    Const sDelim = "||"
    JSRegEx_Extract_Emails = JSRegEx_Matches(sPattern, sPatternFlags, sInput, sDelim)
End Function

y que llamaríamos haciendo:

?JSRegEx_Extract_Emails("some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
      which returns => dani@ccc.com||marth@marth.ru||carl@hotmail.com

O podríamos en cambio crear un contenedor como:

Public Function JSRegEx_Extract_Emails(ByVal sInput As String) As Variant
    Const sPattern = "((a-zA-ZF0-9\u00C0-\u017F._-)+@(a-zA-Z0-9\u00C0-\u017F._-)+\.(a-zA-Z0-9\u00C0-\u017F_-)+)"
    Const sPatternFlags = "igm"
    Const sDelim = "||"
    JSRegEx_Extract_Emails = Split(JSRegEx_Matches(sPattern, sPatternFlags, sInput, sDelim), sDelim)
End Function

que podríamos utilizar así:

Sub JSRegEx_Extract_Emails2_Test()
    Dim aEmails               As Variant
    Dim i                     As Long
    Const myString = "some text with dani@ccc.com and marth@marth.ru. carl@hotmail.com Some more text ."

    aEmails = JSRegEx_Extract_Emails(myString)
    For i = 0 To UBound(aEmails)
        Debug.Print aEmails(i)
    Next i
End Sub

que luego daría como resultado

dani@ccc.com
marth@marth.ru
carl@hotmail.com

Notas sobre el uso del control de script

Mientras desarrollaba mi código y lo probaba, descubrí un par de cosas que debes tener en cuenta si deseas realizar algún desarrollo utilizando el Control de Script, en particular:

  • JScript no acepta // para comentarios en línea. Debemos utilizar la sintaxis /* */.
  • JScript no admite ‘let’, ‘const’, etc. para declarar variables. ¡Por eso, use ‘var’ para prácticamente todo!

Básicamente, la versión de JS es antigua y no es compatible con ninguna de las sintaxis más nuevas. Por lo tanto, mantén las cosas lo más simples posible, incluso si eso infringe lo que hoy consideraríamos una práctica recomendada. De lo contrario, obtendrás todo tipo de errores muy extraños que realmente no indican la verdadera naturaleza del problema con tu código, errores como:

Error 1004 – Se esperaba ‘;’

Error 1009 – Se esperaba ‘}’

Si está interesado en la codificación de control de scripts con JScript, es posible que desee consultar:

Una técnica alternativa: utilizar la biblioteca de objetos HTML de Microsoft (MSHTML)

También quería demostrar rápidamente que incluso podríamos utilizar la biblioteca MSHTML. A continuación, se muestra un ejemplo de una función de validación que utiliza MSHTML.

'---------------------------------------------------------------------------------------
' Procedure : MSHTMLRegEx_Validate
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Performs RegEx validation via the Microsoft HTML Object Library instead of
'               the VBScript RegulareExpression.
'               It returns True if it is valid, otherwise it returns False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft HTML Object Library (mshtml.tlb)
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern      : The RegEx pattern to test the object against
' sPatternFlags : The RegEx pattern flags to use
' vInput        : The Object to validate against the specified pattern
'
' Usage:
' ~~~~~~
' Validate URL
' ? MSHTMLRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.google.ca")
'   Returns => True
'
' ? MSHTMLRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "www.googleca")
'   Returns => False
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2024-03-02              Initial Release
'---------------------------------------------------------------------------------------
Public Function MSHTMLRegEx_Validate(ByVal sPattern As String, _
                                     ByVal sPatternFlags As String, _
                                     ByVal vInput As Variant) As Boolean
    #Const HF_EarlyBind = True     'True => Early Binding -> Microsoft HTML Object Library (mshtml.tlb) 
                                   'False => Late Binding
    'Typically a Global Module Level Variable
    #If HF_EarlyBind = True Then
        Dim oHTMLFile         As MSHTML.HTMLDocument
        Dim oElem             As MSHTML.HTMLGenericElement
        Dim oLink             As MSHTML.HTMLLinkElement
        Dim oTable            As MSHTML.HTMLTable
        Dim oTr               As MSHTML.HTMLTableRow
        Dim oTd               As MSHTML.HTMLTableCell

        Set oHTMLFile = New MSHTML.HTMLDocument
    #Else
        Dim oHTMLFile         As Object
        Dim oElem             As Object
        Dim oLink             As Object
        Dim oTable            As Object
        Dim oTr               As Object
        Dim oTd               As Object

        Set oHTMLFile = CreateObject("HTMLFile")
    #End If

    oHTMLFile.body.innerHTML = ""    'Clear the default paragraph line

    Set oElem = oHTMLFile.createElement("p")
    Call oElem.setAttribute("id", "result")
    Call oHTMLFile.body.appendChild(oElem)

    Set oElem = oHTMLFile.createElement("script")
    Call oElem.setAttribute("type", "text/javascript")    'Not strictly necessary as this is the default value anyways
    oElem.innerText = "function regEx_Pattern_Test() {" & _
                      "  var regEx_Pattern = " & sPattern & sPatternFlags & ";" & _
                      "  var myInput="" & vInput & "";" & _
                      "  document.getElementById('result').innerText = regEx_Pattern.test(myInput);" & _
                      "}"
    Call oHTMLFile.body.appendChild(oElem)

    Call oHTMLFile.parentWindow.execScript("regEx_Pattern_Test();")
    'Debug.Print oHTMLFile.body.innerHTML ' For debugging
    MSHTMLRegEx_Validate = CBool(oHTMLFile.getElementById("result").innerText)

    Set oHTMLFile = Nothing
End Function

y se podría llamar haciendo:

? MSHTMLRegEx_Validate("/^(https?:\/\/)?((\da-z\.-)+\.(a-z\.){2,6}|(\d\.)+)((\/:?=){1}(\da-z\.-)+)*(\/\?)?$/", "igm", "
     Returns => True

Para los partidos, podríamos utilizar una función como:

'---------------------------------------------------------------------------------------
' Procedure : MSHTMLRegEx_Matches
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Uses Microsoft HTML Object Library to run RegEx to extract the matches of
'               the defined pattern.
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft HTML Object Library (mshtml.tlb)
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern      : The RegEx pattern to test the object against and extract macthes of
' sPatternFlags : The RegEx pattern flags to use
' vInput        : The Object to validate against the specified pattern
' sDelimiter    : Delimiter to be used to separate the matches in the returned string
'                   If omitted , is used
'
' Usage:
' ~~~~~~
' Extract E-mail addresses from a string
' ? MSHTMLRegEx_Matches("(a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-)+@(a-zA-Z0-9-)+(?:\.(a-zA-Z0-9-)+)", _
'                   "gmi", _
'                   "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
'       Returns => dani@ccc.com, marth@marth.ru, carl@hotmail.com
'
' ? MSHTMLRegEx_Matches("(a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-)+@(a-zA-Z0-9-)+(?:\.(a-zA-Z0-9-)+)", _
'                   "gmi", _
'                   "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .", _
'                   "~")
'       Returns => dani@ccc.com~marth@marth.ru~carl@hotmail.com
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2024-03-21              Initial Release
'---------------------------------------------------------------------------------------
Public Function MSHTMLRegEx_Matches(ByVal sPattern As String, _
                                    ByVal sPatternFlags As String, _
                                    ByVal vInput As Variant, _
                                    Optional sDelimiter As String = ", ") As String
'#Const HF_EarlyBind = True     'True => Early Binding / False => Late Binding
'Typically a Global Module Level Variable
    #If HF_EarlyBind = True Then
        Dim oHTMLFile         As MSHTML.HTMLDocument
        Dim oElem             As MSHTML.HTMLGenericElement
        Dim oLink             As MSHTML.HTMLLinkElement
        Dim oTable            As MSHTML.HTMLTable
        Dim oTr               As MSHTML.HTMLTableRow
        Dim oTd               As MSHTML.HTMLTableCell

        Set oHTMLFile = New MSHTML.HTMLDocument
    #Else
        Dim oHTMLFile         As Object
        Dim oElem             As Object
        Dim oLink             As Object
        Dim oTable            As Object
        Dim oTr               As Object
        Dim oTd               As Object

        Set oHTMLFile = CreateObject("HTMLFile")
    #End If

    oHTMLFile.body.innerHTML = ""    'Clear the default 

line Set oElem = oHTMLFile.createElement("p") Call oElem.setAttribute("id", "result") Call oHTMLFile.body.appendChild(oElem) Set oElem = oHTMLFile.createElement("script") Call oElem.setAttribute("type", "text/javascript") 'Not strictly necessary as this is the default value anyways '*** Don't use vbcrlf ... as it will erroneously place
tags in your code! oElem.innerText = "function patternTest() {" & _ " var myPattern = '" & sPattern & "';" & _ " var myPatternFlags="" & sPatternFlags & "";" & _ " var myInput="" & vInput & "";" & _ " var myDelimiter="" & sDelimiter & "";" & _ " var reg = new RegExp(myPattern, myPatternFlags);" & _ " var output="";" & _ " var matches = myInput.match(reg);" & _ " if(matches && matches.length != 0){" & _ " for (var i = 0; i

which would could use by doing:

MSHTMLRegEx_Matches("(a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-)+@(a-zA-Z0-9-)+(?:\.(a-zA-Z0-9-)+)", "gmi", "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")

y que devolvería:

dani@ccc.com, marth@marth.ru, carl@hotmail.com

Conclusión

Como puede ver, podemos tener una solución relativamente sencilla para la pérdida de VBScript cuando se trata de RegEx en VBA.

Debo destacar el beneficio de usar la variable de objeto autorreparadora (SHOV) con cualquier procedimiento repetitivo. Por lo tanto, es probable que todos los procedimientos anteriores se beneficien de la implementación de SHOV. Si aún no está familiarizado con SHOV, revise el siguiente artículo para obtener más información:

¡Sólo espero que msscript.ocx no sea parte de vbscript!

Además, siempre existe la posibilidad de ejecutar dicho código a través de los controles del navegador web o alguna otra biblioteca web. Incluso podríamos recurrir a la automatización de RegEx a través de PowerShell, pero no sería mi primera opción porque la automatización de PowerShell conlleva un impacto en el rendimiento. Por lo tanto, en el peor de los casos, todavía existen al menos otras posibles soluciones alternativas que podemos explorar.

Se puede hacer más con este enfoque, pero ese será un artículo para otro momento.

Algunos recursos adicionales para explorar

Historial de la página

FechaResumen de Cambios
18-03-2024Versión inicial
21-03-2024Se agregó la función de muestra MSHTMLRegEx_Matches a la sección Enfoque alternativo

Publicaciones relacionadas

Deja una respuesta

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

Botón volver arriba