This is an old revision of the document!
%tH:%tM:%tS
Gibt eine anhand des Formatierungs-Strings format gebildete Zeichenkette zurück.1)
Download lib_printf.bas (V-2.4.0)
Von PHP bin ich mir die tolle Funktion sprintf() gewohnt. Diese vermisste ich schmerzlich im VBA. Immer alle Strings mit & zusammenzusetzen war einfach hässlich. Auf der Suche nach einer Lösung fand ich eine printf Implementierung auf der Seite PrintF and Related Functions in VB.
Das fand ich am Anfang auch ganz toll. Doch vermisste ich die Reihenfolge-Parameter aus
PHP. Auch die Umsetzung ohne Reguläre Ausdrücke hat mich erstaunt. Darum habe ich mir in
etwa 3 Stunden Arbeit selber sprintf() und vsprintf() aus PHP implementiert.
Später kahm dann noch die Formatierungsregeln etc. dazu.
Hier nun das Resultat.
Erweiterungen findet ihr unter [VBA] PrintF AddOns
Folgende Fehler-Codes sind definiert
Parst einen Pattern mit Werten 5).
Public Function sPrintF( _ ByVal iFormatString As String, _ ParamArray iParams() As Variant _ ) As String
Parse einen String mit den Werten als Array 6).
Public Function vsPrintF( _ ByVal iFormatString As String, _ ByRef iParams() As Variant _ ) As String
Mit dem Property printF_UserDefinedDateFormat können benuzerdefinierte FOrmate hinterlegt werden. Siehe Beispiel benutzerdefinierte_formate
Public Property Get printF_UserDefinedDateFormat(Optional ByVal iNumber As Integer = 0) As String
Die Formatierungen sind verscheidene Patterns. Die meiste habe ich aus PHP übernommen, andere aus Java. Der Aufbau ist folgednermasen
%(Index)(Format)[Typ](Datum/ZeitFormat)
[Zahl]$
Oder <
. Bei eihner Zahl gefolgt von einem $ Zeichen ist die Nummer des Paramters gemeint. Beginnend bei 1. Sprich %2$s
bedeutet, dass der zweite Parameter ausgegeben wird. Bei < wird derselbe Wert verarbeitet wie beim Pattern davor. %<s
nimmt also dieselbe Quelle wie der letzte Pattern vor ihm.
c the argument is treated as an integer, and presented as the character with that ASCII value.
d the argument is treated as an integer, and presented as a (signed) decimal number.
e the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands for the number of digits after the decimal point
E like e but uses uppercase letter (e.g. 1.2E+2).
u the argument is treated as an integer, and presented as an unsigned decimal number.
f the argument is treated as a float, and presented as a floating-point number (locale aware).
g shorter of %e and %f.
G shorter of %E and %f.
o the argument is treated as an integer, and presented as an octal number.
s the argument is treated as and presented as a string.
x the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
X the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
T date/time Prefix for date and time conversion characters. See Date/Time Conversions.
t like T
S date/time Prefix for date and time conversion characters for MS Access SQL. See Date/Time Conversions.
H Hour of the day for the 24-hour clock, formatted as two digits with a leading zero as necessary i.e. 00 - 23.
I Hour for the 12-hour clock, formatted as two digits with a leading zero as necessary, i.e. 01 - 12.
k Hour of the day for the 24-hour clock, i.e. 0 - 23.
l Hour for the 12-hour clock, i.e. 1 - 12.
M Minute within the hour formatted as two digits with a leading zero as necessary, i.e. 00 - 59.
S Seconds within the minute, formatted as two digits with a leading zero as necessary, i.e. 00 - 60 (“60” is a special value required to support leap seconds).
B Locale-specific full month name, e.g. “January”, “February”.
b Locale-specific abbreviated month name, e.g. “Jan”, “Feb”.
A Locale-specific full name of the day of the week, e.g. “Sunday”, “Monday”
a Locale-specific short name of the day of the week, e.g. “Sun”, “Mon”
C Four-digit year divided by 100, formatted as two digits with leading zero as necessary, i.e. 00 - 99
Y Year, formatted as at least four digits with leading zeros as necessary, e.g. 0092 equals 92 CE for the Gregorian calendar.
y Last two digits of the year, formatted with leading zeros as necessary, i.e. 00 - 99.
j Day of year, formatted as three digits with leading zeros as necessary, e.g. 001 - 366 for the Gregorian calendar.
m Month, formatted as two digits with leading zeros as necessary, i.e. 01 - 13.
d Day of month, formatted as two digits with leading zeros as necessary, i.e. 01 - 31
e Day of month, formatted as two digits, i.e. 1 - 31.
R Time formatted for the 24-hour clock as “%tH:%tM”
T Time formatted for the 24-hour clock as “%tH:%tM:%tS”.
r Time formatted for the 12-hour clock as “%tI:%tM:%tS %Tp”. The location of the morning or afternoon marker ('%Tp') may be locale-dependent.
D Date formatted as “%tm/%td/%ty”.
f Datetime formated as “%tY-%t-%td_%H:%M:%S”
F ISO 8601 complete date formatted as “%tY-%tm-%td”.
Um ein wenig klar zu machen was die Funktion alles kann, hier mal einige Beispiele. Den Code zur Funktion befindet sich weiter unten.
Debug.Print sPrintF("Hallo %s", "Hans") Hallo Hans Debug.Print sPrintF("%s wiegt %f Kilo", "Hans", 76.5) Hans wiegt 76.5 Kilo Debug.Print sPrintF("%s wiegt %d Kilo", "Hans", 76.5) Hans wiegt 76 Kilo
? sPrintF("%2$s wiegt %1$f Kilo", 76.5, "Hans") Hans wiegt 76.5 Kilo ? sPrintF("%1$s ist %1$s und %2$s ist %2$s", "Heute", "Morgen") Heute ist Heute und Morgen ist Morgen ? sPrintF("%s ist %<s und %s ist %<s", "Heute", "Morgen") Heute ist Heute und Morgen ist Morgen ? sprintf("Wir haben %d, ich wiederhole. Wir haben %<d Tage Zeit %d Millionen zu finden", 10, 20) Wir haben 10, ich wiederhole. Wir haben 10 Tage Zeit 20 Millionen zu finden ? sPrintF("%s, %<s, %<s! %3$s, %<s, %<s! %2$s, %<s, %<s! %s!", "Ha", "Ho", "Hi", "Yes") Ha, Ha, Ha! Hi, Hi, Hi! Ho, Ho, Ho! Yes!
Dim params(1) As Variant params(0) = "Heute" params(1) = "Morgen" Debug.Print vsPrintF("%1$s ist %1$s und %2$s ist %2$s", params)
'Strings: debug.print sprintf("%1$s %1$'#6s %1$-'#6s %1$.3s %1$-.3s", "abcd") abcd ##abcd abcd## abc bcd 'Nummern: 'Float debug.print sprintf("%1$f %1$6f %1$-.1f %1$.3f %1$d", 123.45) 123.45 000123.45 +123.5 123.450 123 'Integer debug.print sprintf("%1$f %1$6f %1$-.1f %1$.3f %1$d", 123) 123 000123 +123.0 123.000 123
Der Datumspattern ist Zweiteilig. Zuerst kommt ein Buchstabe um anzugegebe, dass es dsich um ein Datum/Zeit handelt und dann ein Buchstabe für das Format. Da sist nicht meine Erfindung, das übernehme ich so von Java (http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html). Einzig beim Einleitenden Buchstaben habe ich noch das S hinzugefügt, um den Output SQL Konform zu schreiben. Somit haben T oder S als Einleitender Buchstabe, gefolgt vom Format
? sprintf("Heute ist %TA der %<Te %<TB, %<TY", now) Heute ist Freitag der 29 Januar, 2016 ? sprintf("SELECT * FROM mytable WHERE in_date BETWEEN %2$SD AND %1$SD", now, now-10) SELECT * FROM mytable WHERE in_date BETWEEN #01/19/2016# AND #01/29/2016#
Es können bis zu 10 benutzerdefinierte Formate hinterlegt werden. Dazu kann man die Funktion printF_UserDefinedDateFormat verwenden. Die Formate können dann mittel U und dem Index abgerufenwerden
printF_UserDefinedDateFormat(0) = "HH.NN" printF_UserDefinedDateFormat(1) = "d. MMMM YYYY" debug.print sPrintF("%TU1 um %<TU0", now) 21. Oktober 2016 um 11.34
'Mit Zeilenumbruch \t und Tabulator \n debug.print sPrintF("%1$s\tist %1$s,\n%2$s\tist %2$s", "Heute", "Morgen") Heute ist Heute, Morgen ist Morgen 'Tabulator \t etc nicht parsen: den \ von \ mit einem \ markieren -> \\t debug.print sprintf("[\\t]\t%s", "ist ein Tabulator") [\t] ist ein Tabulator 'um nur diese Formatierungen umzuschreiben kann man auch ohne Paramter arbeiten debug.print sprintf("item:\tPunkt1") item: Punkt1
Attribute VB_Name = "lib_printF" '------------------------------------------------------------------------------- 'File : lib_printF.bas ' Copyright mpl by ERB software ' All rights reserved ' http://wiki.yaslaw.info/doku.php/vba/functions/printf/index 'Environment : VBA XP - 2010 'Version : 2.7.1 'Name : PrintF 'Author : Stefan Erb (ERS) 'History : 04.04.2013 - 1.0.0 - ERS - Creation ' ... ' 03.03.2015 - 2.1.0 - ERS - NZ fr Excel eingefgt (Siehe #cont isAccess), eval("#" & value & "#") durch DateValue(value) ersetzt ' 29.01.2016 - 2.2.0 - ERS - Formate fr Datum hinzugefgt, < Als Positionszeichen hinzugefgt ' 26.09.2016 - 2.3.0 - ERS - Neues Datumsformat o Und O hinzugegt. Fehler behoben: Wenn in einem Pattern ein Datum und ein anderes Format war, funktioniert es nicht. ' Neues User-Datumsformat. Bis zu 10 Formate speicherbar und mittels %tu# abrufbar ' 30.10.2018 - 2.5.2 - ERS - Hotfix beim Ersetzen ' 15.11.2018 - 2.5.3 - ERS - Hotfox \t, \n, \r Am Anfang eines Patterns funktioniert jetzt ' 09.05.2019 - 2.6.0 - ERS - Neu q eingefhrt. Analog zu s aber als SQL-String. ?sprintf("abc=%1$'#6q", "abc") => abc='###abc' ' 10.05.2019 - 2.6.1 - ERS - Fehler bei formatierung von Integer korrigiert ' 06.02.2020 - 2.7.0 - ERS - \\ am Ende durch \ ersetzen. Jeder \ der als String verwendet werden soll, muss mit einem \ maskiert werden ' Null Parameter werden automatisch mit NZ() in empty umgewandelt '$f' ' ?sprintf("Null-Test: Zahl:'\\%f' Text:'\\%<s'", Null) -> Null-Test: Zahl:'\0' Text:'\' ' 13.02.2020 - 20701 - ERS - strReplace() durch replace() ersetzt. strReplace war von einer frheren Version drin ' 'Description : Aus der PHP-Doku: ' - An optional sign specifier that forces a sign (- or +) to be used on a number. By default, only the - sign is used on a number if it's negative. ' This specifier forces positive numbers to have the + sign attached as well, and was added in PHP 4.3.0. ' - An optional padding specifier that says what character will be used for padding the results to the right string size. This may be a space character or a 0 ' (zero character). The default is to pad with spaces. An alternate padding character can be specified by prefixing it with a single quote (') ' - An optional alignment specifier that says if the result should be left-justified or right-justified. The default is right-justified; a - character ' here will make it left-justified. ' - An optional number, a width specifier that says how many characters (minimum) this conversion should result in. ' - An optional precision specifier in the form of a period (`.') followed by an optional decimal digit string that says how many decimal digits ' should be displayed for floating-point numbers. When using this specifier on a string, it acts as a cutoff point, setting a maximum character ' limit to the string. ' ' A type specifier that says what type the argument data should be treated as. Possible types: ' % - a literal percent character. No argument is required. ' //b - the argument is treated as an integer, and presented as a binary number. ' c - the argument is treated as an integer, and presented as the character with that ASCII value. ' d - the argument is treated as an integer, and presented as a (signed) decimal number. ' e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands for the number of digits after the decimal point ' E - like %e but uses uppercase letter (e.g. 1.2E+2). ' u - the argument is treated as an integer, and presented as an unsigned decimal number. ' f - the argument is treated as a float, and presented as a floating-point number (locale aware). ' //F - the argument is treated as a float, and presented as a floating-point number (non-locale aware). Available since PHP 4.3.10 and PHP 5.0.3. ' g - shorter of %e and %f. ' G - shorter of %E and %f. ' o - the argument is treated as an integer, and presented as an octal number. ' s - the argument is treated as and presented as a string. ' q - the argument is treated as and presented as a string in sql-form: 'string' ' x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters). ' X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters). ' t, T - date/time Prefix for date and time conversion characters. See Date/Time Conversions. ' S - date/time Prefix for date and time conversion characters for MS Access SQL. See Date/Time Conversions. ' ' Datumsformate: ' http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html ' f ' The following conversion characters are used for formatting times: ' H Hour of the day for the 24-hour clock, formatted as two digits with a leading zero as necessary i.e. 00 - 23. ' I Hour for the 12-hour clock, formatted as two digits with a leading zero as necessary, i.e. 01 - 12. ' k Hour of the day for the 24-hour clock, i.e. 0 - 23. ' i Hour for the 12-hour clock, i.e. 1 - 12. ' M Minute within the hour formatted as two digits with a leading zero as necessary, i.e. 00 - 59. ' S Seconds within the minute, formatted as two digits with a leading zero as necessary, i.e. 00 - 60 ("60" is a special value required to support leap seconds). ' The following conversion characters are used for formatting dates: ' B Locale-specific full month name, e.g. "January", "February". ' b Locale-specific abbreviated month name, e.g. "Jan", "Feb". ' A Locale-specific full name of the day of the week, e.g. "Sunday", "Monday" ' a Locale-specific short name of the day of the week, e.g. "Sun", "Mon" ' C Four-digit year divided by 100, formatted as two digits with leading zero as necessary, i.e. 00 - 99 ' Y Year, formatted as at least four digits with leading zeros as necessary, e.g. 0092 equals 92 CE for the Gregorian calendar. ' y Last two digits of the year, formatted with leading zeros as necessary, i.e. 00 - 99. ' j Day of year, formatted as three digits with leading zeros as necessary, e.g. 001 - 366 for the Gregorian calendar. ' m Month, formatted as two digits with leading zeros as necessary, i.e. 01 - 13. ' d Day of month, formatted as two digits with leading zeros as necessary, i.e. 01 - 31 ' e Day of month, formatted as two digits, i.e. 1 - 31. ' The following conversion characters are used for formatting common date/time compositions. ' R Time formatted for the 24-hour clock as "%tH:%tM" ' T Time formatted for the 24-hour clock as "%tH:%tM:%tS". ' r Time formatted for the 12-hour clock as "%tI:%tM:%tS %Tp". The location of the morning or afternoon marker ('%Tp') may be locale-dependent. ' D Date formatted as "#%tm/%td/%ty#". ' t DateTime formatted as "%tm/%td/%ty %tH:%tM:%tS". ' F ISO 8601 complete date formatted as "%tY-%tm-%td". ' f order by Datetie for sortable Date %tY-%t-%td_%H:%M:%S ' L Date in Local "Long Date" Format ' l Date in Local "Short Date" Format ' o order by Date for sortable Date %tY%t%td ' O order by Datetime for sortable Date %tY%t%td_%H%M%S ' u User Format -> %u1. Set the Userformat with the public property printF_UserDefinedDateFormat(1) = "DD.MM" ' ' Zustzlich noch die Formatierungen analog http://www.freevbcode.com/ShowCode.asp?ID=5014 ' Um einen davon nicht zu parsen einfach mit einem \ marieren: '\\t' wird nicht zu Tabulator sondern zu '\t' geparst ' \n Newline (Line Feed) ' \r Carriage Return ' \t Horizontal Tab '------------------------------------------------------------------------------- '------------------------------------------------------------------------------- ' -- ! SETTINGS ! '------------------------------------------------------------------------------- 'In Excel funktionieren Events nicht. Auch der NZ() gibt es dort nicht. 'Darum hier angeben ob es sich um MS Access handelt oder eben nicht. Leider gibts dazu keine Systemvariable #Const isAccess = True Option Explicit '----------------------------------------- '--- Public members '----------------------------------------- 'Error: Zu wenige Werte wurden bergeben Public Const ERR_INSUFFICIENT_PARAMS = vbObjectError - 10 'Es wird keine Zahl geliefert wo eine erwartet wird Public Const ERR_NOT_NUMBER = vbObjectError - 11 'Das Format ist ungltig Public Const ERR_INVALID_FORMAT = vbObjectError - 12 '----------------------------------------- '--- Private members '----------------------------------------- 'Die Auflistung der Submatches des geparsten Elements (Pattern D_ELEMENT_PATTERN) Private Enum eSubElements emSubType1 = 0 emCount emSubType2 emType1 emType2 emFormat emIndex emPrefPos End Enum 'Die Auflistung der Submatches des geparsten Formates (Pattern C_FORMAT_PATTERN) Private Enum eSubFormat emWithSign = 0 emFillZeroChar emFillChar emFillLength emInternalDecimalPlaces End Enum 'Die Sammlung der Infos aus dem Pattern und der Typendefinition Private Type tParts paramIndex As Integer types As String subTypeS As String count As Integer 'Ein Counter valueV As Variant 'Value als Variant, Also in Der Rohform aus dem Input values As String 'Formatierter Value als String format As String 'Das Format typeDef As String 'Typendefinition End Type 'Infos aus dem Format Private Type tFormat withSgn As Boolean 'Flag: das Vorzeigen immer angezeigt werden muss sgn As String 'Vorzeichen hasInternalDecimalPlaces As Boolean 'Flag: hat Fixe nachkommastellen internalDecimalPlaces As Integer 'Fixe Nachkommastellen fillChar As String 'Fllzeichen hasFillLength As Boolean 'Flag: Hat fixe Vorkommalnge fillLength As Integer 'Fixe Vorkommalnge End Type Private Enum eTypeDef eString eNumber eDateTime eSpezial End Enum Private Enum sysLocale LOCALE_IFIRSTDAYOFWEEK = &H100C 'first day of week specifier 0=Mon, 6=Sun https://msdn.microsoft.com/en-us/library/windows/desktop/dd373771%28v=vs.85%29.aspx LOCALE_IFIRSTWEEKOFYEAR = &H100D 'first week of year specifier 0=direct, 1=Full, 2=4days https://msdn.microsoft.com/en-us/library/windows/desktop/dd373772%28v=vs.85%29.aspx LOCALE_SSHORTDATE = &H1F '31: short date format string End Enum Private Declare Function GetUserDefaultLCID Lib "kernel32" () As Long Private Declare Function GetSystemDefaultLCID Lib "kernel32" () As Long Private Declare Function getLocaleInfo Lib "kernel32" Alias "GetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, ByVal lpLCData As String, ByVal cchData As Long) As Long Private userDateFormat(0 To 9) As String '----------------------------------------- '--- Public Methodes '----------------------------------------- '/** ' * Parse einen String mit Werten ' * @param <String> Zu formatierender String ' * @param <Variant>* die dazugehrigen Werte ' * @return <String> ' */ Public Function sPrintF(ByVal iFormatString As String, ParamArray iParams() As Variant) As String On Error GoTo Err_Handler: Err.Source = "sPrintF" If UBound(iParams) = -1 Then sPrintF = replaceFormat(iFormatString) Else sPrintF = vsPrintF(iFormatString, CVar(iParams)) End If Exit_Handler: On Error Resume Next Exit Function Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Function '/** ' * Parse einen String mit den Werten als Array. ' * @param <String> Zu formatierender String ' * @param Array<Variant> die dazugehrigen Werte ' * @return <String> ' */ Public Function vsPrintF(ByVal iFormatString As String, ByRef iParams As Variant) As String On Error GoTo Err_Handler: Err.Source = "vsPrintF" Dim params As Variant: params = IIf(IsArray(iParams), iParams, Array(iParams)) 'Formatedefinitionen /t /n etc ausfhren 'Der String muss nachher umgedreht werden, da VBA object kein (?>!..) kennt vsPrintF = replaceFormat(iFormatString) 'Wenn keine Parameters angegeben sind, dann beenden If UBound(params) = -1 Then Resume Exit_Handler 'String parsen ' vsPrintF = evalWithRx(rxDTElement, vsPrintF, params) vsPrintF = evalWithRx(rxElement, vsPrintF, params) 'Maskierte \ zurcksetzen vsPrintF = replace(vsPrintF, "\\", "\") Exit_Handler: On Error Resume Next Exit Function Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Function '----------------------------------------- '--- Public Properties '----------------------------------------- '/** ' * Ein Userdefiniertes Datumsformat ' * @param Integer Nummer des Formates. 0-9 mglich ' * @return String Datumsformat ' */ Public Property Get printF_UserDefinedDateFormat(Optional ByVal iNumber As Integer = 0) As String iNumber = IIf(iNumber > 9, 9, iNumber) iNumber = IIf(iNumber < 0, 0, iNumber) printF_UserDefinedDateFormat = IIf(userDateFormat(iNumber) = Empty, "Short Date", userDateFormat(iNumber)) End Property Public Property Let printF_UserDefinedDateFormat(ByVal iNumber As Integer, ByVal iDateFormat As String) iNumber = IIf(iNumber > 9, 9, iNumber) iNumber = IIf(iNumber < 0, 0, iNumber) userDateFormat(iNumber) = iDateFormat End Property '----------------------------------------- '--- Private Properties/Cache '----------------------------------------- '/** ' * object um die einzelnen Elemente aus dem Pattern zu extrahieren ' * 0: SubType ' * 1: Type ' * 2: Type ' * 3: Count ' * 4: Type ' * 5: Format ' * 6: Item Nummer ' * 7: Verweis auf vorhergehende Position ' * @return object ' */ Private Property Get rxElement() As Object Static rx As Object If rx Is Nothing Then Set rx = cRx("/(?:(?:([HIikLlMSLNpzZsQBbhAaCYyjmdeRTtrDFfPoO])|(\d)([u]))([tTS])|([sqcdfuoxXeEgG]))([\d-\.'#]*)(?:\$(\d+)|(<))?%(?!\\(?!\\))/gm") Set rxElement = rx End Property Private Property Get rxNonElement() As Object Static rx As Object If rx Is Nothing Then Set rx = cRx("/((?:(?:[HIikLlMSLNpzZsQBbhAaCYyjmdeRTtrDFfPoO]|\d[u])[tTS]|[sqcdfuoxXeEgG])[\d-\.'#]*(?:\$\d+|<)?%)(?:\\(?!\\))/gm") Set rxNonElement = rx End Property 'Private Property Get rxElement() As Object ' Static rx As Object ' If rx Is Nothing Then Set rx = cRx("/()([scdfuoxXeEgG])([\d-\.'#]*)(?:\$(\d+)|(<)|)%(?!\\)/gm") ' Set rxElement = rx 'End Property ' 'Private Property Get rxDTElement() As Object ' Static rx As Object ' If rx Is Nothing Then Set rx = cRx("/([HIikLlMSLNpzZsQBbhAaCYyjmdeRTrDFPoO])([tTS])([\d-\.'#]*)(?:\$(\d+)|(<)|)%(?!\\)/gm") ' Set rxDTElement = rx 'End Property '/** ' * object um die Formate zu analysieren ' * emWithSign = 0 ' * emFillZeroChar ' * emFillChar ' * emFillLength ' * emInternalDecimalPlaces ' * @return object ' */ Private Property Get rxFormat() As Object Static rx As Object If rx Is Nothing Then Set rx = cRx("/([-+]?)(?:(0)|'(.)|)([1-9]\d*|)(?:\.([0-9]+)|)/gm") Set rxFormat = rx End Property '/** ' * Typendefinition ' * @return Dictionary ' */ Private Property Get typeDefs() Static pCacheTypeDefs As Object If pCacheTypeDefs Is Nothing Then Set pCacheTypeDefs = CreateObject("scripting.Dictionary") With pCacheTypeDefs .RemoveAll 'Die verschiedenen Typendefinitionen laden .add "s", eString 's - the argument is treated as and presented as a string. .add "q", eString 'q - the argument is treated as and presented as a string. .add "d", eNumber 'd - the argument is treated as an integer, and presented as a (signed) decimal number. .add "f", eNumber 'f - the argument is treated as a float, and presented as a floating-point number (locale aware). .add "u", eNumber 'u - the argument is treated as an integer, and presented as an unsigned decimal number. .add "c", eSpezial 'c - the argument is treated as an integer, and presented as the character with that ASCII value. .add "o", eSpezial 'o - the argument is treated as an integer, and presented as an octal number. .add "x", eSpezial 'x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters). .add "X", eSpezial 'X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters). .add "e", eSpezial 'e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands for the number of digits after the decimal point .add "E", eSpezial 'E - like %e but uses uppercase letter (e.g. 1.2E+2). .add "g", eSpezial 'g - shorter of %e and %f. .add "G", eSpezial 'G - shorter of %E and %f. .add "t", eDateTime .add "T", eDateTime .add "S", eDateTime End With End If Set typeDefs = pCacheTypeDefs End Property '/** ' * die object um die Formatierungen (\t, \n etc) zu handeln ' * @return Dictionary(object => ReplaceString) ' */ Public Property Get formatDefs() As Object Static pCacheFormatDefs As Object If pCacheFormatDefs Is Nothing Then Set pCacheFormatDefs = CreateObject("scripting.Dictionary") pCacheFormatDefs.add cRx("/(.*[^\\]|^)\\n(.*)/m"), "$1" & vbLf & "$2" '\n Newline (Line Feed) pCacheFormatDefs.add cRx("/(.*[^\\]|^)\\r(.*)/m"), "$1" & vbCr & "$2" '\r Carriage Return pCacheFormatDefs.add cRx("/(.*[^\\]|^)\\t(.*)/m"), "$1" & vbTab & "$2" '\t Horizontal Tab End If Set formatDefs = pCacheFormatDefs End Property '----------------------------------------- '--- Private Methodes '----------------------------------------- '/** ' * Wertet den String anhand eines RegeExp aus ' * @param RegExp ' * @param String ' * @param Array<Variant> ' * @return String ' */ Private Function evalWithRx(ByRef irx As Object, ByVal iString As String, ByRef iParams As Variant) As String On Error GoTo Err_Handler: Err.Source = "evalWithRx" evalWithRx = StrReverse(iString) If Not irx.test(evalWithRx) Then GoTo Exit_Handler Dim matches As Object: Set matches = irx.execute(evalWithRx) Dim lEof As Long: lEof = matches.count - 1 Dim parts() As tParts: ReDim parts(lEof) Dim delta As Long Dim i As Integer: For i = lEof To 0 Step -1 Dim position As Integer: position = (lEof - i) Dim ma As Object: Set ma = matches.item(i) With parts(i) 'Werte aus dem Pattern auslesen 'Der Index aus dem Pattern oder die Position im String 'Falls es sich um ein < Handelt, den vorherigen paramIndex verwenden, ausser es ist der Erste If ma.subMatches(emPrefPos) = "<" And i < lEof Then .paramIndex = parts(i + 1).paramIndex delta = delta - 1 'Keine eindeutige Position angegeben ElseIf ma.subMatches(emIndex) = Empty Then .paramIndex = position + delta 'Position aus dem Pattern bernehmen Else .paramIndex = ma.subMatches(emIndex) - 1 End If 'Formatdefinitionen aus dem Pattern .format = StrReverse(NZ(ma.subMatches(emFormat))) .types = ma.subMatches(emType1) & ma.subMatches(emType2) .subTypeS = ma.subMatches(emSubType1) & ma.subMatches(emSubType2) .count = CInt(NZ(ma.subMatches(emCount))) 'Werte der Typendefinitione bernehmen .typeDef = typeDefs(.types) 'Prfen ob zu dem ermittelten Index auch ein Parameterwert exisitiert If .paramIndex > UBound(iParams) Then Call Err.Raise(ERR_INSUFFICIENT_PARAMS, , "Not enough values fr (v)sprintf(). ") 'unformatierten Wert aus den Parametern auslsen .valueV = NZ(iParams(.paramIndex)) 'Parsen und Formatieren. Select Case .typeDef Case eSpezial: Call parseSpezial(parts(i)) Case eDateTime: Call parseDateTime(parts(i)) Case eNumber: Call parseNumber(parts(i)) Case eString: Call parseString(parts(i)) End Select End With Next i Dim rx As Object: Set rx = cRx("/" & irx.pattern & "/m") i = 0 Do While rx.test(evalWithRx) evalWithRx = rx.replace(evalWithRx, StrReverse(parts(i).values)) i = i + 1 Loop 'Maskierung bei maskierten Pattern entfernen evalWithRx = rxNonElement.replace(evalWithRx, "$1") Exit_Handler: On Error Resume Next evalWithRx = StrReverse(evalWithRx) Set ma = Nothing Set matches = Nothing Exit Function Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Function '/** ' * Parst Formatierungen wie \n, \t etc ' * @param <String> Zu formatierender String ' * @return <String> ' */ Private Function replaceFormat(ByVal iFormatString As String) As String On Error GoTo Err_Handler: Err.Source = "replaceFormat" replaceFormat = iFormatString Dim rx As Variant: For Each rx In formatDefs Do While rx.test(replaceFormat) replaceFormat = rx.replace(replaceFormat, formatDefs(rx)) Loop Next Exit_Handler: On Error Resume Next Exit Function Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Function '/* ' * Nur das preFormat wird ausgefhrt. Der format-Part des Patterns wird ignoriert ' * @param <tParts> ' */ Private Sub parseSpezial(ByRef ioParts As tParts) On Error GoTo Err_Handler: Err.Source = "parseSpezial" With ioParts Select Case .types Case "c": .valueV = Chr(CInt(.valueV)) Case "o": .valueV = Oct(.valueV) Case "x": .valueV = LCase(Hex(.valueV)) Case "X": .valueV = UCase(Hex(.valueV)) Case "e": .valueV = LCase(format(.valueV, "Scientific")) Case "E": .valueV = UCase(format(.valueV, "Scientific")) Case "g": .valueV = IIf(Len(".ValueV") <= Len(format(.valueV, "Scientific")), CDbl(.valueV), LCase(format(.valueV, "Scientific"))) Case "G": .valueV = IIf(Len(".ValueV") <= Len(format(.valueV, "Scientific")), CDbl(.valueV), UCase(format(.valueV, "Scientific"))) End Select End With Exit_Handler: On Error Resume Next Exit Sub Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Sub '/* ' * Parse a Number ' * @param <tParts> ' */ Private Sub parseNumber(ByRef ioParts As tParts) On Error GoTo Err_Handler: Err.Source = "parseNumber" With ioParts If Not IsNumeric(.valueV) Then Call Err.Raise(ERR_NOT_NUMBER, , "Parameter is not a Number") Select Case .types Case "d": .valueV = CLng(.valueV) Case "f": .valueV = CDbl(.valueV) Case "u": .valueV = Abs(CInt(.valueV)) End Select .values = parseNumberFormat(.format, .valueV) End With Exit_Handler: On Error Resume Next Exit Sub Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Sub '/** ' * Formatiert eine Nummer ' * @param String ' * @param Variant ' * @return String ' */ Private Function parseNumberFormat(ByVal iFormat As String, ByVal iValueV As Variant) As String On Error GoTo Err_Handler: Err.Source = "parseNumberFormat" parseNumberFormat = CStr(iValueV) If iFormat = vbNullString Then Exit Function 'Formatdetails auselsen Dim fd As tFormat: fd = parseFormat(iFormat, 0) Dim formats As String 'Mit 0 auffllen formats = "0" If fd.hasFillLength Then formats = str_repeat("0", fd.fillLength) 'Dezmalzeichen If fd.hasInternalDecimalPlaces Then If fd.internalDecimalPlaces > 0 Then formats = formats & "." & str_repeat("0", fd.internalDecimalPlaces) Else If iValueV <> Fix(iValueV) Then formats = formats & "." & str_repeat("#", Len(iValueV - Fix(iValueV))) End If End If 'Vorzeichen If fd.withSgn Then formats = "+" & formats & ";-" & formats parseNumberFormat = format(iValueV, formats) Exit_Handler: On Error Resume Next Exit Function Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Function '/* ' * Parse a String ' * @param <tParts> ' */ Private Sub parseString(ByRef ioParts As tParts) On Error GoTo Err_Handler: Err.Source = "parseString" With ioParts 'ERS: 08.12.2014: Keine Ahnung warum ' maskiert werden sollten ' '-Zeichen im Text markieren ' .valueV = Replace(CStr(.valueV), "'", "''") 'Wenn kein Format definiert ist, einfach die Variable un .values = CStr(.valueV) If Not .format = vbNullString Then 'Formatdetails auselsen. Die Items von fd haben die Namen von der Nummerformatierung Dim fd As tFormat: fd = parseFormat(.format, " ") 'Wenn ien Vorzeichen gesetzt ist, das ganez Rechtbndig nehmen 'An optional alignment specifier that says if the result should be left-justified or right-justified. The default is right-justified; a - character 'here will make it left-justified. If fd.sgn = "-" Then If fd.hasFillLength Then .values = rPad(.values, fd.fillChar, fd.fillLength) End If If fd.hasInternalDecimalPlaces Then .values = Right(.values, fd.internalDecimalPlaces) End If Else If fd.hasFillLength Then .values = lPad(.values, fd.fillChar, fd.fillLength) End If If fd.hasInternalDecimalPlaces Then .values = Left(.values, fd.internalDecimalPlaces) End If End If End If If .types = "q" Then .values = "'" & .values & "'" End With Exit_Handler: On Error Resume Next Exit Sub Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Sub ' The following conversion characters are used for formatting times: ' 'H' Hour of the day for the 24-hour clock, formatted as two digits with a leading zero as necessary i.e. 00 - 23. ' 'I' Hour for the 12-hour clock, formatted as two digits with a leading zero as necessary, i.e. 01 - 12. ' 'k' Hour of the day for the 24-hour clock, i.e. 0 - 23. ' 'i' Hour for the 12-hour clock, i.e. 1 - 12. ' 'M' Minute within the hour formatted as two digits with a leading zero as necessary, i.e. 00 - 59. ' 'S' Seconds within the minute, formatted as two digits with a leading zero as necessary, i.e. 00 - 60 ("60" is a special value required to support leap seconds). '- 'L' Millisecond within the second formatted as three digits with leading zeros as necessary, i.e. 000 - 999. '- 'N' Nanosecond within the second, formatted as nine digits with leading zeros as necessary, i.e. 000000000 - 999999999. '- 'p' Locale-specific morning or afternoon marker in lower case, e.g."am" or "pm". Use of the conversion prefix 'T' forces this output to upper case. '- 'z' RFC 822 style numeric time zone offset from GMT, e.g. -0800. This value will be adjusted as necessary for Daylight Saving Time. For long, Long, and Date the time zone used is the default time zone for this instance of the Java virtual machine. '- 'Z' A string representing the abbreviation for the time zone. This value will be adjusted as necessary for Daylight Saving Time. For long, Long, and Date the time zone used is the default time zone for this instance of the Java virtual machine. The Formatter's locale will supersede the locale of the argument (if any). '- 's' Seconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC, i.e. Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000. '- 'Q' Milliseconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC, i.e. Long.MIN_VALUE to Long.MAX_VALUE. ' ' The following conversion characters are used for formatting dates: ' 'B' Locale-specific full month name, e.g. "January", "February". ' 'b' Locale-specific abbreviated month name, e.g. "Jan", "Feb". '- 'h' Same as 'b'. ' 'A' Locale-specific full name of the day of the week, e.g. "Sunday", "Monday" ' 'a' Locale-specific short name of the day of the week, e.g. "Sun", "Mon" ' 'C' Four-digit year divided by 100, formatted as two digits with leading zero as necessary, i.e. 00 - 99 ' 'Y' Year, formatted as at least four digits with leading zeros as necessary, e.g. 0092 equals 92 CE for the Gregorian calendar. ' 'y' Last two digits of the year, formatted with leading zeros as necessary, i.e. 00 - 99. ' 'j' Day of year, formatted as three digits with leading zeros as necessary, e.g. 001 - 366 for the Gregorian calendar. ' 'm' Month, formatted as two digits with leading zeros as necessary, i.e. 01 - 13. ' 'd' Day of month, formatted as two digits with leading zeros as necessary, i.e. 01 - 31 ' 'e' Day of month, formatted as two digits, i.e. 1 - 31. ' ' The following conversion characters are used for formatting common date/time compositions. ' 'R' Time formatted for the 24-hour clock as "%tH:%tM" ' 'T' Time formatted for the 24-hour clock as "%tH:%tM:%tS". ' 'r' Time formatted for the 12-hour clock as "%tI:%tM:%tS %Tp". The location of the morning or afternoon marker ('%Tp') may be locale-dependent. ' 'D' Date formatted as "%tm/%td/%ty". ' 't' DateTime formatted as "%tm/%td/%ty %tH:%tM:%tS". ' 'F' ISO 8601 complete date formatted as "%tY-%tm-%td". ' 'f' order by Datetie for sortable Date %tY-%t-%td_%H:%M:%S ' 'L' Date in Local "Long Date" Format ' 'l' Date in Local "Short Date" Format '- 'c' Date and time formatted as "%ta %tb %td %tT %tZ %tY", e.g. "Sun Jul 20 16:17:00 EDT 1969". ' 'o' order by Date for sortable Date %tY%t%td ' 'O' order by Datetie for sortable Date %tY%t%td_%H%M%S ' 'u' User Format -> %u0 '/* ' * Parse a String ' * @param <tParts> ' */ Private Sub parseDateTime(ByRef ioParts As tParts) On Error GoTo Err_Handler: Err.Source = "parseDateTime" Select Case ioParts.subTypeS Case "H": ioParts.values = format(ioParts.valueV, "HH") Case "I": ioParts.values = format(ioParts.valueV, "HHAMPM") Case "k": ioParts.values = format(ioParts.valueV, "H") Case "i": ioParts.values = format(ioParts.valueV, "HAMPM") Case "M": ioParts.values = format(ioParts.valueV, "NN") Case "S": ioParts.values = format(ioParts.valueV, "SS") Case "P": ioParts.values = format(ioParts.valueV, "AM/PM") Case "B": ioParts.values = format(ioParts.valueV, "MMMM") Case "b": ioParts.values = format(ioParts.valueV, "MMM") Case "A": ioParts.values = format(ioParts.valueV, "DDDD") Case "a": ioParts.values = format(ioParts.valueV, "DDD") Case "C": ioParts.values = CStr(year(ioParts.valueV) \ 100) Case "Y": ioParts.values = format(ioParts.valueV, "YYYY") Case "Y": ioParts.values = format(ioParts.valueV, "YY") Case "j": ioParts.values = format(format(ioParts.valueV, "Y"), "000") Case "m": ioParts.values = format(ioParts.valueV, "MM") Case "d": ioParts.values = format(ioParts.valueV, "DD") Case "e": ioParts.values = format(ioParts.valueV, "D") Case "R": ioParts.values = format(ioParts.valueV, "HH:NN") Case "T": ioParts.values = format(ioParts.valueV, "HH:NN:SS") Case "r": ioParts.values = format(ioParts.valueV, "HH:NN:SS AM/PM") Case "D": ioParts.values = format(ioParts.valueV, "MM\/DD\/YYYY") Case "t": ioParts.values = format(ioParts.valueV, "MM\/DD\/YYYY HH:NN:SS") Case "F": ioParts.values = format(ioParts.valueV, "YYYY-MM-DD") Case "f": ioParts.values = format(ioParts.valueV, "YYYY-MM-DD HH:NN:SS") Case "L": ioParts.values = format(ioParts.valueV, "Long Date") Case "l": ioParts.values = format(ioParts.valueV, "Short Date") Case "O": ioParts.values = format(ioParts.valueV, "YYYYMMDD_HHNNSS") Case "o": ioParts.values = format(ioParts.valueV, "YYYYMMDD") Case "u": ioParts.values = format(ioParts.valueV, printF_UserDefinedDateFormat) End Select 'Spezialfall SQL If ioParts.types = "S" Then Select Case ioParts.subTypeS Case "R", "T", "t", "D", "F", "f", "r": ioParts.values = "#" & ioParts.values & "#" Case "B", "b", "A", "a": ioParts.values = "'" & ioParts.values & "'" End Select End If Exit_Handler: On Error Resume Next Exit Sub Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Sub '/** ' * Parst den Format-Teil des Patterns ' * @param <String> Formatteil ' * @param <Variant> Standart-Fllzeichen ' * @return <tFormat> ' */ Private Function parseFormat(ByVal iFormat As String, ByVal iDefaultFillChar As Variant) As tFormat On Error GoTo Err_Handler: Err.Source = "parseFormat" If Not rxFormat.test(iFormat) Then Call Err.Raise(ERR_INVALID_FORMAT, , "Wrong Format '" & iFormat & "'") 'Format parsen Dim matches As Object: Set matches = rxFormat.execute(iFormat) Dim subMatches As Object: Set subMatches = matches.item(0).subMatches 'Die einzelnen Teile auslesen und auswerten With parseFormat 'Fllzeichen ermitteln .fillChar = subMatches(emFillZeroChar) If .fillChar = Empty Then .fillChar = subMatches(emFillChar) If .fillChar = Empty Then .fillChar = iDefaultFillChar 'Fllnge .hasFillLength = subMatches(emFillLength) <> Empty If .hasFillLength Then .fillLength = CInt(subMatches(emFillLength)) 'Nachkommastellen .hasInternalDecimalPlaces = subMatches(emInternalDecimalPlaces) <> Empty If .hasInternalDecimalPlaces Then .internalDecimalPlaces = CInt(subMatches(emInternalDecimalPlaces)) 'Vorzeichen (bei String, Links-Rechts Bndig) .withSgn = subMatches(emWithSign) <> Empty 'Das Vorzeichen selber wird so nur beim String verwendet .sgn = IIf(.withSgn, subMatches(emWithSign), "+") End With Exit_Handler: On Error Resume Next Set subMatches = Nothing Set matches = Nothing Exit Function Err_Handler: Call Err.Raise(Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext) Resume Exit_Handler Resume End Function '----------------------------------------- '--- Verwendete allgemeine Funktionen '----------------------------------------- '----------------------------------------- ' cDict V 2.1.0 '----------------------------------------- '/** ' * Wandelt verschiedene Formate in ein Dictionary um ' * http://wiki.yaslaw.info/dokuwiki/doku.php/vba/cast/cdict ' * @param ParamArray ' * @return Dictionary ' */ Private Function cDict(ParamArray iItems() As Variant) As Object Set cDict = CreateObject("scripting.Dictionary") Dim items() As Variant: items = CVar(iItems) Dim i As Integer, key As Variant, value As Variant Dim isList As Boolean If UBound(items) = -1 Then Exit Function 'Prfen ob 2 Parametetrs bergeben wurden If UBound(items) = 1 Then 'Beide Parametrer sind Arrays -> Combine: Erster Array Keys, 2ter Values If IsArray(items(0)) And IsArray(items(1)) Then key = items(0): value = items(1) Dim delta As Long: delta = LBound(key) - LBound(value) ReDim Preserve value(LBound(value) To UBound(key) + delta) For i = LBound(key) To UBound(key) If Not cDict.exists(key(i)) Then cDict.add key(i), value(i + delta) Next i Exit Function End If End If 'Alle Items durchackern For i = 0 To UBound(items) Dim item As Variant: ref item, items(i) 'Dictionary If Not isList And TypeName(item) = "Dictionary" Then For Each key In items(i).keys If Not cDict.exists(key) Then cDict.add key, item.item(key) Next key 'einsamer Array ElseIf Not isList And IsArray(item) Then For key = LBound(item) To UBound(item) If Not cDict.exists(key) Then cDict.add key, item(key) Next key 'SetString ElseIf Not isList And Not IsArray(item) And Not IsObject(item) Then If rxSetString.test(StrReverse(item)) Then Dim mc As Object: Set mc = rxSetString.execute(StrReverse(item)) Dim k As Integer: For k = mc.count - 1 To 0 Step -1 Dim m As Object: Set m = mc(k) key = StrReverse(firstValue(m.subMatches(6), m.subMatches(5), m.subMatches(3))) value = StrReverse(firstValue(m.subMatches(2), m.subMatches(1))) Select Case m.subMatches(0) Case "#": value = dateValue(value) 'eval("#" & value & "#") Case Empty: value = CDec(value) Case Else: value = cRx("/\\(['""])/g").replace(value, "$1") End Select If Not cDict.exists(key) Then cDict.add key, value Next k Else GoTo default 'Zuerst wollte ich diesen GoTo nicht haben. Aber StrReverse() wirft ein Fehler wenn ein Objekt bergeben wird. Darum konnte der Test nicht im ElseIf durchgefhrt werden. End If 'Alles andere geht in ein WertePaar. ElseIf i = 0 Or isList Then default: If i Mod 2 = 0 Then key = item Else If Not cDict.exists(key) Then cDict.add key, item End If isList = True End If Next i 'Falls es sich um eine nicht abgeschlossene Liste handelt If isList And i Mod 2 <> 0 Then If Not cDict.exists(key) Then cDict.add key, Empty End If End Function '------------------------------------------------------------------------------- '-- Private methodes / properties for cDict() '------------------------------------------------------------------------------- '/** ' * Gibt den ersten Wert zurck, der nicht Nothing, Empty oder Null ist ' * @param ParamArray ' * @return Variant ' */ Private Function firstValue(ParamArray items() As Variant) As Variant For Each firstValue In items If IsObject(firstValue) Then If Not firstValue Is Nothing Then Exit For Else If Not IsNull(firstValue) And Not firstValue = Empty Then Exit For End If Next End Function '/** ' * Gibt eine Refernez auf den Wert zurck ' * @param Variant Variable, di abgefllt werden soll ' * @param Variant Value ' */ Private Sub ref(ByRef oItem As Variant, Optional ByRef iItem As Variant) If IsMissing(iItem) Then oItem = Empty ElseIf IsObject(iItem) Then Set oItem = iItem Else oItem = iItem End If End Sub '/** ' * Handelt den object-Cache um ein Set-String zu zerlegen ' * @return object ' */ Private Property Get rxSetString() As Object Static rxCachedSetString As Object If rxCachedSetString Is Nothing Then Set rxCachedSetString = CreateObject("VBScript.RegExp") rxCachedSetString.Global = True rxCachedSetString.pattern = "(?:(['""#])(?!\\)(.+?)\1(?!\\)|([0-9\.]+?))\s*(?:>=|[:=])\s*(?:\]([^\[]+)\[|(['""])(?!\\)(.+?)\5(?!\\)|(\w+))" End If Set rxSetString = rxCachedSetString End Property '------------------------------------------------------------------------------- '-- cRx() '------------------------------------------------------------------------------- '/** ' * Dies ist die Minimalversion von cRegExp ' * http://wiki.yaslaw.info/dokuwiki/doku.php/vba/cast/cregexp#abgespeckte_version ' * mgliche Delemiter: @&!/~#=\| ' * mgliche Modifiers: g (Global), i (IgnoreCode, m (Mulitline) ' * ' * @example myRx = cRx("/([a]{2,})/i") 'Finde alle folgen von a. Flag:IgnoreCase ' * @version 2.1.0 (01.12.2014) ' * @param String Pattern mit Delimiter und Modifier analog zu PHP ' * @return Object object-Object ' */ Private Function cRx(ByVal iPattern As String) As Object Static rxP As Object: Set cRx = CreateObject("VBScript.RegExp") If rxP Is Nothing Then: Set rxP = CreateObject("VBScript.RegExp"): rxP.pattern = "^([@&!/~#=\|])?(.*)\1(?:([Ii])|([Gg])|([Mm]))*$" Dim sm As Object: Set sm = rxP.execute(iPattern)(0).subMatches cRx.pattern = sm(1): cRx.IgnoreCase = Not isEmpty(sm(2)): cRx.Global = Not isEmpty(sm(3)): cRx.Multiline = Not isEmpty(sm(4)) End Function 'http://support.microsoft.com/kb/96458 '===================================================================== 'The following function will left pad a string with a specified 'character. It accepts a base string which is to be left padded with 'characters, a character to be used as the pad character, and a 'length which specifies the total length of the padded result. '===================================================================== Private Function lPad(ByVal MyValue$, ByVal MyPadCharacter$, ByVal MyPaddedLength%) Dim x As Integer Dim PadLength As Integer PadLength = MyPaddedLength - Len(MyValue) Dim padString As String For x = 1 To PadLength padString = padString & MyPadCharacter Next lPad = padString + MyValue End Function '===================================================================== 'The following function will right pad a string with a specified 'character. It accepts a base string which is to be right padded with 'characters, a character to be used as the pad character, and a 'length which specifies the total length of the padded result. '===================================================================== Private Function rPad(ByVal MyValue$, ByVal MyPadCharacter$, ByVal MyPaddedLength%) Dim x As Integer Dim PadLength As Integer PadLength = MyPaddedLength - Len(MyValue) Dim padString As String For x = 1 To PadLength padString = MyPadCharacter & padString Next rPad = MyValue + padString End Function '/** ' * Wiederholt einen String ' * http://wiki.yaslaw.info/wikka/vbvbaStringFunctions ' * @param String zu wiederholender String ' * @param Integer Anzahl weiderholungen ' * @return String Resultat ' */ Private Function str_repeat(ByVal str As String, ByVal multiplier As Integer) As String Dim i As Integer For i = 1 To multiplier str_repeat = str_repeat & str Next i End Function #If Not isAccess Then '/** ' * Wandelt NULL in EMpty oder einen Defaultwert ' * @param Variant ' * @param Variant ' * @return Variant ' */ Private Function NZ(ByRef iValue As Variant, Optional ByRef iDefault As Variant = Empty) As Variant If IsNull(iValue) Then NZ = iDefault Else NZ = iValue End If End Function #End If