比較的新しい関数を使い、1つだけ抽出する方法と複数のカッコ内をすべて抽出する方法を試してみます。基本的に多重カッコに対応できませんが、複数抽出の場合は意味のある結果が得られます。
- TEXTBEFORE/TEXTAFTER関数を使ってカッコ内の文字列を抽出することができます。
- TEXTSPLIT関数等を駆使すれば複数抽出も可能です。
1つだけ抽出する場合
TEXTBEFORE/TEXTAFTER関数を使う方法
文字列中の指定した文字(列)より前の部分を取得するTEXTBEFORE関数と、同じく後の部分を取得するTEXTAFTER関数を併用する方法です。
式のネスト(入れ子)はやはり必要ですが、直観的に何をやっているのかわかりやすいのがメリットです。
画像ではC3セルに次の式を入力し、下方にコピーしています(以下同様)。
C3セル
(全角カッコが識別しにくいので【】に置き換えた場合を併記しています)
=TEXTBEFORE(TEXTAFTER(B3,"("),")") =TEXTBEFORE(TEXTAFTER(B3,"【"),"】")
内側の「TEXTAFTER(B3,"(")」で最初の前カッコより後の部分を抽出しています。その結果に対して外側のTEXTBEFOREを適用し、最初の後カッコより前の部分を抽出しています。
また、内側の関数の第3引数を使えばn番目や最後のカッコ内を取得することも可能です。例として「=TEXTBEFORE(TEXTAFTER(B4,"(",2),")")」とすれば2番目のカッコ内を取得できます。
注意点
シンプルな考え方で抽出できるのがメリットですが、カッコが2重、3重になっている場合は適切な文字列が得られません。これはFIND/MID関数を使った結果と同様です。
また、カッコの整合をチェックしていないので、カッコのつけ方がデタラメであっても何らかの文字列を取得してしまうのを避けられません。これは以下の例でも同じです。
TEXTSPLIT/INDEX関数を使う方法
前カッコと後カッコの両方を区切り文字として文字列を分割し、2番目を取得すれば最初のカッコ内を取得できる、というという割り切った方法です。
C3セル
(全角カッコが識別しにくいので【】に置き換えた場合を併記しています)
=INDEX(TEXTSPLIT(B3,{"(",")"}),2) =INDEX(TEXTSPLIT(B3,{"【","】"}),2)
TEXTSPLIT関数の第2引数(区切り文字)を「{"(",")"}」とすることで前カッコと後カッコの両方で文字列を区切り、INDEX関数で2番目の値を取得しています。
やはり「カッコが整合している」という前提に強く依存していますが、INDEXの引数を4にして2番目のカッコ内を取得したり、配列にして複数のカッコ内を取得するといった応用が可能です。
ちなみにTEXTSPLITは空白を無視しない(適宜空白セルを出力する)ので冒頭がカッコの場合でもうまく取得できます。
多重カッコには対応できません。
複数抽出する場合
TEXTSPLIT/TEXTBEFORE関数を使う方法
TEXTSPLIT関数で文字列が複数に分割されるのを利用して、複数のカッコ内の値を一括して取得する方法です。
多重カッコが存在する場合、階層が局所的に最も深いもののみを抽出します。
D3セル
(全角カッコが識別しにくいので【】に置き換えた場合を併記しています)
=LET(x,TEXTBEFORE(TEXTSPLIT(B3,"("),")"), FILTER(x,NOT(ISNA(x))) ) =LET(x,TEXTBEFORE(TEXTSPLIT(B3,"【"),"】"), FILTER(x,NOT(ISNA(x))) )
TEXTSPLIT関数で前カッコを区切り文字として文字列を分割し、さらに分割されたそれぞれの文字列に対しTEXTBEFORE関数を適用して後カッコより前を取得しています。後カッコがない文字列は#N/Aエラーになりますが、いずれこの結果全体にLET関数でxと名付けます。
最後にFILTER関数でxから#N/Aエラーを除いて完成です。
カッコが多重になっている場合は、前後の部分よりカッコの階層が深いカッコ内のみ取得します。面白い結果ではありますが実用的にあまり使いどころはなさそうです。
SCAN/LAMBDA/TEXTSPLIT関数等を使う方法
SCAN関数を使ってカッコの深さを数える方法です。
式は面倒ですがカッコ内のカッコを含めて抽出できます。抽出する対象の深さを変えることも可能です。
D3セル
(全角カッコが識別しにくいので【】に置き換えた場合を併記しています)
=LET(x,MID(B3,SEQUENCE(1,LEN(B3)),1), y,SCAN(0,x,LAMBDA(a,b,a+SWITCH(b,"(",1,")",-1,0))), REPLACE(TEXTSPLIT(CONCAT(IF(y>0,x,"_")),"_",,TRUE),1,1,"") ) =LET(x,MID(B3,SEQUENCE(1,LEN(B3)),1), y,SCAN(0,x,LAMBDA(a,b,a+SWITCH(b,"【",1,"】",-1,0))), REPLACE(TEXTSPLIT(CONCAT(IF(y>0,x,"_")),"_",,TRUE),1,1,"") )
LET関数を使い、文字列を1文字ずつ分割した配列をxとしています。
yはxを1文字ずつチェックして前カッコだったら1とし、後カッコだったら-1とするときの累積値の配列です。つまり1文字ごとにカッコの深さを数えています。
そしてxのうちy=0である位置に対応するものを「_」に置き換えて文字列として連結し、TEXTSPLITで再度分割します(この過程で深さ0=カッコ外の文字列が消滅します)。ただしすべてのセルの冒頭に1文字の前カッコが残るので、最後にREPLACEで除いて完成です。
文字列内で使われていない区切り文字(ここでは「_」)を選択する必要があります。計算が愚直すぎて冴えませんが、多重カッコ内の文字列を含め、すべてのカッコ内を取得できるのがメリットです。
備考として上記式中のxとyの配列を示します。
ちなみにyの最小値が0未満であるか、最後の値が0でない場合は、カッコが整合していないと判断することができます。