たまたま見かけた質問掲示板で、おもしろそうな質問がありました。
セルに「この商品は1000円です」のような文字列が入力されています。
こうした文字列内の数値にカンマを付けて「この商品は1,000円です」
のように変換するにはどうしたらいいでしょう?(岐阜県42才自営業)
その掲示板では力技のマクロによる回答がありましたが、数値の桁数に制限があるなどスマートではありませんでした。ちなみに「この商品は」や「円です」などの文字列は不定です。さあ、どうしましょう?一緒に考えてみませんか?ネタバレを防止するために、当ページの下の方に私が考えた方法を書いておきます。
(1)文字列内に数値が1回しか登場しない場合のやり方→犬の後ろ
(2)文字列内に数値が2回以上登場する場合のやり方→猫の後ろ

文字列内に数値が1回しか登場しない場合は、けっこう簡単です。ここでのポイントは、Val関数の特性と、Like演算子です。なお、Like演算子についての詳細な解説は「正規表現のようなマッチング」をご覧ください。
Val関数は、引数に指定した文字列を数値に変換する関数です。ただし、VBAでは自動的に型キャストが行われるため、一般的には意識する必要がありません。Val関数を使う機会はそう多くないのですが、実はVal関数には非常に便利な特性があるんです。それは「文字列の先頭から数字と認識される部分までを変換してくれる」という特性です。
つまり、こういうことです。
Val("1000") では、もちろん 1000 が返ります。さらに、
Val("1000円だぴょ~ん") のように、数値+文字列 を引数に渡すと、数値部分だけを数値に変換して 1000 を返します。
これを知ってると意外なときに役立ちます。たとえば「123」や「123円」「123ドル」などが混在するデータを純粋な数値に変換する場合、If Right(buf,1) = "円" なんてやらなくていいんです。Val(buf)とすれば、数値の後ろにある文字列を除去できちゃうんです。便利ですね~(^_^)
話を元に戻します。
「この商品は1000円です」を「この商品は1,000円です」に変換するには、次のように考えました。
ということです。
とりあえずロジックをコード化してみましょう。ここでは、文字列がセルA1に入力されているとします。
Sub Sample1()
Dim buf As String, RightStr As String, i As Long
buf = Range("A1")
''変数bufを先頭から1文字ずつチェックする
''もし任意の数値だったら、そこから後ろを変数RightStrに格納する
''Val(RightStr)で数値部分を抜き出す
''Val(RightStr)を、Format関数で整形した書式に置き換える
End Sub
変数bufを(文字数分だけ)先頭から1文字ずつチェックするのだから・・・
Sub Sample1()
Dim buf As String, RightStr As String, i As Long
buf = Range("A1")
For i = 1 To Len(buf)
If Mid(buf, i, 1) が 任意の数値
''もし任意の数値だったら、そこから後ろを変数RightStrに格納する
End If
Next i
''Val(RightStr)で数値部分を抜き出す
''Val(RightStr)を、Format関数で整形した書式に置き換える
End Sub
任意の数値かどうかはLike演算子を使って・・・
Sub Sample1()
Dim buf As String, RightStr As String, i As Long
buf = Range("A1")
For i = 1 To Len(buf)
If Mid(buf, i, 1) Like "#" Then
RightStr = Mid(buf, i)
Exit For
End If
Next i
''Val(RightStr)で数値部分を抜き出す
''Val(RightStr)を、Format関数で整形した書式に置き換える
End Sub
数値の抜き出しと、整形と、置換は一気にできそうだな・・・
Sub Sample1()
Dim buf As String, RightStr As String, i As Long
buf = Range("A1")
For i = 1 To Len(buf)
If Mid(buf, i, 1) Like "#" Then
RightStr = Mid(buf, i)
Exit For
End If
Next i
Range("B1") = Replace(buf, Val(RightStr), Format(Val(RightStr), "#,###"))
End Sub
変換した結果は、隣のセルB1に出力しました。これで完成です。
さてさて・・・では、文字列内に数値が2回以上登場する場合はどうでしょう。
その場合は→猫の後ろ
ページの先頭に戻るときは、Homeキーを押してください。

「この商品は1000円(税込み1050円)です」のようなケースです。この「1000」と「1050」をどちらも「1,000」「1,050」に変換したいんです。上述の方法でやろうとすると混乱してきます。数値がいくつ登場するかわからないからです。こんなときは、正規表現の出番でしょう。正規表現についての詳細な解説「正規表現によるマッチング」をご覧ください。
正規表現を使って、文字列内に存在する、数値が連続する部分を、すべて取り出せれば何とかなりそうです。
Sub Sample2()
Dim buf As String, RE, reMatch, reValue
Set RE = CreateObject("VBScript.RegExp")
buf = Range("A2")
With RE
.Pattern = 数値が1回以上連続するブロック
.Global = True ''←文字列内をすべて検索するオプション(この場合は必須)
Set reMatch = .Execute(buf)
''もし見つかったら
''見つかったすべての数値を取り出して
''Format関数で整形して
''Replace関数で置換する
End With
Set RE = Nothing
Range("B2") = buf
End Sub
RegExpオブジェクトでは「\d」が任意の数値を表します。また「+」は直前パターンの1回以上の繰り返しですから、「\d+」とすることで「1」や「1234」や「123456789012…」を検索することができます。
Executeメソッドで検索が成功すると、1個以上のMatcheオブジェクトを含むMatchesコレクションが返ります。Matches.Countが0より大きかったら、見つかったことになりますね。
Sub Sample2()
Dim buf As String, RE, reMatch, reValue
Set RE = CreateObject("VBScript.RegExp")
buf = Range("A2")
With RE
.Pattern = "\d+"
.Global = True
Set reMatch = .Execute(buf)
If reMatch.Count > 0 Then
''見つかったすべての数値を取り出して
''Format関数で整形して
''Replace関数で置換する
End If
End With
Set RE = Nothing
Range("B2") = buf
End Sub
Matchesコレクションのメンバを1つずつ処理するにはFor Eachステートメントがいいでしょう。
Sub Sample2()
Dim buf As String, RE, reMatch, reValue
Set RE = CreateObject("VBScript.RegExp")
buf = Range("A2")
With RE
.Pattern = "\d+"
.Global = True
Set reMatch = .Execute(buf)
If reMatch.Count > 0 Then
For Each reValue In reMatch
''Format関数で整形して
''Replace関数で置換する
Next reValue
End If
End With
Set RE = Nothing
Range("B2") = buf
End Sub
整形と置換部分は、先の「1回しか登場しない場合」と同じですから・・・
Sub Sample2()
Dim buf As String, RE, reMatch, reValue
Set RE = CreateObject("VBScript.RegExp")
buf = Range("A2")
With RE
.Pattern = "\d+"
.Global = True
Set reMatch = .Execute(buf)
If reMatch.Count > 0 Then
For Each reValue In reMatch
buf = Replace(buf, reValue, Format(reValue, "#,###"))
Next reValue
End If
End With
Set RE = Nothing
Range("B2") = buf
End Sub
となります。
どちらも、テストした結果はOKでした(^_^)