; ######################################## ; FORMATSTRING ; V 5 (01.03.2013) ; (C) BY NILS HINTZE ; http://www.elektronikpage.net/purebasic/ ; ######################################## ; ######################################## ; FormatString() ; Die Funktion FormatString() dient dazu, Variablen verschiedener Typen in einen ; String einzufügen und dabei auf eine bestimmte Art zu formatieren. Sie erfüllt ; damit einen vergleichbaren Zweck wie sprintf() unter C. ; ; Aufruf und Parameter ; ==================== ; ; Result$ = FormatString(String$, *Variables.FormatStringVariables) ; ; String$ ist ein String mit zunächst nicht näher definiertem Inhalt. Er darf an ; beliebiger Stelle sogenannte Platzhalter enthalten, welche von FormatString() ; durch die Variablen ersetzt werden. Die Platzhalter werden im entsprechenden ; Abschnitt näher beschrieben. ; ; *Variables ist ein Zeiger auf eine FormatStringVariables-Struktur. Diese ; Struktur enthält ein Array von FormatStringVariable-Strukturen, in welchen ; bestimmte Informationen über die Variablen angegeben werden müssen. Dies sind ; die Adresse der Variable, welche in das Feld Pointer geschrieben werden muss, ; sowie der Typ der Variable, welcher im Feld Type mit einer der folgenden ; Konstanten angegeben werden muss: ; ; #FormatString_Byte ; #FormatString_UnsignedByte ; #FormatString_Ascii ; #FormatString_Character ; #FormatString_Word ; #FormatString_UnsignedWord ; #FormatString_Unicode ; #FormatString_Long ; #FormatString_UnsignedLong ; #FormatString_Integer ; #FormatString_UnsignedInteger ; #FormatString_Quad ; #FormatString_UnsignedQuad ; #FormatString_Float ; #FormatString_Double ; #FormatString_String ; ; Die Unsigned-Typen sollten dann angegeben werden, wenn eine eigentlich ; vorzeichenbehaftete Variable als vorzeichenlos behandelt werden soll. Dies ist ; dann empfehlenswert, wenn die Ausgabe hexadezimal oder binär erfolgen soll. ; ; Platzhalter ; =========== ; ; Ein Platzhalter ist in geschweifte Klammern eingefasst und setzt sich wie ; folgt zusammen: ; ; = "{}" ; ; ist der Index der Variable in *Variables\Var[], die an Stelle des ; Platzhalters eingefügt werden soll. ; ; enthält Anweisungen zur Formatierung. Der Aufbau von hängt ; vom Variablentyp ab und wird in den folgenden Abschnitten beschrieben. ; ; Zusätzlich zu der oben genannten Form sind noch die folgenden speziellen ; Platzhalter verfügbar: ; ; = "{}" ; Fügt eine öffnende geschweifte Klammer ein, welche nicht als Anfang eines ; Platzhalters interpretiert wird. ; ; = "{t}" ; Fügt ein Tabulator-Zeichen (TAB) ein. ; ; = "{n}" ; Fügt ein Zeilenvorschub-Zeichen (LF) ein. ; ; = "{r}" ; Fügt ein Wagenrücklauf-Zeichen (CR) ein. ; ; Format bei Ganzzahlen ; ===================== ; ; Wenn auf eine Variable vom Typ Byte, Word, Long, Integer oder Quad ; verweist, ist wie folgt aufgebaut: ; ; = "" ; ; Bei einer Variable des vorzeichenlosen Typs UnsignedByte, Ascii, Character, ; UnsignedWord, Unicode, UnsignedLong, UnsignedInteger oder UnsignedQuad ; entfällt : ; ; = "" ; ; = "" ; Wenn die Zahl nicht negativ ist, wird nichts vorangestellt, andernfalls wird ; ein Minuszeichen vorangestellt. ; ; = "-" ; Wenn die Zahl nicht negativ ist, wird ein Leerzeichen vorangestellt, ; andernfalls wird ein Minuszeichen vorangestellt. ; ; = "+" ; Wenn die Zahl nicht negativ ist, wird ein Pluszeichen vorangestellt, ; andernfalls wird ein Minuszeichen vorangestellt. ; ; = "" ; Die Zahl wird in dezimaler Form ausgegeben. ; ; = "h" ; Die Zahl wird in hexadezimaler Form ausgegeben. ; ; = "b" ; Die Zahl wird in binärer Form ausgegeben. ; ; = "" ; Die Zahl wird mit so vielen Stellen wie nötig ausgegeben. ; ; = "d" ; Wenn die Zahl weniger als Stellen hat, wird sie mit führenden ; Nullen verlängert, andernfalls wird nichts verändert. ; ; Format bei Gleitkommazahlen ; =========================== ; ; Wenn auf eine Variable vom Typ Float oder Double verweist, ist ; wie folgt aufgebaut: ; ; = "" ; ; = "" ; Wenn die Zahl nicht negativ ist, wird nichts vorangestellt, andernfalls wird ; ein Minuszeichen vorangestellt. ; ; = "-" ; Wenn die Zahl nicht negativ ist, wird ein Leerzeichen vorangestellt, ; andernfalls wird ein Minuszeichen vorangestellt. ; ; = "+" ; Wenn die Zahl nicht negativ ist, wird ein Pluszeichen vorangestellt, ; andernfalls wird ein Minuszeichen vorangestellt. ; ; = "" ; Die Zahl wird in normaler Schreibweise ausgegeben ("1.23"). ; ; = "e" ; Die Ausgabe erfolgt in Exponentialdarstellung ("1.23e4"). Wenn der Exponent ; gleich Null ist und ebenfalls gleich Null ist, wird die Ausgabe ; des Exponenten unterdrückt. Ist größer als Null, wird der Exponent ; immer ausgegeben. Ist größer als Null und der Exponent nicht ; negativ, so wird ihm ein Pluszeichen vorangestellt. Wenn der Exponent ; weniger als Stellen hat, wird er mit führenden Nullen verlängert, ; andernfalls wird nichts verändert. ; ; = "E" ; Wie vorher, jedoch mit Großbuchstabe ("1.23E4"). ; ; = "s" ; Die Zahl wird mit einem SI-Präfix ausgegeben ("1.23µ"). ; ; = "S" ; Wie vorher, jedoch mit Leerzeichen zwischen Zahl und SI-Präfix ("1.23 µ"). ; ; = "" ; Die Zahl wird mit so vielen Stellen wie nötig ausgegeben. ; ; = "d" ; Es werden signifikante Stellen ausgegeben, folgende Nullen nach ; dem Dezimaltrennzeichen werden entfernt. ; ; = "D" ; Es werden signifikante Stellen ausgegeben, folgende Nullen nach ; dem Dezimaltrennzeichen werden nicht entfernt. ; ; Format bei Strings ; ================== ; ; Wenn auf eine Variable vom Typ String verweist, ist wie folgt ; aufgebaut: ; ; = "" ; ; = "" ; Es wird nichts verändert. ; ; = "u" ; Der String wird in Großbuchstaben umgewandelt. ; ; = "l" ; Der String wird in Kleinbuchstaben umgewandelt. ; ; = "" ; Es wird nichts verändert. ; ; = "p" ; Wenn der String kürzer als Zeichen ist, wird der String durch ; Voranstellen des Zeichens verlängert, andernfalls wird nichts ; verändert. ; ; = "a" ; Wenn der String kürzer als Zeichen ist, wird der String durch ; Anhängen des Zeichens verlängert, andernfalls wird nichts ; verändert. ; ; Bekannte Probleme ; ================= ; ; Wird eine Variable vom Typ UnsignedQuad, deren Wert größer als der ; größtmögliche positive Wert eines Quads (9223372036854775807) ist, per ; = "d" mit führenden Nullen versehen, ist die Anzahl der ; vorangestellten Nullen eventuell falsch. ; ######################################## #FormatString_MaxVars = 16 Enumeration #FormatString_Byte #FormatString_Ascii #FormatString_Word #FormatString_Unicode #FormatString_Long #FormatString_UnsignedLong #FormatString_Quad #FormatString_UnsignedQuad #FormatString_Float #FormatString_Double #FormatString_String EndEnumeration #FormatString_UnsignedByte = #FormatString_Ascii #FormatString_UnsignedWord = #FormatString_Unicode CompilerSelect SizeOf(Character) CompilerCase SizeOf(Ascii) #FormatString_Character = #FormatString_Ascii CompilerCase SizeOf(Unicode) #FormatString_Character = #FormatString_Unicode CompilerDefault CompilerError "Character hat unzulässige Größe" CompilerEndSelect CompilerSelect SizeOf(Integer) CompilerCase SizeOf(Long) #FormatString_Integer = #FormatString_Long #FormatString_UnsignedInteger = #FormatString_UnsignedLong CompilerCase SizeOf(Quad) #FormatString_Integer = #FormatString_Quad #FormatString_UnsignedInteger = #FormatString_UnsignedQuad CompilerDefault CompilerError "Integer hat unzulässige Größe" CompilerEndSelect Structure FormatStringPointer StructureUnion Byte.b Ascii.a Word.w Unicode.u Long.l Quad.q Float.f Double.d EndStructureUnion EndStructure Structure FormatStringVariable Type.i *Pointer.FormatStringPointer EndStructure Structure FormatStringVariables Var.FormatStringVariable[#FormatString_MaxVars] EndStructure Procedure FS_GetExp(Double.d, Base.d = 10) If Double = 0 Or 1.0 * IsNAN(Double) Or 1.0 * IsInfinity(Double) ; Multiplikation mit 1.0 ist erforderlich, damit die Funktionen mit doppelter Genauigkeit arbeiten ProcedureReturn 0 EndIf ProcedureReturn 1.0 * Round(Log(Abs(Double))/Log(Base), #PB_Round_Down) EndProcedure Procedure.s FS_StrD(Double.d, Digits = 10, Trim = #True) Protected String$, *c.Character, l, l2, p Digits - (FS_GetExp(Double) + 1) If Digits < 0 Digits = 0 EndIf String$ = StrD(Double, Digits) If Not Trim ProcedureReturn String$ EndIf *c = @String$ While *c\c l + 1 If *c\c = '.' p = #True EndIf If (*c\c <> '0' And *c\c <> '.') Or Not p l2 = l EndIf *c + SizeOf(Character) Wend ProcedureReturn Left(String$, l2) EndProcedure Procedure.s FS_MakeStr(Length, Char.c) Protected String$ String$ = Space(Length) FillMemory(@String$, Length*SizeOf(Character), Char, #PB_Character) ProcedureReturn String$ EndProcedure Macro FS_IncPtr() *c + SizeOf(Character) : l - 1 EndMacro Macro FS_ReadInt(Var) Var = 0 While l > 0 And *c\c >= '0' And *c\c <= '9' Var * 10 : Var + *c\c - '0' FS_IncPtr() Wend EndMacro Procedure.s FS_Format(*c.Character, l, *Variables.FormatStringVariables) Protected Result$, Index, Double.d, Sign.c, Trim, Digits, Char.c, ExponentDigits, Exponent, Suffix$, Side.c, Length, Quad.q, Base, Unsigned ; Spezielle Platzhalter If l = 0 ProcedureReturn "{" EndIf Select *c\c Case 't' ProcedureReturn #TAB$ Case 'n' ProcedureReturn #LF$ Case 'r' ProcedureReturn #CR$ EndSelect FS_ReadInt(Index) If Index < #FormatString_MaxVars ; Index gültig If *Variables\Var[Index]\Pointer ; Zeiger gültig Select *Variables\Var[Index]\Type Case #FormatString_Byte, #FormatString_Ascii, #FormatString_Word, #FormatString_Unicode, #FormatString_Long, #FormatString_UnsignedLong, #FormatString_Quad, #FormatString_UnsignedQuad ; Ganzzahl Select *Variables\Var[Index]\Type Case #FormatString_Byte Quad = *Variables\Var[Index]\Pointer\Byte Case #FormatString_Ascii Quad = *Variables\Var[Index]\Pointer\Ascii Unsigned = #True Case #FormatString_Word Quad = *Variables\Var[Index]\Pointer\Word Case #FormatString_Unicode Quad = *Variables\Var[Index]\Pointer\Unicode Unsigned = #True Case #FormatString_Long Quad = *Variables\Var[Index]\Pointer\Long Case #FormatString_UnsignedLong Quad = *Variables\Var[Index]\Pointer\Long & $FFFFFFFF Unsigned = #True Case #FormatString_Quad Quad = *Variables\Var[Index]\Pointer\Quad Case #FormatString_UnsignedQuad Quad = *Variables\Var[Index]\Pointer\Quad & $FFFFFFFFFFFFFFFF Unsigned = #True EndSelect ; Vorzeichen If Not Unsigned If l > 0 And (*c\c = '-' Or *c\c = '+') Sign = *c\c FS_IncPtr() EndIf If Quad < 0 Result$ + "-" Quad = -Quad ElseIf Sign = '-' Result$ + " " ElseIf Sign = '+' Result$ + "+" EndIf EndIf ; Darstellung If l > 0 And *c\c = 'h' ; Hexadezimal FS_IncPtr() Suffix$ = Hex(Quad) Base = 16 ElseIf l > 0 And *c\c = 'b' ; Binär FS_IncPtr() Suffix$ = Bin(Quad) Base = 2 Else ; Dezimal Suffix$ = StrU(Quad) Base = 10 EndIf ; Länge If l > 0 And *c\c = 'd' FS_IncPtr() FS_ReadInt(Digits) Length = Digits - (FS_GetExp(Quad, Base) + 1) If Length > 0 Result$ + FS_MakeStr(Length, '0') EndIf EndIf Result$ + Suffix$ Case #FormatString_Float, #FormatString_Double ; Gleitkommazahl Select *Variables\Var[Index]\Type Case #FormatString_Float Double = *Variables\Var[Index]\Pointer\Float Case #FormatString_Double Double = *Variables\Var[Index]\Pointer\Double EndSelect ; Vorzeichen If l > 0 And (*c\c = '-' Or *c\c = '+') Sign = *c\c FS_IncPtr() EndIf If Double < 0 Result$ + "-" Double = -Double ElseIf Sign = '-' Result$ + " " ElseIf Sign = '+' Result$ + "+" EndIf ; Darstellung If l > 0 And (*c\c = 'e' Or *c\c = 'E') ; Exponential Char = *c\c FS_IncPtr() FS_ReadInt(ExponentDigits) Exponent = FS_GetExp(Double) Double * Pow(10, -Exponent) If Exponent <> 0 Or ExponentDigits > 0 Suffix$ + Chr(Char) If ExponentDigits > 0 If Exponent < 0 Suffix$ + "-" Exponent = -Exponent Else Suffix$ + "+" EndIf Length = ExponentDigits - (FS_GetExp(Exponent) + 1) If Length > 0 Suffix$ + FS_MakeStr(Length, '0') EndIf EndIf Suffix$ + Str(Exponent) EndIf ElseIf l > 0 And (*c\c = 's' Or *c\c = 'S') ; SI-Präfix If *c\c = 'S' Suffix$ + " " EndIf FS_IncPtr() Select Round(FS_GetExp(Double)/3, #PB_Round_Down) Case 0 Case 1 Double * 1e-3 Suffix$ + "k" Case -1 Double * 1e3 Suffix$ + "m" Case 2 Double * 1e-6 Suffix$ + "M" Case -2 Double * 1e6 Suffix$ + "µ" Case 3 Double * 1e-9 Suffix$ + "G" Case -3 Double * 1e9 Suffix$ + "n" Case 4 Double * 1e-12 Suffix$ + "T" Case -4 Double * 1e12 Suffix$ + "p" Case 5 Double * 1e-15 Suffix$ + "P" Case -5 Double * 1e15 Suffix$ + "f" Case 6 Double * 1e-18 Suffix$ + "E" Case -6 Double * 1e18 Suffix$ + "a" Case 7 Double * 1e-21 Suffix$ + "Z" Case -7 Double * 1e21 Suffix$ + "z" Case 8 Double * 1e-24 Suffix$ + "Y" Case -8 Double * 1e24 Suffix$ + "y" EndSelect EndIf ; Länge If l > 0 And (*c\c = 'd' Or *c\c = 'D') If *c\c = 'd' Trim = #True EndIf FS_IncPtr() FS_ReadInt(Digits) Else Trim = #True Select *Variables\Var[Index]\Type Case #FormatString_Float Digits = 6 Case #FormatString_Double Digits = 15 EndSelect EndIf Result$ + FS_StrD(Double, Digits, Trim) + Suffix$ Case #FormatString_String ; String Result$ = PeekS(*Variables\Var[Index]\Pointer) ; Umwandlung If l > 0 And *c\c = 'u' ; Großbuchstaben FS_IncPtr() Result$ = UCase(Result$) ElseIf l > 0 And *c\c = 'l' ; Kleinbuchstaben FS_IncPtr() Result$ = LCase(Result$) EndIf ; Verlängerung If l >= 2 And (*c\c = 'p' Or *c\c = 'a') Side = *c\c FS_IncPtr() Char = *c\c FS_IncPtr() FS_ReadInt(Length) Length - Len(Result$) If Length > 0 Select Side Case 'p' ; Voranstellen Result$ = FS_MakeStr(Length, Char) + Result$ Case 'a' ; Anhängen Result$ + FS_MakeStr(Length, Char) EndSelect EndIf EndIf EndSelect EndIf EndIf ProcedureReturn Result$ EndProcedure Procedure.s FormatString(String$, *Variables.FormatStringVariables) Protected Result$, *c.Character, *s, l, Opened *c = @String$ *s = *c While *c\c If *c\c = '{' And Not Opened Result$ + PeekS(*s, l) *c + SizeOf(Character) *s = *c l = 0 Opened = #True ElseIf *c\c = '}' And Opened Result$ + FS_Format(*s, l, *Variables) *c + SizeOf(Character) *s = *c l = 0 Opened = #False Else *c + SizeOf(Character) l + 1 EndIf Wend If Not Opened Result$ + PeekS(*s, l) EndIf ProcedureReturn Result$ EndProcedure ; ; Beispiele ; w.w = 31337 ; q.q = 12370788598393192175 ; f.f = 0.00012345 ; d.d = -#PI ; s$ = "Nils" ; With FSV.FormatStringVariables ; \Var[0]\Type = #FormatString_Word ; \Var[0]\Pointer = @w ; \Var[1]\Type = #FormatString_UnsignedQuad ; \Var[1]\Pointer = @q ; \Var[2]\Type = #FormatString_Float ; \Var[2]\Pointer = @f ; \Var[3]\Type = #FormatString_Double ; \Var[3]\Pointer = @d ; \Var[4]\Type = #FormatString_String ; \Var[4]\Pointer = @s$ ; EndWith ; Debug FormatString("{0}", @FSV) ; Debug FormatString("{0-}", @FSV) ; Debug FormatString("{0+}", @FSV) ; Debug FormatString("{0h}", @FSV) ; Debug FormatString("{0b}", @FSV) ; Debug FormatString("{0d2}", @FSV) ; Debug FormatString("{0d10}", @FSV) ; Debug FormatString("{1}", @FSV) ; Debug FormatString("{1h}", @FSV) ; Debug FormatString("{2}", @FSV) ; Debug FormatString("{2-}", @FSV) ; Debug FormatString("{2+}", @FSV) ; Debug FormatString("{2e0}", @FSV) ; Debug FormatString("{2E0}", @FSV) ; Debug FormatString("{2e3}", @FSV) ; Debug FormatString("{2s}", @FSV) ; Debug FormatString("{2S}", @FSV) ; Debug FormatString("{2d3}", @FSV) ; Debug FormatString("{2d7}", @FSV) ; Debug FormatString("{2D3}", @FSV) ; Debug FormatString("{2D7}", @FSV) ; Debug FormatString("{2+e3D7}", @FSV) ; Debug FormatString("{3}", @FSV) ; Debug FormatString("{3-}", @FSV) ; Debug FormatString("{3+}", @FSV) ; Debug FormatString("{4}", @FSV) ; Debug FormatString("{4u}", @FSV) ; Debug FormatString("{4l}", @FSV) ; Debug FormatString("{4p.10}", @FSV) ; Debug FormatString("{4a!10}", @FSV) ; Debug FormatString("Mein Name ist {4}, ich bin {0} Jahre alt.", @FSV) ; ; Geschwindigkeitsmessung, Debugger abschalten! ; d.d = -12345.9876 ; With FSV.FormatStringVariables ; \Var[0]\Type = #FormatString_Double ; \Var[0]\Pointer = @d ; EndWith ; QueryPerformanceFrequency_(@freq.q) ; For n.i = 0 To 249999 ; QueryPerformanceCounter_(@start.q) ; s$ = FormatString("{0+e3D7}", @FSV) ; QueryPerformanceCounter_(@stop.q) ; time.q + (stop - start) ; Next ; d = time / (n * freq) ; d2.d = 1 / d ; With FSV ; \Var[0]\Type = #FormatString_Double ; \Var[0]\Pointer = @d ; \Var[1]\Type = #FormatString_Double ; \Var[1]\Pointer = @d2 ; \Var[2]\Type = #FormatString_Integer ; \Var[2]\Pointer = @n ; \Var[3]\Type = #FormatString_String ; \Var[3]\Pointer = @s$ ; EndWith ; MessageRequester("", FormatString("{0Sd3}s pro Durchlauf.{n}{1d3} Durchläufe pro Sekunde.{n}Gemessen über {2} Durchläufe.{n}Ausgabe: {3}.", @FSV)) ; IDE Options = PureBasic 5.10 (Windows - x86) ; Folding = -- ; EnableXP