Print Using



  • Print Using
    di Vecchio Frac data: 02/08/2016 20:18:10

    Qualche volta mi farebbe comodo il vecchio Print Using... forse solo Thyke se lo ricorda ^_^
    In linguaggi più evoluti (python, C#, C++, Java, ...) c'è la possibilità di formattare le stringhe con dei placeholders e dei modificatori ma in VBA no
    Allora stasera visto che avevo una mezz'ora ho buttato giù un'idea e la voglio condividere qui... ho prima cercato se qualcuno aveva fatto qualcosa di simile ma onestamente non ho trovato niente per VBA. Così l'ho bozzata da solo e ve la propino propongo :)
    Niente di stratosferico... e poi per esempio non formatta nè numeri nè date, ma qualcosa riesce a fare.
    In pratica si inseriscono nella stringa dei segnaposto (placeholders) alfanumerici racchiusi tra graffe e poi si passano alla stringa i valori da visualizzare nel posto corretto, nell'ordine in cui si vuole che compaiano (no, non è implementata la funzione dei placeholders per nome).
    Insomma provatela e poi ditemi le correzioni da fare... :)
     
    Option Explicit
    
    'simulare la funzione format di python in VBA
    'VF 2016
    'consente di utilizzare placeholders {0}..{n}
    'in una stringa di formato passando poi gli argomenti da stampare
    'i placeholders sono indici alfanumerici e
    'devono essere coerenti con il numero di variabili
    'passate nel secondo argomento
    'es. print_using("{0} <{1}> *{2}", "a", "b", "c") ---> "a  *c"
    'es. print_using("{0} <{1}> *{0}", "a", "b") ---> "a  *a"
    'es. print_using("Ecco qui {0} e {1}. Questo non conta: {} ma questi sì {pippo} e {0abc}.", "a", "b", "c", "d", "e")   -> Error
    
    Function print_using(stri As String, ParamArray vars() As Variant) As String
    Dim itm As Variant, i As Integer, v() As Variant, m As String
    Dim regex As Object, placeholders As Object, placeholder As Object
    Dim unique_placeholders As Collection
    
        Set regex = CreateObject("VBScript.RegExp")
        With regex
            .Global = True
            .MultiLine = False
            .IgnoreCase = True
            .Pattern = "({w+})"    'match '{0}' but not '{}'
        End With
    
        For Each itm In vars
            ReDim Preserve v(i) As Variant
            v(i) = itm
            i = i + 1
        Next
        
        Set placeholders = regex.Execute(stri)
        
        On Error GoTo coll_err_handler
        Set unique_placeholders = New Collection
        For Each placeholder In placeholders
            unique_placeholders.Add placeholder, placeholder
        Next
            
        If i <> unique_placeholders.Count Then        'nb collection base is 0
            print_using = "Inconsistent number of variables and number of uniques placeholders."
            Exit Function
        End If
        
        i = 0
        m = Trim(stri)
        For Each placeholder In unique_placeholders
            m = Replace(m, placeholder.Value, v(i))
            i = i + 1
        Next
        
        print_using = m
        Exit Function
    
    coll_err_handler:
        'intercept adding duplicates
        If Err.Number = 457 Then
            Err.Clear
            Resume Next
        Else
            MsgBox "Error number " & Err.Number & vbCrLf & Err.Description, vbInformation
        End If
    End Function
    






  • di scossa data: 02/08/2016 22:48:33

    cit.: "(no, non è implementata la funzione dei placeholders per nome)"

    Non ho capito cosa intendi con quanto citato, comunque mi sono permesso di "sintetizzare" un po' il codice:
    - l'array v() non serve in quanto è un duplicato di vars() e quindi si può evitare risparmiando il relativo ciclo For ... Next;
    - ho spostato nel ciclo di popolamento della collection l'istruzione di replace dei placeholder in modo da eliminare il relativo ciclo For .. Next
    - ho considerato errato solo indicare più placeholder in stri rispetto a quelli passati in vars() (posso passare un argomento anche se poi non lo utilizzo in stri)
    - ho reso più lineare il flusso del codice modificando la gestione dell'errore in caso di duplicati nella collection e lasciando come unica uscita dalla function la "naturale" fine del codice.




    scossa's web site
    Se tu hai una mela, ed io ho una mela, e ce le scambiamo, allora tu ed io abbiamo sempre una mela per uno. Ma se tu hai un'idea, ed io ho un'idea, e ce le scambiamo, allora abbiamo entrambi due idee. (George Bernard Shaw)

     
    'simulare la funzione format di python in VBA
    'VF 2016
    'consente di utilizzare placeholders {0}..{n}
    'in una stringa di formato passando poi gli argomenti da stampare
    'i placeholders sono indici alfanumerici e
    'devono essere coerenti con il numero di variabili
    'passate nel secondo argomento
    'es. print_using("{0} <{1}> *{2}", "a", "b", "c") ---> "a  *c"
    'es. print_using("{0} <{1}> *{0}", "a", "b") ---> "a  *a"
    'es. print_using("Ecco qui {0} e {1}. Questo non conta: {} ma questi sì {pippo} e {0abc}.", "a", "b", "c", "d", "e")   -> Error
    '
    'variante scossa:
    'eliminato array v() in quanto era un duplicato di vars()
    'spostato nel ciclo di popolamento della collection l'istruzione di replace dei placeholder,
    'eliminando il successivo ciclo
    
    Function print_using(stri As String, ParamArray vars() As Variant) As String
        Dim itm As Variant, i As Integer, m As String
        Dim regex As Object, placeholders As Object, placeholder As Object
        Dim unique_placeholders As Collection
        Const sErr As String = "Inconsistent number of variables and number of uniques placeholders."
    
        Set regex = CreateObject("VBScript.RegExp")
        With regex
            .Global = True
            .MultiLine = False
            .IgnoreCase = True
            .Pattern = "({w+})"    'match '{0}' but not '{}'
        End With
        
        m = Trim(stri)
        
        Set placeholders = regex.Execute(stri)
        Set unique_placeholders = New Collection
        On Error Resume Next
        For Each placeholder In placeholders
            unique_placeholders.Add placeholder, placeholder
            If Err.Number = 0 Then
              m = Replace(m, placeholder.Value, vars(i))
              i = i + 1
            End If
            Err.Clear
        Next
        On Error GoTo coll_err_handler
        If UBound(vars) + 1 < unique_placeholders.Count Then        'nb collection base is 0
            print_using = sErr
            Err.Raise vbObjectError + 513, Description:=sErr
        End If
        
        
        print_using = m
    
    coll_err_handler:
        If Err.Number <> 0 Then
            MsgBox "Error " & Err.Description, vbInformation
        End If
        Set unique_placeholders = Nothing
        Set regex = Nothing
    
    End Function
    



  • di Vecchio Frac data: 03/08/2016 09:35:43

    Ciao scossa e grazie!
    Era solo una bozza infatti e andava affinata. Nella prima stesura ho seguito pedissequamente un 'idea e chiaramente il codice grezzo andava scolpito ulteriormente.
    Mi piacciono le tue correzioni e stasera provvedo ad aggiornare il blogghino citando la fonte :)

    Due cose per precisare:
    cit. "- ho considerato errato solo indicare più placeholder in stri rispetto a quelli passati in vars() (posso passare un argomento anche se poi non lo utilizzo in stri)"
    ---> il comportamento di python cui mi sono ispirato fa quello che dici (se indico un placeholder ma non lo valorizzo, perdo il relativo valore passato in argomento). Peccato che mi sia sfuggito :)

    cit. VF "non è implementata la funzione dei placeholders per nome"
    cit. scossa "Non ho capito cosa intendi con quanto citato"
    ---> Intendo dire che in python puoi scrivere print("Ciao sono {nome} {cognome}".format(cognome='Paperino', nome='Paolino')) --> 'Ciao sono Paolino Paperino'
    E strettamente scritta così il mio codice non implementa questa denominazione dei placeholders. Però poi all'atto pratico e pensandoci su è chiaro che il risultato ottenuto nel codice VBA è comunque simile, perchè replace non fa altro che sostituire le occorrenze di quello che trova scritto nelle graffe, così com'è scritto.





  • di scossa data: 03/08/2016 11:28:53

    cit. V.F.: "... provvedo ad aggiornare il blogghino ..."


    mi sono perso qualche cosa?



    scossa's web site
    Se tu hai una mela, ed io ho una mela, e ce le scambiamo, allora tu ed io abbiamo sempre una mela per uno. Ma se tu hai un'idea, ed io ho un'idea, e ce le scambiamo, allora abbiamo entrambi due idee.
    (George Bernard Shaw)



  • di Vecchio Frac data: 03/08/2016 11:54:14

    Bè non lo menziono mai e non faccio pubblicità. E' più che altro un mio deposito personale di appunti e articoli :)





  • di scossa data: 03/08/2016 13:47:18

    cit.: "Bè non lo menziono mai...."





    scossa's web site
    Se tu hai una mela, ed io ho una mela, e ce le scambiamo, allora tu ed io abbiamo sempre una mela per uno. Ma se tu hai un'idea, ed io ho un'idea, e ce le scambiamo, allora abbiamo entrambi due idee.
    (George Bernard Shaw)



  • di Vecchio Frac data: 03/08/2016 13:58:01

    Scossa, non ti perdi niente. Non è certo al tuo livello.