Skip to content

Instantly share code, notes, and snippets.

@IISResetMe
Created November 21, 2025 00:46
Show Gist options
  • Select an option

  • Save IISResetMe/e24b6e17b6e46e3ce8e3aa94d45ae2f0 to your computer and use it in GitHub Desktop.

Select an option

Save IISResetMe/e24b6e17b6e46e3ce8e3aa94d45ae2f0 to your computer and use it in GitHub Desktop.
String.Format emulator
##
## regex-based String.Format emulator
##
## In order to better understand how String.Format (or it's corresponding PowerShell operator `-f`), it might be useful to
## attempt to implement it from (relative) scratch.
##
## String.Format composite templates consist of verbatim string contents interspersed with the following macro syntax:
##
## "{" <index>["," <width>][":" <formatString>] "}"
##
## Where:
## - <index> is a non-negative integer describing the index of the $item value in the argument list
## - <width> is an integer describing the minimum length of the resulting formatted substring, negative for right-padding, positive for left-padding
## - <formatString> is a string describing the formatting parameters to be passed to ([IFormattable]$item).ToString
##
## see https://learn.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting#composite-format-string
##
## The implementation in the dotnet runtime uses a low-overhead char scanner to parse template strings, but we
## can emulate the correct parsing and substition behavior using Regex.Replace in PowerShell.
##
function Format-StringCustom {
param(
[Parameter(Mandatory, Position = 0)]
[string]$Format,
[Parameter(ValueFromRemainingArguments, Position = 1)]
[Alias('ArgumentList')]
[psobject[]]$Items,
[System.IFormatProvider]$Provider = $($null)
)
$formatSpecifierPattern = @'
(?x)
# short form:
# $formatSpecifierPattern = '\{(?<index>\d+)(?:,(?<width>-?\d+))?(?::(?<formatString>[^\}]+))?\}'
\{ # literal left curly
(?<index> # named capture group "index"
[0-9]+ ) # ... consisting of one or more digits
(?: # non-capturing group #1
, # literal comma
(?<width> # named capture group "width"
-?[0-9]+ ) # ... consisting of optional hyphen followed by one or more digits
)? # group #1 is optional
(?: # non-capturing group #2
: # literal colon
(?<formatString> # named capture group "formatString"
[^\}]+ ) # ... consisting of 1 or more of any character that is NOT a right curly
)? # group #2 is optional
\} # literal right curly
'@
# use the regex pattern describe above to replace all `{index[,width[:formatString]]}` substrings, resolve formatted item in match evaluator
return [regex]::Replace($format, $formatSpecifierPattern, [System.Text.RegularExpressions.MatchEvaluator] {
param(
[System.Text.RegularExpressions.Match]
$Match
)
$groups = $Match.Groups
# grab index value to resolve item in argument list
$indexValue = +$groups['index'].Value
if ($indexValue -ge $items.Count) {
throw [System.FormatException]::new("Index '${indexValue}' exceeds item list count")
}
$itemValue = $items[$indexValue]
# apply formatString if applicable, otherwise fallback to default string representation
if ($groups['formatString'].Success -and $itemValue -is [System.IFormattable]) {
$string = $itemValue.ToString($groups['formatString'].Value, $Provider)
}
else {
$string = "$itemValue"
}
# check for width specifier, pad if applicable
if ($groups['width'].Success) {
# calculate absolute min-width from capture group value
$absWidth = [System.Math]::Abs($groups['width'].Value)
# negative width? right-pad!
if ($groups['width'].Value -like '-*') {
$string = $string.PadRight($absWidth)
}
else {
$string = $string.PadLeft($absWidth)
}
}
return $string
})
}
# demo class implementing IFormattable
class FormattableClass : IFormattable {
hidden [string]$__value
FormattableClass([string]$value) {
$this.__value = $value
}
[string]
ToString([string]$format) { return $this.ToString($format, $null) }
[string]
ToString([string]$format, [IFormatProvider]$provider) {
if ($format -ceq 'U') { return $this.__value.ToUpper() }
if ($format -ceq 'L') { return $this.__value.ToLower() }
return $this.__value
}
}
# demo code
$h = [FormattableClass]'Hello, World!'
Write-Host (Format-StringCustom "{0,18:U}`n{0,-18:L}" $h)
##
## outputs:
##
## >>> HELLO, WORLD!<<<
## >>>hello, world! <<<
##
## Compare with String.Format:
##
## "{0,18:U}`n{0,-18:L}" -f $h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment