和風スパゲティのレシピ

日本語でコーディングするExcelVBA

文字列操作の汎用関数集

文字列操作でよく出てくる処理の、汎用関数集を紹介します。

例えば「愛媛産みかんから愛媛(産より左側)を取り出す」ときに、

産地 = Left("愛媛産みかん", Instr("愛媛産みかん", "産") -1)
' ⇩ 汎用関数を用意しておけばこう書けるようになる
産地 = Left文字列まで("愛媛産みかん", "産")

このように、よくある処理をいちいち書かなくて済む(+読みやすくする)ための関数集です。

実行結果のサンプルをセットで置いておきますので、
関数作成の参考にするなり、中身を見ずに使うなり、
お好きなようにお使いください。


ちなみに「Left文字列まで」のように、なるべく英語始まりになるように無理やり関数名を付けているのは、

Leftの選択肢

↑こんな風に既存の関数に仲間入りさせて、選んで入力したいからです。
Ctrl+Spaceでこの選択肢を出せますので、知らなかった方はこの機会に是非覚えておきましょう。

汎用関数によく用いる機能

本ページの汎用関数には、以下の機能・関数をよく使っています。

◇ IIf関数
分岐を一行にまとめて「IIf(判定, Trueの時, Falseの時)」と書ける関数。
要はワークシート関数のIFと同じもの

◇ Optional キーワード
省略可能の引数を設定する機能。


詳しい解説を見たい方はリンク先の解説ページをご覧ください。


では実際の関数に入ります。

左(先頭)から特定の文字列が登場するまで

呼出 結果
Left文字列まで("愛媛産みかん", "産") 愛媛
Left文字列まで("愛媛産みかん", "産", True) 愛媛産
Left文字列まで("愛媛みかん", "産")  
Function Left文字列まで(ByVal 元テキスト As String, ByVal 検索値 As String _
    , Optional is検索値を結果に含む As Boolean = False) As String
    
    Dim instr位置 As Long: instr位置 = InStr(元テキスト, 検索値)
    If instr位置 > 0 Then
        Left文字列まで = Left(元テキスト, instr位置 - 1) & IIf(is検索値を結果に含む, 検索値, "")
    End If
    
End Function

※ 検索値が見つからなかった場合は""を返します。

右(後尾)から特定の文字列が登場するまで

呼出 結果
Right文字列まで("愛媛産みかん", "産") みかん
Right文字列まで("愛媛産みかん", "産", True) 産みかん
Right文字列まで("愛媛みかん", "産")  
Function Right文字列まで(ByVal 元テキスト As String, ByVal 検索値 As String _
    , Optional is検索値を結果に含む As Boolean = False) As String
    
    Dim instrRev位置 As Long: instrRev位置 = InStrRev(元テキスト, 検索値)
    If instrRev位置 > 0 Then
        Right文字列まで = IIf(is検索値を結果に含む, 検索値, "") _
                                          & Mid(元テキスト, instrRev位置 + Len(検索値))
    End If
    
End Function

※ 検索値が見つからなかった場合は""を返します。
※ 検索値が複数ある場合は一番右の位置を採用します。

呼出 結果
Right文字列まで("2021.01.02", ".", ) 02

特定の文字列から特定の文字列まで

呼出 結果
Mid文字列から文字列("みかん(愛媛産)", "(", ")") 愛媛産
Mid文字列から文字列("みかん(愛媛産)", "(", ")", True, True) (愛媛産)
Mid文字列から文字列("みかん「愛媛産」", "(", ")")
Function Mid文字列から文字列(ByVal 元テキスト As String, ByVal 前検索値 As String, ByVal 後検索値 As String _
    , Optional is前検索値を結果に含む As Boolean = False, Optional is後検索値を結果に含む As Boolean = False) As String
    
    Dim instr前値 As Long: instr前値 = InStr(元テキスト, 前検索値)
    If instr前値 = 0 Then Exit Function
    
    Dim instr後値 As Long: instr後値 = InStr(instr前値 + Len(前検索値) + 1, 元テキスト, 後検索値)
    If instr後値 = 0 Then Exit Function
    
    Mid文字列から文字列 = _
        IIf(is前検索値を結果に含む, 前検索値, "") & _
        Mid(元テキスト, instr前値 + Len(前検索値), instr後値 - instr前値 - Len(前検索値)) & _
        IIf(is後検索値を結果に含む, 後検索値, "")
        
End Function

※ いずれかの検索値が見つからなかった場合は""を返します。
※ 検索値は左から探し、見つかった場所から次の検索値を探します。

呼出 結果
Mid文字列から文字列("(みかん)(愛媛産)", "(", ")") みかん

特定の文字列から特定の文字列までを削除

呼出 結果
Mid文字列から文字列を削除("みかん(愛媛産)", "(", ")") みかん
Mid文字列から文字列を削除("みかん(愛媛産)", "(", ")", False, False) みかん()
Mid文字列から文字列を削除("みかん「愛媛産」", "(", ")") みかん「愛媛産」
Function Mid文字列から文字列を削除(ByVal 元テキスト As String, ByVal 前検索値 As String, ByVal 後検索値 As String _
    , Optional is前検索値も削除する As Boolean = True, Optional is後検索値も削除する As Boolean = True) As String
    
    Mid文字列から文字列を削除 = 元テキスト ' いずれかの検索値が見つからなければ元テキストをそのまま返す
    
    Dim instr前値 As Long: instr前値 = InStr(元テキスト, 前検索値)
    If instr前値 = 0 Then Exit Function
    
    Dim instr後値 As Long: instr後値 = InStr(instr前値 + Len(前検索値) + 1, 元テキスト, 後検索値)
    If instr後値 = 0 Then Exit Function
    
    Mid文字列から文字列を削除 = _
        Left(元テキスト, instr前値 - 1) & _
        IIf(is前検索値も削除する, "", 前検索値) & _
        IIf(is後検索値も削除する, "", 後検索値) & _
        Mid(元テキスト, instr後値 + Len(後検索値))
    
End Function

※ 削除対象が見つからない場合は元の文字列をそのまま返す


なお、文字列から文字列を削除する場合、Replace関数ではなく、
ワイルドカードが使えるRangeオブジェクトのReplaceメソッドを利用すれば、

セル範囲.Replace "(*)", ""

と、非常に簡単に()内を消すこともできます。
速度もこちらの方が高速ですので、セル値をただ書き替えたいだけなら、わざわざ汎用関数を使わず、こちらを活用しましょう。

左(先頭)からn文字を削除

呼出 結果
leftからn文字削除("温州みかん", 1) 州みかん
leftからn文字削除("温州みかん", 2) みかん
leftからn文字削除("温州みかん", 100)  
Function Leftからn文字削除(ByVal 元テキスト As String, n As Long) As String
    Leftからn文字削除 = Mid(元テキスト, n + 1)
End Function

右(後尾)からn文字を削除

呼出 結果
Rightからn文字削除("温州みかん", 1) 温州みか
Rightからn文字削除("温州みかん", 2) 温州み
Rightからn文字削除("温州みかん", 100)  
Function Rightからn文字削除(ByVal 元テキスト As String, n As Long) As String
    If Len(元テキスト) - n > 0 Then
        Rightからn文字削除 = Left(元テキスト, Len(元テキスト) - n)
    End If
End Function

地味ですが「元の文字列の長さを超えて削除した場合」に、
エラーではなく""を返してくれるので結構重宝します。

○文字目から△文字目まで(Midのカスタマイズ)

文字数を指定する通常のMid関数をちょっとカスタマイズし、
開始と終了をどっちも位置で指定できるようにしたバージョンです。

呼出 結果
Mid("温州みかん(和歌山産)", 3, 3) みかん
Mid位置指定版("温州みかん(和歌山産)", 3, 5) みかん
Function Mid位置指定版(ByVal 元テキスト As String, ByVal 開始位置 As Long, ByVal 終了位置 As Long) As String
    If 開始位置 > 終了位置 Then Err.Raise 5, , "Midの終了が開始より小さくなっています。"
    Mid位置指定版 = Mid(元テキスト, 開始位置, 終了位置 - 開始位置 + 1)
End Function

終了位置 - 開始位置 + 1 の計算はコードによっては書くのが面倒なので、
何文字目から何文字目かがわかっている時は、こっちのバージョンを使うと良いです。

ラッパー(ラップ関数)を作る

ここまで紹介してきた

呼出 結果
Mid文字列から文字列("みかん(愛媛産)", "(", ")") 愛媛産

↑これでも十分便利な関数なのですが、この関数は今回のように、
第3,4引数に "(" と ")" を渡すことが非常に多いです。


そんなときは、

Function Midカッコ内(ByVal 元テキスト As String) As String
    Midカッコ内 = Mid文字列から文字列(元テキスト, "(", ")")
End Function

このように、自作関数に常に固定の引数を渡す自作関数を作る技が使えます。

この「関数を包んでちょこっと仕様を変えた関数」のことを、ラッパー・ラップ関数と呼びます。


↑のように、ラップ関数は非常に作るのが簡単ですので、
以下の例を参考に、皆さんも自作してみてください。

カッコの中身を取り出す

呼出 結果
Midカッコ内("みかん(愛媛産)") 愛媛産
Midカッコ内("みかん(愛媛産)", True) (愛媛産)
Function Midカッコ内(ByVal 元テキスト As String, Optional isカッコも結果に含む As Boolean = False) As String
    Midカッコ内 = Mid文字列から文字列(Replaceカッコを半角に統一(元テキスト) _
                                , "(", ")", isカッコも結果に含む, isカッコも結果に含む)
End Function

※ サンプルで紹介したものに加えて、ついでに「カッコを半角に統一する関数」も使用しています。本ページ下部を参照ください。

カッコと中身を消す

呼出 結果
Deleteカッコ内("みかん(愛媛産)") みかん
Function Deleteカッコ内(ByVal 元テキスト As String) As String
    Deleteカッコ内 = Mid文字列から文字列を削除(Replaceカッコを半角に統一(元テキスト) , "(", ")")
End Function

ファイル名から拡張子を取り出す

呼出 結果
Get拡張子("テスト.xlsm") xlsm
Get拡張子("テスト.xlsm", TRUE) .xlsm
Function Get拡張子(ByVal ファイル名 As String, Optional isドットも含む As Boolean = False) As String
    Get拡張子 = Right文字列まで(ファイル名, ".", is検索値を結果に含む:=isドットも含む)
End Function

フォルダパスからフォルダ名を取り出す

呼出 結果
Getフォルダ名("C:\Users\○○\Desktop\テストフォルダ") テストフォルダ
Getフォルダ名("C:\Users\○○\Desktop\テストフォルダ\") テストフォルダ
Function Getフォルダ名(ByVal フォルダパス As String) As String
    If Right(フォルダパス, 1) = "\" Then フォルダパス = Rightからn文字削除(フォルダパス, 1)
    Getフォルダ名 = Right文字列まで(フォルダパス, "\")
End Function

※ 最終文字が\でも\がなくても取り出せます。


このように、とても短い記述で関数を量産できますので、ラップ関数を作ってみてください。


ちなみに、当たり前ですがラップ関数をコピーしてくるときは、元の関数もコピーしてこないと動きません。
ラップ関数を作る際は、その点に十分注意してください。

では関数の紹介に戻ります。

文字列内の特定文字列の登場回数をカウント

呼出 結果
Count文字列の登場回数("みかんりんごみかんいちごみかん", "みかん") 3
Function Count文字列の登場回数(ByVal テキスト全体 As String, ByVal 検索値 As String) As Long
    Count文字列の登場回数 = (Len(テキスト全体) - Len(Replace(テキスト全体, 検索値, ""))) / Len(検索値)
End Function

複数種類の文字列を1つの文字列に一括で置換

呼出 結果
Replace複数文字列の一括置換("りんごぶどういちご", "みかん", "りんご", "ぶどう", "いちご") みかんみかんみかん
Function Replace複数文字列の一括置換(ByVal 元テキスト As String, ByVal 置換後の文字列 As String _
    , ParamArray pArr置換対象()) As String
    
    ' 1つの配列も受け取る
    Dim Arr置換対象: Arr置換対象 = IIf(IsArray(pArr置換対象(0)), pArr置換対象(0), pArr置換対象)
    
    Replace複数文字列の一括置換 = 元テキスト
    Dim 置換文字列
    For Each 置換文字列 In Arr置換対象
        Replace複数文字列の一括置換 = Replace(Replace複数文字列の一括置換, 置換文字列, 置換後の文字列)
    Next
    
End Function


これもラップ関数の作成に便利な関数です。
作成例は以下の通りです。

シート名に使用するとエラーになる文字を削除

呼出 結果
Deleteシート名禁則文字("?や[]はシート名に使えない") やはシート名に使えない
Function Deleteシート名禁則文字(ByVal 元テキスト As String) As String
    Deleteシート名禁則文字 = Replace複数文字列の一括置換(元テキスト, "", Split("',’,',*,:,?,\,¥,*,/,:,?,[,[,],],\,/,<,>", ","))
End Function

法人格(㈱/(株)など)の書式の統一

呼出 結果
Replace法人格を統一("㈱和風スパゲティ") (株)和風スパゲティ
Replace法人格を統一("(有)和風スパゲティ") (有)和風スパゲティ
Function Replace法人格を統一(ByVal 元テキスト As String) As String
    Replace法人格を統一 = Replace複数文字列の一括置換(元テキスト, "(株)", "(株)", "㈱", "(株)", "(株)")
    Replace法人格を統一 = Replace複数文字列の一括置換(Replace法人格を統一, "(有)", "(有)", "㈲", "(有)", "(有)")
End Function

最終的にどれに統一するかは第1引数をカスタマイズしてください。

カッコの統一・置換

呼出 結果
Replaceカッコを半角に統一("みかん(愛媛産)") みかん(愛媛産)
Replaceカッコ→アンダーバー("みかん(愛媛産)") みかん_愛媛産
Function Replaceカッコを半角に統一(ByVal 元テキスト As String) As String
    Replaceカッコを半角に統一 = Replace(元テキスト, "(", "(")
    Replaceカッコを半角に統一 = Replace(Replaceカッコを半角に統一, ")", ")")
End Function

Function Replaceカッコ→アンダーバー(ByVal 元テキスト As String) As String
    Replaceカッコ→アンダーバー = Replace(元テキスト, "(", "_")
    Replaceカッコ→アンダーバー = Replace(Replaceカッコ→アンダーバー, "(", "_")
    Replaceカッコ→アンダーバー = Replace(Replaceカッコ→アンダーバー, ")", "")
    Replaceカッコ→アンダーバー = Replace(Replaceカッコ→アンダーバー, ")", "")
End Function

セル範囲に一括実行する

法人格やカッコの全角/半角など、Replace関数を使って何かを統一する処理は、
元データのセル範囲をそのまま書き換えることが多いです。

この時は、引数を文字列ではなくセル範囲にしてしまうと、より使い勝手のいい関数になります。

Sub カッコを半角に統一する(セル範囲 As Range)
    セル範囲.Replace "(", "(", lookat:=xlPart
    セル範囲.Replace ")", ")"
End Sub
Sub 法人格を統一する(セル範囲 As Range)
    Call 複数の文字列を一括で置換する(セル範囲, "(株)", "(株)", "㈱", "(株)", "(株)")
    Call 複数の文字列を一括で置換する(セル範囲, "(有)", "(有)", "㈲", "(有)", "(有)")
End Sub
Sub 複数の文字列を一括で置換する(セル範囲 As Range, ByVal 置換後の文字列 As String _
    , ParamArray pArr検索対象())
    
    ' 1つの配列も受け取る
    Dim Arr検索対象: Arr検索対象 = IIf(IsArray(pArr検索対象(0)), pArr検索対象(0), pArr検索対象)
    
    Dim 置換文字列
    For Each 置換文字列 In Arr検索対象
        セル範囲.Replace 置換文字列, 置換後の文字列, lookat:=xlPart
    Next
    
End Sub

このようにセル範囲に対して置換を行う場合は、
速度面で有利なReplaceメソッドを使用しましょう。


これらの関数は、マクロ内で呼び出して使う以外に、
実行したい範囲を選択して、イミディエイトウィンドウに

Call カッコを半角に統一する(selection)

と打ち込んで実行する、便利マクロ的な使い方も可能です。


ちなみに余談ですが、私がFunctionで作る関数は、長い代入式の内部に書くことを想定して、なるべく短めにして体言止めにしています。

Worksheets(1).Name = Deleteシート名禁則文字(Worksheets(1).Range("A1"))

 
対してSubで作る関数は、Callの先頭に書くことになるため、コード全体が文章として読みやすくなるよう、送り仮名と最後の「する」を省略していません。

Call 法人格を統一する(Worksheets(1).Range("E2:E100"))

 
どうでもいい小ネタですが、もし気に入ったらこの命名規則を採用してみてください。

アルファベット ⇔ 数字

呼出 結果
Conv123→ABC(1) A
Conv123→ABC(37) AK
ConvABC→123("A") 1
ConvABC→123("BC") 55
Function Conv123→ABC(ByVal 数字 As Long) As String
    Conv123→ABC = Rightからn文字削除(Cells(1, 数字).Address(False, False), 1)
End Function
Function ConvABC→123(ByVal アルファベット As String) As Long
    On Error Resume Next
    ConvABC→123 = Range(アルファベット & 1).Column
End Function

文字列から数値だけを取り出す

呼出 結果
文字列からすべての数字を取り出す("123みかん456りんご789") "123456789"
文字列から最初に登場した数値部分を取り出す("1,100円(税10%)") "1,100"

※ どちらもLongではなくStringを返します。(0と""を区別したいから)

※ ソースが長いので下記記事を参照ください。
www.limecode.jp

文字列の結合(改行,カンマ,\など)

呼出 結果
Joinフォルダ名("商品別フォルダ", "くだもの", "みかん") 商品別フォルダ\くだもの\みかん
Join改行("みかん", "りんご", "いちご") みかん
りんご
いちご
Joinカンマ("みかん", "りんご", "いちご") みかん,りんご,いちご
Function Joinフォルダ名(ParamArray pArr各文字列() As Variant) As String
    Joinフォルダ名 = Join(pArr各文字列, "\")
End Function

Function Join改行(ParamArray pArr各文字列() As Variant) As String
    Join改行 = Join(pArr各文字列, vbLf)
End Function

Function Joinカンマ(ParamArray pArr各文字列() As Variant) As String
    Joinカンマ = Join(pArr各文字列)
End Function

 
& "," & とか、& vbLf & とかをたくさん書くのが面倒な人のための関数です。
非常に単純な中身で、Join関数のラッパーといってもいいくらいですね。

Msgbox Join改行("あけましておめでとうございます。", "今年もよろしくお願いいたします。")

このような使い方が多い関数ですが、これは

Msgbox Join(Array("あけましておめでとうございます。", "今年もよろしくお願いいたします。"), vblf)

と、Join+Arrayをイディオム的に使えばできることなので、
これに慣れている方は、わざわざ汎用関数にする必要は薄いです。

この記述も汎用性があって便利なので、ぜひ覚えておきましょう。

まとめ

以上で文字列操作の汎用関数集の紹介を終わります。

この他に追加があれば、適時ページを更新していきます。
追加してほしい関数などありましたらコメントもしくはTwitterにご連絡ください。


ちなみに汎用関数は、ただ呼び出して使うだけでなく、
サンプルコードの辞書としても機能します。

似た処理を書きたいときに、関数の中身のコードをコピーしてきて、
コードテンプレートとしても活用できるということですね。


本ページの関数で、簡単なコードでもちゃんとした変数名を付けているのは、
「コピペ先のコードにある変数と名前がかぶらないように」
という意図があったりします。


汎用関数を自分で作ってみたいという方は、よろしければ以下の記事もどうぞ
www.limecode.jp