> ## Content Index
> Fetch the complete content index at: https://nolongerset.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Printf Function
- URL: https://nolongerset.com/printf-function/
- Published: 2022-04-20T03:00:12.000Z
- Updated: 2026-05-08T12:58:11.000Z
- Description: User @wqweto's string interpolation VBA function has some neat tricks, like using Unicode's Private Use Area to get safe temporary placeholder characters.
- Author: Mike Wolfe
- Tags: Code Library, #StringFunctions, #Import 2026-05-20 02:59

VBA may not have native support for [string interpolation](https://en.wikipedia.org/wiki/String%5Finterpolation), but this deceptively clever function from Vladimir Vissoultchev will get you many of the same benefits.

> "In computer programming, **string interpolation** is the process of evaluating a [string literal](https://en.wikipedia.org/wiki/String%5Fliteral) containing one or more [placeholders](https://en.wikipedia.org/wiki/Form%5F%28document%29#Placeholders), yielding a result in which the placeholders are replaced with their corresponding values." -[Wikipedia](https://en.wikipedia.org/wiki/String%5Finterpolation)

Over on the twinBASIC GitHub issues project, someone [requested string interpolation](https://github.com/WaynePhillipsEA/twinbasic/discussions/791) as a language feature. Vladimir ([@wqweto](https://github.com/wqweto)) offered the following function–very loosely based on the [C printf function](https://www.tutorialspoint.com/c%5Fstandard%5Flibrary/c%5Ffunction%5Fprintf.htm)–as a workaround to provide similar functionality:

```vba
Public Function Printf(ByVal sText As String, ParamArray A() As Variant) As String
    Const LNG_PRIVATE   As Long = &HE1B6 '-- U+E000 to U+F8FF - Private Use Area (PUA)
    Dim lIdx            As Long
    
    For lIdx = UBound(A) To LBound(A) Step -1
        sText = Replace(sText, "%" & (lIdx - LBound(A) + 1), Replace(A(lIdx), "%", ChrW$(LNG_PRIVATE)))
    Next
    Printf = Replace(sText, ChrW$(LNG_PRIVATE), "%")
End Function
```

Vladimir included the following [brief explanation](https://github.com/WaynePhillipsEA/twinbasic/discussions/791#discussioncomment-2536359) with his code:

> The loop is backwards so that `%10` and `%11` do not get messed up by `%1` placeholders. This allows values to contain "%1" strings too that do not get replaced (are not treated as placeholders).

## Some Neat Tricks

One of the reasons I asked Vladimir if I could re-post his code is that he had several relatively advanced techniques packed into a small space, one of which that was completely new to me (the last one on the list below).

- `[ParamArray](https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/understanding-parameter-arrays)` for passing a variable number of arguments to a function
- `[UBound()](https://docs.microsoft.com/en-us/office/vba/Language/Reference/user-interface-help/ubound-function)`/[](https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/lbound-function)[LBound()](https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/lbound-function) for safely looping through an array (especially one where the bounds won't be known until runtime)
- `[Step -1](https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/using-fornext-statements)` for looping backwards through the `For` loop
- Using the [Unicode Private Use Area](https://en.wikipedia.org/wiki/Private%5FUse%5FAreas) (PUA) to provide a temporary replacement character that should never occur in a normal string

## Usage

To use the function, enter placeholders numbered `%1`, `%2`, `%3`, etc. inside your literal string. Then pass values that correspond with the numbered placeholders as additional arguments to the function. Some notes:

- There is no practical limit to the number of items you can replace (e.g., the function supports `%1` and `%10` in the same string)
- You can reuse a placeholder if it refers to the same value (e.g., `Printf("x: %1; y: %2; z: %3; x: %1", x, y, z)`)
- You can remove a placeholder from the literal string if you don't need it (e.g., `Printf("x: %1; z: %3", x, y, z)`)
- Your replacement values may contain percent signs

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2022/04/image-63.png)

You can reuse a placeholder if it refers to the same value.

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2022/04/image-64.png)

You can skip over a placeholder if you don't need it (e.g., `%2` in the above example).

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2022/04/image-62.png)

Nice function, wqweto!

## Caveats and Edge Cases

While the function uses some nice techniques, it's not foolproof.

Cristian Buse [pointed out](https://github.com/WaynePhillipsEA/twinbasic/discussions/791#discussioncomment-2566668) one edge case that the Printf function does not handle correctly:

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2022/04/image-65.png)

This would be an unusual situation, but that's why they call them edge cases. 

> "The only way to be sure there are no unexpected edge cases is to parse the mask. It can be done character by character or as a split." -Cristian Buse

## A Better `Printf` Function?

Here's [Cristian's refactored Printf function](https://github.com/WaynePhillipsEA/twinbasic/discussions/791#discussioncomment-2566668) that handles the above edge case correctly:

```vba
Public Function Printf(ByVal mask As String, ParamArray tokens() As Variant) As String
    Dim parts() As String: parts = Split(mask, "%")
    Dim i As Long
    Dim j As Long
    Dim isFound As Boolean
    Dim s As String
    '
    'Always ignore first part - covers if mask started or not with %
    For i = LBound(parts) + 1 To UBound(parts)
        If LenB(parts(i)) = 0 Then
            parts(i) = "%"
        Else
            isFound = False
            For j = UBound(tokens) To LBound(tokens) Step -1
                s = CStr(j + 1)
                If Left$(parts(i), Len(s)) = s Then
                    parts(i) = tokens(j) & Right$(parts(i), Len(parts(i)) - Len(s))
                    isFound = True
                    Exit For
                End If
            Next j
            If Not isFound Then
                parts(i) = "%" & parts(i)
            End If
        End If
    Next i
    Printf = Join(parts, vbNullString)
End Function
```

---

### External references

[String interpolation - Wikipedia![](https://en.wikipedia.org/static/apple-touch/wikipedia.png)Wikimedia Foundation, Inc.Contributors to Wikimedia projects![](https://en.wikipedia.org/static/images/footer/wikimedia-button.png)](https://en.wikipedia.org/wiki/String%5Finterpolation)

[Private Use Areas - Wikipedia![](https://en.wikipedia.org/static/apple-touch/wikipedia.png)Wikimedia Foundation, Inc.Contributors to Wikimedia projects![](https://upload.wikimedia.org/wikipedia/en/thumb/9/96/Symbol_category_class.svg/16px-Symbol_category_class.svg.png)](https://en.wikipedia.org/wiki/Private%5FUse%5FAreas)

*Image by [PublicDomainPictures](https://pixabay.com/users/publicdomainpictures-14/?utm%5Fsource=link-attribution&utm%5Fmedium=referral&utm%5Fcampaign=image&utm%5Fcontent=19858) from [Pixabay](https://pixabay.com/?utm%5Fsource=link-attribution&utm%5Fmedium=referral&utm%5Fcampaign=image&utm%5Fcontent=19858)*