User Tools

Site Tools


vba:functions:print_r:index

[VBA] print_r()

print_r() zeigt Informationen über eine Variable in menschenlesbarer Form an.1)

Version 2.17.1 19.02.2020

In VBA vermisse ich eine Möglichkeit, die Inhalte von Arrays, Dictionaries etc zu Debugzwecken sinnvoll auszugeben. Klar, es gibt im Debug-Mode den Watch. Doch jedes mal alles durchzuklicken ist umständlich. Bei Dictionaries versagt der Watch total und man sieht immer nur den Key des nächsten Levels.
Des weiteren wünschte ich mir eine lesbare Ausgabe im Debug-Fenster um das Resultat in Foren oder dieses Wiki kopieren zu können. Unter PHP gibt es da die Befehle var_dump() und print_r(). Diese dienten mir als Vorlage zu dieser VBA-Funktion print_r().

Was bietet print_r()

Die folgenden Vorteile hat print_r() gegenüber dem mitgelieferten debug.print:

  • Kontrolle über Führende oder folgende Leerzeichen: Da Strings in '' ausgegeben werden, sieht man recht klar, ob da noch Leerzeichen dabei sind
  • Anzeige der Nicht anzeigbaren Zeichen als Unicode
  • Ausgabe von Verschachtelungen (Array, Collection, Dictionary)
  • Ausgabe von Resultaten aus RegExp (MatchCollection, Match)
  • Ausgabe von allen lesbareren Properties aus Objekten von Klassenmodulen
  • Bei native Datatypen erfolgt die Typenausgabe über den Varaibelentyp, nicht über isNumber() etc.

Was print_r() nicht kann

Leider konnte ich die folgenden Punkte noch nicht umsetzen:

  • User Defined Types (UDT). Da diese mit Late-Bindung erstellt werden, können sie nicht einfach so in ein Variant übergeben werden. Ergo geht auch print_r() mit ihnen nicht

Aliase

Es gibt neben der eigentlichen Funktion noch fast analoge, bei denen ggf die Paramter anderst vordefiniert sind

  • print_r() Die eigentliche Funktion
  • d() Ein Alias zu print_r() mit denselben Paramtern. Ist praktisch im Debugfenster, da er so kurz ist
  • c() Gibt das Resultat in einer Form zurück, die gleich wieder für weitere Zwecke verwendet werden kann
  • print_l Wendet bei meinen bekannten ListenObjekten den Print_r nur auf die Liste an, nicht auf das ganze Objekt
  • print_rm() Wendet print_r() auf mehrere Elemente an
  • dRx() Spezialfall zum vereinfachten Testen von RegEx Patterns
  • analyze() Alias um ohne Paramter einen Rückgabewert zu erstellen. Praktisch in Abfragen

Anwendungsbeispiele

Ich beginne mit den Beispielen, da diese recht gut aufzeigen, wass die Funktion alles kann

Einfache Beispiele

Einfach einige Beispiele die ich im Direktfenster ausgeführt habe

print_r 123.4
<Double> 123.4
 
print_r 12345
<Integer> 12345
 
print_r "Hallo Welt "
<String> 'Hallo Welt '
 
print_r NULL
<Null> 
 
Print_r split("a b v")
<String()> (
    [0] => <String> 'a'
    [1] => <String> 'b'
    [2] => <String> 'v'
)

Vergleich zwischen debug.print, d(), c() und print_l

'Text mit leerzeichen am Schluss. Das ist bei debug.print nichterkennbar
? "Hallo Welt   "
Hallo Welt   
'Beim print_r() hingegen schon
d "Hallo Welt   "
<String> 'Hallo Welt   '
 
'Und dasselbe mit nicht angezeigten Spezialteichen
? "Hallo Welt" & chr(1)
Hallo Welt
d "Hallo Welt" & chr(1)
<String> 'Hallo Welt\u0001'
 
'Spezielle Leerzeichen (Zeilenumbruch, Tabulator)
? "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?"
Wie geht es dir?
Hallo   Welt
 
'Normalsettingsfür print_f
d "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?"
<String> 'Hallo\tWelt\r\nWie geht es dir?'
 
'Ohne das spezelle Darstellen der Spaces
d "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?", -prEscapeSpaces
<String> 'Hallo\u0009Welt\u000D\u000AWie geht es dir?'
 
'Ohne Unicodeübersetzung von Sonderzeichen
d "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?", -prEscapeSpaces-prEscapeNotPrintableChars
<String> 'Hallo Welt
Wie geht es dir?'
 
'Ganz ohne Paramters
d "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?", prNoParams
Hallo   Welt
Wie geht es dir?

Für diesen Test verwende ich die Cast-Funktionen von [VBA] JSON

'Normale Ausgabe im ImmadiateWindow
? obj2json(array(1, "a", "c""d", array(4, 5)))
[1,"a","c\"d",[4,5]]
 
'Normaler print_r(). In dem Fall über den Alias d()
d obj2json(array(1, "a", "c""d", array(4, 5)))
<String> '[1,"a","c\"d",[4,5]]'
 
'Und zur Codeweiterverarbeitung geeignet
c obj2json(array(1, "a", "c""d", array(4, 5)))
"[1,""a"",""c\""d"",[4,5]]"
 
'Ausgabe direkt kopiert zum weitertesten
d json2obj("[1,""a"",""c\""d"",[4,5]]", jrtArray)
<Variant()>  (
    [0] => <Byte> 1
    [1] => <String> 'a'
    [2] => <String> 'c"d'
    [3] => <Variant()>  (
        [0] => <Byte> 4
        [1] => <Byte> 5
    )
)

print_l wird vorerst nur für die folgenden Klassen unterstätzt. Ansonsten funktioniert er wie d(). Und da uch nur, wenn diese Objekt direkt übergeben wird. In einer Verschachtelung nicht mehr [VBA] Iterator & [VBA] ListStream

'Objekt initalisieren
Set it = Iterator.instance(array(1,2))
 
'Herkömlich mit d()
d it
<Class Module::Iterator>  (
    [paramDaoValue] => <Boolean> False
    [paramErrorAtEmptyList] => <Boolean> False
    [paramIndexInsteadKey] => <Boolean> False
    [paramListNextNoParamsAsToNext] => <Boolean> True
    [paramNothingAsEmptyList] => <Boolean> False
    [isInitialized] => <Boolean> True
    [isEmpty] => <Boolean> False
    [count] => <Long> 2
    [BOF] => <Boolean> True
    [EOF] => <Boolean> False
    [absolutePosition] => <Long> -1
    [index] => <Long> -1
    [key] => <Long> -1
    [source] => <Variant()>  (
        [#0] => <Integer> 1
        [#1] => <Integer> 2
    )
 
)
 
'und so kommt es mit print_l
print_l it
<Variant()>  (
    [#0] => <Integer> 1
    [#1] => <Integer> 2
)

Diverse Verschachtelungen

Hier ein kleines Testbeispiel bei dem diverse verschiedene Typen ineinander verschachtelt sind

Public Sub testPrintR()
    Dim col As New Collection
    Dim dict As New Dictionary
    Dim arr(1)   As Variant
    Dim FSO As New FileSystemObject
 
    'Ein Array zusammenstellen
    arr(0) = "abc"
    arr(1) = 1345.43
 
    'Ein Dictionaray zusammenstellen
    Call dict.add("a", "AAA")
    Call dict.add("int", 123)
    Call dict.add("dbl", 12.3)
    Call dict.add("arr", arr)
    Call dict.add("Null-Wert", Null)
    Call dict.add("Empty-Wert", Empty)
    Call dict.add("FSO", FSO)
 
    'Und eine Collection
    Call col.add(Now())
    Call col.add(dict)
 
    print_r col
 
    Set FSO = Nothing
    Set dict = Nothing
    Set col = Nothing
End Sub

Und das ist die Ausgabe im Direkt-Fenster

<Collection> (
    [1] => <Date> 29.10.2013 09:51:45
    [2] => <Dictionary> (
        [a] => <String> 'AAA'
        [int] => <Integer> 123
        [dbl] => <Double> 12.3
        [arr] => <Variant()> (
            [0] => <String> 'abc'
            [1] => <Double> 1345.43
        )
        [Null-Wert] => <Null> 
        [Empty-Wert] => <Empty> 
        [FSO] => <FileSystemObject> 
    )
)

Resultate von RegExp.execute() auswerten

Die ganzen MatchCollection, Match und Submatches können einfach über print_r ausgegeben werden

Public Sub testPrintRRegExp()
    Dim rx As New regExp
    rx.Pattern = "([a-c])#(\d+)"
    rx.Global = True
 
    'Das Objekt rx analysieren
    print_r rx
    'Und die Treffer
    print_r rx.execute("Wir haben die Schlüssel a#1, b#12 und c#9 zur Auswahl")
 
    Set rx = Nothing
End Sub
<IRegExp2>  (
    [Pattern] => <String> '([a-c])#(\d+)'
    [Global] => <Boolean> True
    [IgnoreCase] => <Boolean> False
    [Multiline] => <Boolean> False
)
<IMatchCollection2>  (
    [0] => <IMatch2>  (
        [Match] => <String> 'a#1'
        [FirstIndex] => <Long> 24
        [SubMatches] => <ISubMatches>  (
            [0] => <String> 'a'
            [1] => <String> '1'
        )
    )
    [1] => <IMatch2>  (
        [Match] => <String> 'b#12'
        [FirstIndex] => <Long> 29
        [SubMatches] => <ISubMatches>  (
            [0] => <String> 'b'
            [1] => <String> '12'
        )
    )
    [2] => <IMatch2>  (
        [Match] => <String> 'c#9'
        [FirstIndex] => <Long> 38
        [SubMatches] => <ISubMatches>  (
            [0] => <String> 'c'
            [1] => <String> '9'
        )
    )
)

Merhdimensionaler Array

Dim a(1, 1, 1) As Integer
a(0, 0, 0) = 0
a(0, 0, 1) = 1
a(0, 1, 0) = 2
a(0, 1, 1) = 3
a(1, 0, 0) = 4
a(1, 0, 1) = 5
a(1, 1, 0) = 6
a(1, 1, 1) = 7
print_r a

ergibt

<Integer()> (
    [0.0.0] => <Integer> 0
    [0.0.1] => <Integer> 1
    [0.1.0] => <Integer> 2
    [0.1.1] => <Integer> 3
    [1.0.0] => <Integer> 4
    [1.0.1] => <Integer> 5
    [1.1.0] => <Integer> 6
    [1.1.1] => <Integer> 7
)

Objekt aus einem Klassenmodul

Als erstes mal ein kleines Klassenmodul mit 3 Properties. Nachname und Vorname als Get und Set. Dazu noch Name nur als Get, da dieser aus Vor- und Nachname zusammengesetzt ist.

TestClass.cls
Option Compare Database
 
Private pVorname    As String
Private pNachname   As String
 
Public Property Get nachname() As String
    nachname = pNachname
End Property
 
Public Property Let nachname(ByVal vNewValue As String)
    pNachname = vNewValue
End Property
 
Public Property Get vorname() As String
    vorname = pVorname
End Property
 
Public Property Let vorname(ByVal vNewValue As String)
    pVorname = vNewValue
End Property
 
Public Property Get name() As String
    name = vorname & " " & nachname
End Property

Dann mal ein kleines Testscript

Dim yaslaw As New TestClass
 
yaslaw.vorname = "Yaslaw"
yaslaw.nachname = "Kowalejeff"
 
print_r yaslaw

Und so sieht es dann aus

<Class Module::TEstClass> (
    [nachname] => <String> 'Kowalejeff'
    [vorname] => <String> 'Yaslaw'
    [name] => <String> 'Yaslaw Kowalejeff'
)

Beispiele zur Ausgabe

Ausgabe in Direktfenster

Das ist die Standarteinstellung

print_r 123
 
print_r 123, prConsole

Ausgabe als Rückgabewert

Dim txt as String
txt = print_r(123, prReturn)

Ausgabe in den PC-Zwischenspeicher + Konsole

print_r 123, prConsole + prClipboard

mehrere ausgeben mitteles print_rm()

print_rm "Hallo Welt", strToDate("01022013", "mmddyyyy"), split("1,2,3", ",")
<String> 'Hallo Welt'
<Date> 02.01.2013
<String()> (
    [0] => <String> '1'
    [1] => <String> '2'
    [2] => <String> '3'
)

Beispiele zu den weiteren Parametern enuPrintRParams

'Standart mit Tabulator und Nicht druckbaren Leerzeichen
print_r chr(29) & " A " & vbTab & " B " & chr(1) & " C"
<String> '\u001D A \t B \u0001 C'
 
'Dito, aber ohne ersetzen des Tabulators
print_r chr(29) & " A " & vbTab & " B " & chr(1) & " C", -prEscapeSpaces
<String> '\u001D A   B \u0001 C'
 
'Ausgabe von Zahlen und Texten ohne Anfühurnungszeichen
print_r Array("a", "b" & vbTab & "c", 101), -prStringSingleQuotes
<Variant()>  (
    [0] => <String> a
    [1] => <String> b\tc
    [2] => <Integer> 101
)
 
'Ditp, mit Dippoelten Anführungszeichen
print_r Array("a", "b" & vbTab & "c", 101), prParamsDefault -prStringSingleQuotes + prStringDoubleQuotes
<Variant()>  (
    [0] => <String> "a"
    [1] => <String> "b\tc"
    [2] => <Integer> 101
)
 
'Ausgabe ohne weitere formatierung
print_r Array("a", "b" & vbTab & "c", 101), prNoParams
 (
    [0] => a
    [1] => b    c
    [2] => 101
)
 
'Ausgabe eines Strings mit doppelten Anführungszeichen zur Weiterverarbeitung
print_r "abc(""Hallo Welt"& chr(34) & ")", prEscapeDoubleQuotes+prStringDoubleQuotes
"abc(""Hallo Welt"")"

Beispiel zu dRx()

Die Patterns sind nach [VBA] RegExp testen im Direktfenster zusammengestellt.

Nur Pattern. Das RegExp Objekt wird ausgegeben

dRx "^(\d{3}).*\.(\w{3,})$"
<IRegExp2>  (
    [Pattern] => <String> '^(\d{3}).*\.(\w{3,})$'
    [Global] => <Boolean> False
    [IgnoreCase] => <Boolean> False
    [Multiline] => <Boolean> False
)

Mit einem Text zum Parsen wird automatisch geparst un dann ausgewertet

dRx "^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx"
<IMatchCollection2>  (
    [0] => <IMatch2>  (
        [Match] => <String> '123 Mein test.xlsx'
        [FirstIndex] => <Long> 0
        [SubMatches] => <ISubMatches>  (
            [0] => <String> '123'
            [1] => <String> 'xlsx'
        )
    )
)

Mit einem Replace-Text wird dies ebenfalls ausgewertet

dRx "^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx", "OUT_$1.$2"
<String> 'OUT_123.xlsx'

Natürlich kann man auch die normalen print_r() Paramters mitgeben

dRx "^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx", "OUT_$1.$2", prStringDoubleQuotes
"OUT_123.xlsx"
 
dRx "^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx", "OUT_$1.$2", prNoParams
OUT_123.xlsx
 
? dRx("^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx", "OUT_$1.$2", prNoParams, prReturn)
OUT_123.xlsx

Beispiel zu analyze()

Die Funktion gibt mit den Standardeinstellungen den Text zurück. Das ist praktisch für den Einsatz in einer Abfrage/SQL.

Abfrageeditor

DebugMyField: analyze([MY_FIELD])

Im SQL

analyze([MY_FIELD]) AS DebugMyField
SELECT
	*,
	analyze([MY_FIELD]) AS DebugMyField
FROM test_analyze;
 
DebugMyField      | ID | MY_FIELD | DESCRIPTION                                   
------------------|----|----------|-----------------------------------------------
<String> 'a'      | 1  | a        | Einfacher String                              
<String> 'a '     | 2  | a        | String mit folgenden Leerzeichen              
<Null>            | 3  |          |                                               
<String> '    '   | 4  |          | Ein String der nur aus Leerzeichen besteht    
<String> '\n\r'   | 5  |          | Ein Zeilenumbruch (Wagenrücklauf & neue Zeile)
                       |          |
<String> '\u0001' | 6  |          | Das Zeichen mit dem ASCII-Code 1           

Definitionen

Funktionen print_r(), print_rm(), d(), c()

Definitionen

Public Function print_r( _
        ByRef iExpression As Variant, _
        Optional ByVal iParams As enuPrintRParams = prParamsDefault, _
        Optional ByVal iReturn As enuPrintROutputMethode = prConsole _
) As String

Definition

Parameter-Liste

Diverse Parameter zur Formatierung der Ausgabe.

  • iExpression Variable die analysiert werden soll
  • iParams Diverse Parameter zur Formatierung der Ausgabe. Die verschiedenen Parameter lassen sich kombinieren. Es gibt Parameters, die in Kombination keinen Sinn machen. zB: prStringSingleQuotes+prStringDoubleQuotes. In diesem Fall wird der kleinere Wert genommen. Also nur prStringSingleQuotes. Für die genaue Funktion der Parameter, siehe Kommentar beim Enum enuPrintRParams Wenn man vom Standart einfach etwas weghabenwill, dann kann man das auch als Negativ-Parameter mitgeben
  • iReturn (Optional) Art der Rückgabe: Standart ist das Direktfenster. Alternativ kann man auch als Rückgabewert der Funktion oder in den Zwieschnepseicher des PCs schreiben. Grad letzteres ist bei gösseren Verschachtelungen Empfehlenswert, da das Direktfenser eine Beschränkte Anzahl Zeilen hat. Die Auswahlen lassen sich auch kombinieren: prConsole+prClipboard.

Enumerator

enuPrintRParams
  • prNoParams Kein Parameter hat Gültigkeit
  • prShowType Zeigt den Datentype am Anfangan in <> an
  • prStringSingleQuotes Setzt Strings in einfache Anführungszeichen. Hat vor prStringDoubleQuotes vorrang.
  • prStringDoubleQuotes Setzt Strings in doppetle Anführungszeichen
  • prEscapeDoubleQuotes Setzt wandelt “ innerhalb eines Textes zu ”“. Somit ist der String direkt weiterverwendbar
  • prEscapeSpaces Tab, Cr, Lf als /t, /r, /n zurückgeben
  • prEscapeNotPrintablaChars Nicht Druckbare Zeichen als Unicode ausgeben
  • prParamsDefault = prShowType + prStringSingleQuotes + prEscapeSpaces + prEscapeNotPrintablaChars
enuPrintROutputMethode

Auswahl was mir der Analyse geschehen soll. Die Werte lassen sich mit + kombinieren

  • prConsole(1) Die Ausgabe wird ins Direktfenster geschrieben
  • prReturn(2) Die Ausgabe wird als Funktionsrückgabewert zurückgegeben
  • prClipboard(4) Die Ausgabe wird in den Zwieschenspeicher geschrieben
  • prMsgBox(8) Die Ausgabe wird als Popup ausgegeben

Rückgabewerte

Falls ein array übergeben wurde, werden die Werte in einem Format angezeigt, das sowohl die Schlüssel als auch die Elemente darstellt. Für Variablen vom Typ Collection und Dictionary gilt das Gleiche. Für die restlichen Objekte wird nur der Klassenname angezeigt.
Für alle einfachen Variablen wird der Type und der Inhalt angezeigt

Ruft print_r mit dem Parameter prConsole für eine ganze Liste von Variablen auf

Definitionen

Public Sub print_rm(ParamArray iExpressions() As Variant)
Parameter-Liste
  • iExpressions Liste der Variablen die analysiert werden soll

d()

d() ist ein Alais zu print_r() und steht für [D]ebug

Definition

Beschreibung der Komponenten, siehe print_r()

Public Function d( _
        ByRef iExpression As Variant, _
        Optional ByVal iParams As enuPrintRParams = prParamsDefault, _
        Optional ByVal iReturn As enuPrintROutputMethode = prConsole _
) As String

c()

Wie print_r mit den den Parametern prEscapeDoubleQuotes + prStringDoubleQuotes, sowieprReturn + prConsole \ c steht für [C]ode
Eignet sich, wenn man das Resultat später weiterverwenden will

'/**
' * Wie print_r mit den den Parametern prEscapeDoubleQuotes + prStringDoubleQuotes, sowieprReturn + prConsole
' * c für [C]ode
' * Eignet sich, wenn man das Resultat später weiterverwenden will
' * @example: c "a" & chr(34) & "b" -> "a""b"
' * @param  Variant     Zu prüfende Variable
' * @return Variant     Nichts oder die Analyse als String
Public Function c(ByRef iExpression As Variant) As String

dRx()

Ein Spezialfall zum Testen von RegEx Patterns
Die Patterns sind nach [VBA] RegExp testen im Direktfenster zusammengestellt.

'/**
' * Spezialfall zum einfach RegExp testen zu können
' * [D]ebug[R]eg[E]xp
' * @param  String      Pattern
' * @param  String      zu untersuchneder Text. Wenn Leer, dann wird der RegExp analysiert
' * @param  String      Replace. Wenn leer, dann wird der Exeute analysiert, ansonsten das Replaceergebnis
' * @param  enuPrintRParams
' * @param  enuPrintROutputMethode
' * @return Variant     Nichts oder die Analyse
' */
Public Function dRx( _
        ByVal iPattern As String, _
        Optional ByVal iText As String, _
        Optional ByVal iReplace As String, _
        Optional ByVal iParams As enuPrintRParams = prParamsDefault, _
        Optional ByVal iReturn As enuPrintROutputMethode = prConsole _
)

analyze()

'/**
' * Eine ganz einfache Form, die den Wert zurückgibt. Ist Praktisch um in einem SQL angewendet zu werden
' * @param  Variant     Zu prüfende Variable
' * @return Variant     die Analyse als String
' */
Public Function analyze(ByRef iExpression As Variant) As String
    analyze = print_r(iExpression, prParamsDefault, prReturn)
End Function

Ausgewertete Variablen/Obejekte

Im Moment kann die Funktion die folgenden Objekte/Variablen auswerten

  • Native Variablen: Strings, Datum, Integer, Long, Double, Decimal, Null, Empty
  • Ein- und Mehrdimensionale Arrays (auch Verschachtelte)
  • Collection
  • Dictionaries
  • ErrObject
  • Objekte aus Klassenmodulen (beschränkt auf Public Property Get)
  • RegExp (Referenz auf 'Microsoft VBScript Regular Expressions 5.5' notwendig)
  • Dao.Recordset (Anzahl Einträge sind begrenzt)
  • TempVars (Nur bei Access)
  • Properties (Nur bei Access)
  • [VBA] Iterator (Muss nicht installiert sein)

Bei allen restlichen Objekten wird nur der Klassenname angegeben

Offene Auswertungen

Die Folgenden will ich demnächst mal noch implementieren.

  • Excel.Range
  • User Defined Type UDT (noch keine Ahnung wie ich die hinkriegen soll)

Selber erweitern

Diese Funktion kann problemlos für die Analyse von weiteren Klassenobjekten ausgebaut werden. Dazu einfach eine private Funktion prMyClass schreiben (Beispiele hat es genügend im Code) und entsprechend in der Funktion parseObj() einen Abzweiger zu dieser Funktion hinzufügen.

Code

vba/functions/print_r/index.txt · Last modified: 23.05.2022 09:23:05 by yaslaw