文字列から "(" と ")" を検索し、カッコの中身を取得する方法を解説します。
基本のコード
例えば「みかん(和歌山産)」から「和歌山産」を取得する場合は、
以下のコードを実行します。
Dim 商品名 As String, 産地 As String Dim 開始 As Long, 終了 As Long 商品名 = "みかん(和歌山産)" 開始 = Instr(商品名, "(") + 1 終了 = Instr(商品名, ")") - 1 産地 = Mid(商品名, 開始, 終了 - 開始 + 1) MsgBox 産地 ' ← 「和歌山産」が表示されます。
まずはInstr関数で「開きカッコ」を検索し、その次の文字を開始位置に、
続いてInstr関数で「閉じカッコ」を検索し、その前の文字を終了位置に、
そうして求まった「○文字目から△文字目」という位置情報を、
Mid関数(元の文字列, ○, △ - ○ + 1)
に代入しています。
Mid関数は終了位置ではなく文字数を指定する必要がありますので、
終了位置 - 開始位置 + 1で文字数を計算しています。
変数が多くて面倒な気がしてしまうかもしれませんが、
変数を用意しない場合は、
Mid("みかん(和歌山産)", InStr("みかん(和歌山産)", "(") + 1, InStr("みかん(和歌山産)", ")") - InStr("みかん(和歌山産)", "(") - 1)
こうなります。
確かに1行で済んではいますが、書き上げるのにかかる時間を計測したら、
間違いなく変数を用意した方が短くなると思います。
全角カッコを半角カッコに戻してから位置を取得する
基本形のコードは上記の通りですが、残念ながら実務ではそう上手くはいきません。
カッコは基本的に全角と半角が入り乱れているものですからね。
これに対処するのは意外と簡単で、
商品名 = Replace(商品名, "(", "(") 商品名 = Replace(商品名, ")", ")") 開始 = Instr(商品名, "(") + 1 終了 = Instr(商品名, ")") - 1
と、Replace関数を使ってカッコを半角に統一してから、
開始・終了位置の取得に入ればOKです。
こういったカスタマイズを後から行うときも、
変数に入れて読みやすくしておくのはとても大切です。
未来の自分への贈り物だと思って、サボらず変数を作っておきましょう。
複数のセルへ実行するコード
例えば、
商品名 | 産地 |
---|---|
みかん(和歌山産) | |
りんご(青森産) | |
… |
このような表で、B列の産地を取得するコードは以下のように書きます。
Sub A列の商品名からB列の産地を取得する() Application.ScreenUpdating = False Dim R As Long Dim LastR As Long LastR = ActiveSheet.UsedRange.Rows.Count + ActiveSheet.UsedRange.Row - 1 ' 商品名のある全データをループ For R = 2 To LastR If Cells(R, 1) <> "" Then ' カッコを半角にしてから位置を検索 Dim 商品名 As String, 開始 As Long, 終了 As Long 商品名 = Cells(R, 1) 商品名 = Replace(商品名, "(", "(") 商品名 = Replace(商品名, ")", ")") 開始 = InStr(商品名, "(") + 1 終了 = InStr(商品名, ")") - 1 ' どちらのカッコもあり、(よりも)が後ろにあれば産地を取得 If 開始 > 1 And 終了 >= 開始 Then Cells(R, 2) = Mid(商品名, 開始, 終了 - 開始 + 1) Cells(R, 1) = 商品名 ' ←ついでにカッコの表記ゆれも直す End If End If Next ' 商品名のある全データをループ End Sub
メインの「カッコの中身」取得コードは基本形そのままですね。
メインコードの実行前に、
If 開始 > 1 And 終了 >= 開始 Then
この判定を入れておくことで、Mid関数がエラーで止まることを防いでいます。
データエリア全体に処理をする場合は、
このようにFor~Nextループ内でカッコ内取得コードを使ってください。
おまけ:カッコ内を取得する自作関数を作る
カッコ内の文字を取得する処理をよく行う方は、
その処理を関数として自作しておくと非常に便利です。
上記のマクロを、自作関数を使って書くとこんな感じになります↓
Sub A列の商品名からB列の産地を取得する() Application.ScreenUpdating = False Dim R As Long For R = 2 To Get最終行(ActiveSheet) If Cells(R, 1) <> "" Then Cells(R, 2) = Getカッコ内(Cells(R, 1)) End If Next End Sub ' テキストのカッコ内を取得する Function Getカッコ内(ByVal 元テキスト As String) As String 元テキスト = Replace(元テキスト, "(", "(") 元テキスト = Replace(元テキスト, ")", ")") Dim 開始 As Long, 終了 As Long 開始 = InStr(元テキスト, "(") + 1 終了 = InStr(元テキスト, ")") - 1 If 開始 > 1 And 終了 >= 開始 Then Getカッコ内 = Mid(元テキスト, 開始, 終了 - 開始 + 1) End If End Function ' シートの最終行を取得する Function Get最終行(ws As Worksheet) As Long Get最終行 = ws.UsedRange.Rows.Count + ws.UsedRange.Row - 1 End Function
メインマクロが尋常じゃないほど短くなっていますね。
しかも「Getカッコ内」と、ノーコメントで読めるようになっているおまけつき!
関数を自作するメリットは、実は「使いまわせる」というメリット以上に、
「処理を1行にして名前を付けられる」メリットの方が重要だったりします。
その効果はこのコードを見れば一目瞭然ですね。
その上でもちろん「使いまわせる」というのも重要で、
この関数は別のマクロでも使えますので、じゃんじゃん使いまわしてください。
自作関数といいながら作成者は私ですが、
これをコピペして持っていけばそのまま使えます。
しれっと関数化した「シートの入力最終行の取得」もお土産にどうぞ。
関数化に興味がわいた方は、ぜひこちらの記事もご覧ください。
最後に小ネタを少々。
関数を自作するメリットは、実は「使いまわせる」というメリット以上に、
「処理を1行にして名前を付けられる」メリットの方が重要だったりします。
↑この件ですが、
Dim R As Long For R = 2 To GetLastRow(ActiveSheet) If Cells(R, 1) <> "" Then Cells(R, 2) = GetStrInParentheses(Cells(R, 1)) End If Next
↑このコードは、そのメリットを享受できていますでしょうか?
今回の例のように、「関数の作り方」を学ぶとなると、
Functionプロシージャの構文を覚えるといったシステム的な勉強よりも、
文章力や読解力をプログラミングに活かす勉強が必要になります。
それを母国語を使わずに修練しようとすると、
無駄に苦労するだけでちゃんとした設計力が身に付きません。
ある程度実力が付いた後、変数・関数を英語にするかどうかは自由です。
ですが少なくとも「関数の作り方」を学んでいる段階では、
格好つけずにちゃんと母国語を頼りましょう。
www.limecode.jp