version=2.17.1 vdate=19.02.2020 fname=lib_printr.bas ns=%NAMESPACE% fpath=/vba/functions {{keywords>vba,printr,debug}} {{description>Implementation von print_r aus PHP nach VBA}} ====== [VBA] print_r() ====== //print_r() zeigt Informationen über eine Variable in menschenlesbarer Form an.((Beschreibung zitiert von http://php.net/manual/de/function.print-r.php))// ==Version %%version%% %%vdate%%== {{..%%fname%%|Download %%fname%% (V-%%version%%)}} \\ {{..lib_printR_excel.bas|Download lib_printR_excel.bas (V-%%version%%)}} 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 123.4 print_r 12345 12345 print_r "Hallo Welt " 'Hallo Welt ' print_r NULL Print_r split("a b v") ( [0] => 'a' [1] => 'b' [2] => '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 " 'Hallo Welt ' 'Und dasselbe mit nicht angezeigten Spezialteichen ? "Hallo Welt" & chr(1) Hallo Welt d "Hallo Welt" & chr(1) '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?" 'Hallo\tWelt\r\nWie geht es dir?' 'Ohne das spezelle Darstellen der Spaces d "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?", -prEscapeSpaces 'Hallo\u0009Welt\u000D\u000AWie geht es dir?' 'Ohne Unicodeübersetzung von Sonderzeichen d "Hallo" & vbtab & "Welt" & vbcrlf & "Wie geht es dir?", -prEscapeSpaces-prEscapeNotPrintableChars '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:cast: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))) '[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) ( [0] => 1 [1] => 'a' [2] => 'c"d' [3] => ( [0] => 4 [1] => 5 ) ) === print_l() === 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:classes:iterator:index|]] & [[vba:classes:liststream|]] 'Objekt initalisieren Set it = Iterator.instance(array(1,2)) 'Herkömlich mit d() d it ( [paramDaoValue] => False [paramErrorAtEmptyList] => False [paramIndexInsteadKey] => False [paramListNextNoParamsAsToNext] => True [paramNothingAsEmptyList] => False [isInitialized] => True [isEmpty] => False [count] => 2 [BOF] => True [EOF] => False [absolutePosition] => -1 [index] => -1 [key] => -1 [source] => ( [#0] => 1 [#1] => 2 ) ) 'und so kommt es mit print_l print_l it ( [#0] => 1 [#1] => 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 ( [1] => 29.10.2013 09:51:45 [2] => ( [a] => 'AAA' [int] => 123 [dbl] => 12.3 [arr] => ( [0] => 'abc' [1] => 1345.43 ) [Null-Wert] => [Empty-Wert] => [FSO] => ) ) ==== 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 ( [Pattern] => '([a-c])#(\d+)' [Global] => True [IgnoreCase] => False [Multiline] => False ) ( [0] => ( [Match] => 'a#1' [FirstIndex] => 24 [SubMatches] => ( [0] => 'a' [1] => '1' ) ) [1] => ( [Match] => 'b#12' [FirstIndex] => 29 [SubMatches] => ( [0] => 'b' [1] => '12' ) ) [2] => ( [Match] => 'c#9' [FirstIndex] => 38 [SubMatches] => ( [0] => 'c' [1] => '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 ( [0.0.0] => 0 [0.0.1] => 1 [0.1.0] => 2 [0.1.1] => 3 [1.0.0] => 4 [1.0.1] => 5 [1.1.0] => 6 [1.1.1] => 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. 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 ( [nachname] => 'Kowalejeff' [vorname] => 'Yaslaw' [name] => '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", ",") 'Hallo Welt' 02.01.2013 ( [0] => '1' [1] => '2' [2] => '3' ) ==== Beispiele zu den weiteren Parametern enuPrintRParams ==== 'Standart mit Tabulator und Nicht druckbaren Leerzeichen print_r chr(29) & " A " & vbTab & " B " & chr(1) & " C" '\u001D A \t B \u0001 C' 'Dito, aber ohne ersetzen des Tabulators print_r chr(29) & " A " & vbTab & " B " & chr(1) & " C", -prEscapeSpaces '\u001D A B \u0001 C' 'Ausgabe von Zahlen und Texten ohne Anfühurnungszeichen print_r Array("a", "b" & vbTab & "c", 101), -prStringSingleQuotes ( [0] => a [1] => b\tc [2] => 101 ) 'Ditp, mit Dippoelten Anführungszeichen print_r Array("a", "b" & vbTab & "c", 101), prParamsDefault -prStringSingleQuotes + prStringDoubleQuotes ( [0] => "a" [1] => "b\tc" [2] => 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:tutorials:regexptest|]] zusammengestellt. > Siehe auch [[vba:cast:cregexp|]] Nur Pattern. Das RegExp Objekt wird ausgegeben dRx "^(\d{3}).*\.(\w{3,})$" ( [Pattern] => '^(\d{3}).*\.(\w{3,})$' [Global] => False [IgnoreCase] => False [Multiline] => False ) Mit einem Text zum Parsen wird automatisch geparst un dann ausgewertet dRx "^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx" ( [0] => ( [Match] => '123 Mein test.xlsx' [FirstIndex] => 0 [SubMatches] => ( [0] => '123' [1] => 'xlsx' ) ) ) Mit einem Replace-Text wird dies ebenfalls ausgewertet dRx "^(\d{3}).*\.(\w{3,})$", "123 Mein test.xlsx", "OUT_$1.$2" '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 ------------------|----|----------|----------------------------------------------- 'a' | 1 | a | Einfacher String 'a ' | 2 | a | String mit folgenden Leerzeichen | 3 | | ' ' | 4 | | Ein String der nur aus Leerzeichen besteht '\n\r' | 5 | | Ein Zeilenumbruch (Wagenrücklauf & neue Zeile) | | '\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 ==== print_rm() ==== 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:tutorials:regexptest|]] zusammengestellt. > Siehe auch [[vba:cast:cregexp|]] '/** ' * 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:classes:iterator:index]] (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 ===== {{..%%fname%%|Download %%fname%% (V-%%version%%)}}