VBAでsprintfしたい

初投稿です
# はじめに

やっぱVBAにCのsprintfほしいよね

とりあえず[「VBA sprintf」とかで検索してみる](https://www.google.co.jp/search?q=VBA+sprintf "Google先生に訊く")とだいたいこんな結果がでてくる

>**Format 関数を使う**
>　-> 変数1つしか変換できないのでダメ

　
>**System.Text.StringBuilder を使う**
>　-> 可変個扱えるから勝ちか？と思ったが記法がまるで謎なのでクソ

　
>**自分で実装する**
>　-> や　っ　ぱ　こ　れ　だ　ね
>　　 先駆者はあちこちにいるみたいだけど、ちょっと貧弱だったりそもそも使い方が違ったりなので自分で実装したいところまで実装してみような

# 仕様
こんな形式の関数をつくる

```vb
Public Function sprintf(fmt As String, ParamArray Prmary()) As String
```

実装する指定子はこちら

| 種類 | 説明 |
|:-:|:-:|
| %d | 整数 |
| %f | 小数 |
| %s | 文字列 |
| %% | "%"という文字 |

期待される出力

```vb
Debug.Print sprintf("今日は%d月%d日、%sです",Month(Date),Day(Date),WeekdayName(Weekday(Date)))
' -> (例) 今日は4月21日、土曜日です

Debug.Print sprintf("%f %d",3.14,3.14)
' -> 3.14 3

Debug.Print sprintf("%d%%オフ",40)
' -> 40%オフ

```

全くいい例が思いつかなかった　ごめん


んじゃ組んでみましょか
順を追ってやってくから分かりやすいと思うよ　知らんけど

# 実装
## 1. fmtを一文字ずつ走査する
まあこれが出来ないとお話にならんわな

```vb
Public Function sprintf(fmt As String, ParamArray Prmary()) As String

Dim i As Long: i = 1
Dim Result As String

Do Until i > Len(fmt)
    Result = Result & Mid(fmt, i, 1)
    i = i + 1
Loop

sprintf = Result
End Function
```

後々のことを考えてFor文ではなくUntil文を採用　柔軟に走査位置を変えたいからね
`Mid(fmt, i, 1)`で、i文字目を取り出せる

私はVBAで関数を書く時はResultという変数を用意して、最後に関数名変数に代入するようにしています　多分やり方は人それぞれだろうから無理に推しはしない

## 2. "%"を検出する
とりあえず今は、見つけたら"!"に置換(文字"%"の代わりに"!"を追加)するようにしてみます

```vb
Public Function sprintf(fmt As String, ParamArray Prmary()) As String

Dim i As Long: i = 1
Dim Result As String

Do Until i > Len(fmt)
    If Mid(fmt, i, 1) = "%" Then
        Result = Result & "!"
    Else
        Result = Result & Mid(fmt, i, 1)
    End If
    i = i + 1
Loop

sprintf = Result
End Function
```

## 3. "%"の後ろの文字で処理を分岐する
走査位置を一つ後ろにずらして、dとかsとかか判定します
`Select Case`を使うことで後々拡張できるようにします

```vb
Public Function sprintf(fmt As String, ParamArray Prmary()) As String

Dim i As Long: i = 1
Dim Result As String

Do Until i > Len(fmt)
    If Mid(fmt, i, 1) = "%" Then
        i = i + 1 '%の次の文字に進めておく
        Select Case Mid(fmt, i, 1)
            Case "d":
                Result = Result & "<整数>"
            Case "f":
                Result = Result & "<小数>"
            Case "s":
                Result = Result & "<文字列>"
            Case "%":
                Result = Result & "%"
            Case Else:
                Debug.Print "無効な識別子" 'とりあえずこんな対応にしておく　無効な識別子は上手く無視してくれます
        End Select
    Else
        Result = Result & Mid(fmt, i, 1)
    End If
    i = i + 1
Loop

sprintf = Result
End Function
```

このへんで少し実行例を載せてみましょうかね

```vb
Debug.Print sprintf("dd%dd")
' -> dd<整数>d

Debug.Print sprintf("%f%d%%%s")
' -> <小数><整数>%<文字列>

Debug.Print sprintf("%a%b%c%d")
' -> (複数行)
' 無効な識別子
' 無効な識別子
' 無効な識別子
' <整数>
```

なんかいけそうな感じしてきたでしょ？

## 4. 実際にPrmaryから置換
`<整数>`とかってしてたとこに上手く配列を入れてあげれば良さそうですね！
検出回数をカウントする変数を新たに用意します

```vb
Public Function sprintf(fmt As String, ParamArray Prmary()) As String

Dim i As Long: i = 1
Dim j As Long: j = LBound(Prmary) 'これね
Dim Result As String

Do Until i > Len(fmt)
    If Mid(fmt, i, 1) = "%" Then
        i = i + 1
        Select Case Mid(fmt, i, 1)
            Case "d":
                Result = Result & CInt(Prmary(j))
                j = j + 1
            Case "f":
                Result = Result & CDbl(Prmary(j))
                j = j + 1
            Case "s":
                Result = Result & CStr(Prmary(j))
                j = j + 1
            Case "%":
                Result = Result & "%"
            Case Else:
                Debug.Print "無効な識別子"
        End Select
    Else
        Result = Result & Mid(fmt, i, 1)
    End If
    i = i + 1
Loop

sprintf = Result
End Function
```

おおおおおお動いたああああああああ！！！！！！！！！
あとはお好みでフォーマット増やすなりなんなりしてみてください

以上です　ありがとうございました

<font color="whitesmoke">つづく</font>