Samples Gallery & Reference > Utilities

Format$ update by RhoSigma

(1/1)

bplus:
Author: @RhoSigma
Source: qb64.org Forum
URL: https://www.qb64.org/forum/index.php?topic=2932.msg121898#msg121898
Version: 2021-01-02
Tag: [String Function]

Description: More than just a replacement for missing FORMAT$ function from QB of past, RhoSigma has added some features to make this function more flexible. Whether you want to format some numbers with commas or create a form letter, you should find this very well documented function a handy tool for your toolbox. HTML document included in the 7z attachment.


--- Quote --- Completely new are the preferences specifiers, which allow to customize the standard behavior of some PRINT USING format symbols and the bin/dec/hex/oct outputs. So you can set an alignment for strings in a fixed length field (\\), group bin/dec/hex/oct outputs in blocks with a certain amout of digits, replace the dollar sign for currency formatting and/or flip the usage of comma and dot as group/decimal separator.

--- End quote ---


--- Code: QB64: ---_TITLE "FormatExample"'=== Full description for the IndexFormat$() function is available'=== in the separate HTML document.'=====================================================================WIDTH , 30  '-- The following format templates need its arguments in different order,'-- no problem with indexing, no need to reorder the given arguments.'-- You may even use different formatting for the same argument, as long'-- as its types are compatible (ie. string vs. number).dateDE$ = "Date format Germany: 0{#}. 1{&} 2{####}" ' 1{} = full stringdateUS$ = "Date format US     : 1{\ \}/0{#} 2{####}" '1{} = first 3 chars only '-- The easiest way to pass a variable number of arguments, which may'-- even be of different types, to a user function is using a string.'-- All arguments will be concatenated in this string, separated by a'-- designated char/string which does not appear in the arguments itself.'-- Strings can be added as is, numbers can be added as literal strings'-- too, or in the form STR$(variable).year% = 2021argStr$ = "2|Januar|" + STR$(year%)'-- In this example the | is the argument separator. Use whatever is'-- suitable for your needs, maybe even a CHR$(0). '-- Now let's test the whole thing, we've different token orders in the'-- format templates, but use the same argument string for both calls.PRINT IndexFormat$(dateDE$, argStr$, "|")PRINT IndexFormat$(dateUS$, argStr$, "|")PRINT   '-- And here the examples from the function description, which also'-- shows the reuse of arguments without the need to pass more arguments'-- for the additional "feet" and "toes" format tokens.head = 1: hands = 2: fingers = 10PRINT USING "## head, ## hands and ## fingers"; head, hands, fingersPRINT USING "## fingers, ## head and ## hands"; head, hands, fingers argStr$ = STR$(head) + "|" + STR$(hands) + "|" + STR$(fingers)PRINT IndexFormat$("2{##} fingers, 0{##} head and 1{##} hands", argStr$, "|")PRINTPRINT IndexFormat$("0{##} head, 1{##} hands and 2{##} fingers, also 1{##} feet and 2{##} toes", argStr$, "|")PRINT   '-- The function can also handle escape sequences as known from C/C++,'-- so you may use those sequences within your format templates.PRINT IndexFormat$("Column-1\tColumn-2\tColumn-3\n0{#.##}\t\t1{#.##}\t\t2{#.##}", "1.11|2.22|3.33", "|")PRINTPRINT IndexFormat$("This is a \x220{&}\x22 section.", "quoted", "|")PRINT'-- Using escape sequences and the new bin/dec/hex/oct/real formatting,'-- while reusing the same argument for all tokens. Also showing the use'-- of preferences specifiers to group bin and hex outputs.PRINT IndexFormat$(" Bin: 0{?4:B16}\n Dec: 0{D}\n Hex: 0{?2:H8}\n Oct: 0{O}\nReal: 0{R}\n", "2021.00548", "|")   '-- Alignment of strings in a fixed length field, the square brackets are'-- just used to better visualize the field.PRINT IndexFormat$("[0{?L:\             \}]", "RhoSigma", "|")PRINT IndexFormat$("[0{?C:\             \}]", "RhoSigma", "|")PRINT IndexFormat$("[0{?R:\             \}]", "RhoSigma", "|")PRINT   '-- Finally a currency example with replaced dollar sign and flipped'-- comma/dot notation. I'd like to get that much for this function ;)PRINT IndexFormat$("Account balance: 0{?î,:**$#####,.##}", "12345.67", "|")  '-- doneEND     '--- Full description available in separate HTML document.'---------------------------------------------------------------------FUNCTION IndexFormat$ (fmt$, arg$, sep$)'--- option _explicit requirements ---DIM args$, shan&, dhan&, than&, idx%, cpos&, res$, lit%, tok%, ft$, cch$DIM och$, opos&, tmp$, fp$, tyl%, typ$, oval&&, temp~&&, curr%, high%'--- init ---args$ = arg$ 'avoid side effectsshan& = _SOURCE: dhan& = _DEST: than& = _NEWIMAGE(256, 1, 0)_SOURCE than&: _DEST than&REDIM argArr$(0 TO 35) 'all args empty'--- parse arguments ---IF RIGHT$(args$, LEN(sep$)) <> sep$ THEN args$ = args$ + sep$FOR idx% = 0 TO 35    cpos& = INSTR(args$, sep$): IF cpos& = 0 THEN EXIT FOR    argArr$(idx%) = LEFT$(args$, cpos& - 1)    args$ = MID$(args$, cpos& + LEN(sep$))NEXT idx%'--- process format template ---res$ = "": lit% = 0: tok% = 0: ft$ = "": idx% = -1FOR cpos& = 1 TO LEN(fmt$)    cch$ = MID$(fmt$, cpos&, 1)    IF cch$ = "_" AND lit% = 0 THEN 'take next \{} as literal        IF NOT tok% THEN lit% = -1    ELSEIF cch$ = "\" AND lit% = 0 AND tok% = 0 THEN 'insert esc sequence        IF cpos& < LEN(fmt$) THEN            SELECT CASE UCASE$(MID$(fmt$, cpos& + 1, 1))                CASE "A": och$ = CHR$(7) ' audio bell                CASE "B": och$ = CHR$(8) ' backspace                CASE "T": och$ = CHR$(9) ' tabulator                CASE "N": och$ = CHR$(10) 'line feed                CASE "V": och$ = CHR$(11) 'vertical tabulator                CASE "F": och$ = CHR$(12) 'form feed                CASE "R": och$ = CHR$(13) 'carriage return                CASE "E": och$ = CHR$(27) 'escape                CASE "0", "1", "2", "3" '  octal ASCII (3 digits)                    och$ = CHR$(VAL("&O" + MID$(fmt$, cpos& + 1, 3)))                    cpos& = cpos& + 2                CASE "X" '                 hex ASCII (x + 2 digits)                    och$ = CHR$(VAL("&H" + MID$(fmt$, cpos& + 2, 2)))                    cpos& = cpos& + 2                CASE ELSE: och$ = "" '     ignore unknowns            END SELECT            res$ = res$ + och$            cpos& = cpos& + 1: opos& = cpos&        END IF    ELSEIF cch$ = "{" AND lit% = 0 THEN 'begin of formatting token        IF idx% = -1 THEN            och$ = UCASE$(MID$(fmt$, cpos& - 1, 1)): tok% = -1            IF ((cpos& - 1) = opos&) OR ((och$ < "0" OR och$ > "9") AND (och$ < "A" OR och$ > "Z")) THEN och$ = "-"            IF och$ = "-" THEN och$ = "0": ELSE res$ = LEFT$(res$, LEN(res$) - 1)            IF och$ >= "A" THEN idx% = ASC(och$) - 55: ELSE idx% = VAL(och$)        END IF    ELSEIF cch$ = "}" AND lit% = 0 THEN 'end of formatting token        IF idx% >= 0 THEN            GOSUB doArgFormat: res$ = res$ + tmp$            tok% = 0: ft$ = "": idx% = -1        END IF    ELSE 'accumulate chars/symbols in correct channel        IF lit% AND INSTR("\{}", cch$) = 0 THEN cch$ = "_" + cch$        IF tok% THEN ft$ = ft$ + cch$: ELSE res$ = res$ + cch$        lit% = 0    END IFNEXT cpos&'--- cleanup & set result ---ERASE argArr$_SOURCE shan&: _DEST dhan&: _FREEIMAGE than&IndexFormat$ = res$EXIT FUNCTION'-----------------------------doArgFormat:CLS: tmp$ = "": fp$ = "": ft$ = LTRIM$(RTRIM$(ft$))IF LEFT$(ft$, 1) = "?" THEN    tyl% = INSTR(2, ft$, ":")    IF tyl% > 0 THEN fp$ = LEFT$(MID$(ft$, 2, tyl% - 2), 2): ft$ = LTRIM$(MID$(ft$, tyl% + 1)) 'extract format prefsEND IFIF ft$ = "" THEN RETURN 'empty token = empty formattedSELECT CASE UCASE$(LEFT$(ft$, 1))    CASE "!", "&", "\" 'regular string formatting        IF LEFT$(ft$, 1) = "\" THEN            tyl% = INSTR(2, ft$, "\"): IF tyl% = 0 THEN ft$ = "\" + ft$: tyl% = 2            IF LTRIM$(fp$) <> "" AND LEN(argArr$(idx%)) < tyl% THEN                SELECT CASE LEFT$(LTRIM$(fp$), 1)                    CASE "C", "c": tyl% = (tyl% - LEN(argArr$(idx%))) \ 2                    CASE "R", "r": tyl% = tyl% - LEN(argArr$(idx%))                    CASE ELSE: tyl% = 0 'L or Unknown is default (left)                END SELECT                argArr$(idx%) = SPACE$(tyl%) + argArr$(idx%)            END IF        END IF        PRINT USING ft$; argArr$(idx%);: fp$ = ""    CASE "B", "D", "H", "O", "R" 'extended number formatting (bin/dec/hex/oct/real)        typ$ = LEFT$(ft$, 1): tyl% = VAL(MID$(ft$, 2))        SELECT CASE typ$            CASE "B", "b": GOSUB doBinString            CASE "D", "d": tmp$ = LTRIM$(STR$(_ROUND(VAL(argArr$(idx%)))))            CASE "H", "h"                tmp$ = HEX$(VAL(argArr$(idx%)))                IF typ$ = "H" THEN tmp$ = UCASE$(tmp$): ELSE tmp$ = LCASE$(tmp$)            CASE "O", "o": tmp$ = OCT$(VAL(argArr$(idx%)))            CASE "R", "r": tmp$ = LTRIM$(STR$(VAL(argArr$(idx%)))): fp$ = ""        END SELECT        IF tyl% > 0 THEN 'adjust field length (if any)            IF LEN(tmp$) <= tyl% THEN                tmp$ = RIGHT$(STRING$(tyl%, "0") + tmp$, tyl%): idx% = INSTR(tmp$, "-")                IF idx% > 0 THEN                    typ$ = UCASE$(MID$(tmp$, idx% - 1, 1))                    IF typ$ <> "E" AND typ$ <> "D" THEN tmp$ = "-" + LEFT$(tmp$, idx% - 1) + MID$(tmp$, idx% + 1)                END IF            ELSE                tmp$ = "%" + tmp$            END IF        END IF        IF LTRIM$(fp$) <> "" THEN 'apply grouping (if any)            typ$ = "": tyl% = 0            FOR idx% = LEN(tmp$) TO 1 STEP -1                typ$ = MID$(tmp$, idx%, 1) + typ$: tyl% = tyl% + 1                IF tyl% = VAL(fp$) THEN typ$ = " " + typ$: tyl% = 0            NEXT idx%            tmp$ = LTRIM$(typ$): IF LEFT$(tmp$, 2) = "- " THEN tmp$ = "-" + MID$(tmp$, 3)        END IF        RETURN    CASE ELSE 'regular number formatting (or invalid nonsense)        IF INSTR(ft$, "**") = 0 AND INSTR(ft$, "$$") = 0 AND INSTR(ft$, "#") = 0 THEN            PRINT ft$; 'take nonsense as is        ELSE            PRINT USING ft$; VAL(argArr$(idx%));        END IFEND SELECTtyl% = INSTR(fp$, ","): IF tyl% > 0 THEN MID$(fp$, tyl%, 1) = " "fp$ = LTRIM$(RTRIM$(fp$))FOR idx% = 1 TO POS(0) - 1    typ$ = CHR$(SCREEN(1, idx%)): ft$ = typ$    IF fp$ <> "" AND typ$ = "$" THEN ft$ = fp$    IF tyl% > 0 AND typ$ = "," THEN ft$ = "."    IF tyl% > 0 AND typ$ = "." THEN ft$ = ","    tmp$ = tmp$ + ft$NEXT idx%RETURN'-----------------------------doBinString:oval&& = VAL(argArr$(idx%)): temp~&& = oval&&tmp$ = STRING$(64, "0"): curr% = 64: high% = 64DO    IF (temp~&& AND 1) THEN MID$(tmp$, curr%, 1) = "1": high% = curr%    curr% = curr% - 1: temp~&& = temp~&& \ 2LOOP UNTIL temp~&& = 0IF oval&& < 0 THEN    IF -oval&& < &H0080000000~&& THEN high% = 33    IF -oval&& < &H0000008000~&& THEN high% = 49    IF -oval&& < &H0000000080~&& THEN high% = 57END IFtmp$ = MID$(tmp$, high%)RETURNEND FUNCTION  

Navigation

[0] Message Index

Go to full version