7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VBAでsprintfしたい

Last updated at Posted at 2018-04-27

初投稿です

はじめに

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

とりあえず「VBA sprintf」とかで検索してみるとだいたいこんな結果がでてくる

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

 

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

 

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

仕様

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

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

実装する指定子はこちら

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

期待される出力

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を一文字ずつ走査する

まあこれが出来ないとお話にならんわな

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. "%"を検出する

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

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を使うことで後々拡張できるようにします

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

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

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

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

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

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

4. 実際にPrmaryから置換

<整数>とかってしてたとこに上手く配列を入れてあげれば良さそうですね!
検出回数をカウントする変数を新たに用意します

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

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

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

つづく

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?