115  実践4 Look Around(先読み、後読み)

library(tidyverse)

さて、ここまでで、メタ文字を駆使して数字と小数点を抜き出す方法について解説してきました。

ここからは、Look Aroundと呼ばれる特殊な書き方を解説します。

例として、次文字列を見てください。

exa <-c(
  "room200 3unit AM3:40 abc300ml",
  "room201 4unit AM12:50 def125ml",
  "room202 5unit PM4:00 ghi400ml",
  "room203 6unit PM12:10 jkl50ml"
)

exa
[1] "room200 3unit AM3:40 abc300ml"  "room201 4unit AM12:50 def125ml"
[3] "room202 5unit PM4:00 ghi400ml"  "room203 6unit PM12:10 jkl50ml" 

この文字列は適当なので、内容については突っ込みはなしでお願いします。

ここで、それぞれの要素の最後の部分、例えば1つ目の要素だとすると、“abc300ml”の300という数字を抜き出したいとしましょう

str_view(exa,"\\d+") #X
[1] │ room<200> <3>unit AM<3>:<40> abc<300>ml
[2] │ room<201> <4>unit AM<12>:<50> def<125>ml
[3] │ room<202> <5>unit PM<4>:<00> ghi<400>ml
[4] │ room<203> <6>unit PM<12>:<10> jkl<50>ml

残念ながら単純に\d+とするだけでは一番最初の数字の塊を取得してしまいます。

こういうケースではlook aroundと呼ばれる特殊な記載で「条件を付けて」取得するとうまくいきます。。

条件の付け方を少し見ていきます。

str_view("dog:dog;dog", "dog")
[1] │ <dog>:<dog>;<dog>

この例で、真ん中のdogを対象としたい場合はdog(?=;)という書き方を利用します。<パターン1>(?=<パターン2>)と書くことで

パターン1パターン2と並んでいる文字列を認識して、パターン1だけを取得するということができます。

str_view("dog:dog;dog", "dog(?=;)")
[1] │ dog:<dog>;dog

また、(?<=:)dogと書くことで、

str_view("dog:dog;dog", "(?<=:)dog")
[1] │ dog:<dog>;dog

(?<=…)に該当する部分の後ろのパターンを選択することができます。

ということで、

exa
[1] "room200 3unit AM3:40 abc300ml"  "room201 4unit AM12:50 def125ml"
[3] "room202 5unit PM4:00 ghi400ml"  "room203 6unit PM12:10 jkl50ml" 

の最後のmlの前の数字を拾うこと、できますでしょうか?

str_view(exa,"\\d+(?=ml)")
[1] │ room200 3unit AM3:40 abc<300>ml
[2] │ room201 4unit AM12:50 def<125>ml
[3] │ room202 5unit PM4:00 ghi<400>ml
[4] │ room203 6unit PM12:10 jkl<50>ml

ですね?

この、(?=A)B と A(?<=B)を利用すれば

str_view(exa,"(?<=room)\\d+")
[1] │ room<200> 3unit AM3:40 abc300ml
[2] │ room<201> 4unit AM12:50 def125ml
[3] │ room<202> 5unit PM4:00 ghi400ml
[4] │ room<203> 6unit PM12:10 jkl50ml
str_view(exa,"(A|P)M\\d+:\\d+")
[1] │ room200 3unit <AM3:40> abc300ml
[2] │ room201 4unit <AM12:50> def125ml
[3] │ room202 5unit <PM4:00> ghi400ml
[4] │ room203 6unit <PM12:10> jkl50ml
str_view(exa,"\\d+(?=ml)")
[1] │ room200 3unit AM3:40 abc<300>ml
[2] │ room201 4unit AM12:50 def<125>ml
[3] │ room202 5unit PM4:00 ghi<400>ml
[4] │ room203 6unit PM12:10 jkl<50>ml
str_view(exa,"\\d+(?=unit)")
[1] │ room200 <3>unit AM3:40 abc300ml
[2] │ room201 <4>unit AM12:50 def125ml
[3] │ room202 <5>unit PM4:00 ghi400ml
[4] │ room203 <6>unit PM12:10 jkl50ml

ということもできます。

またここで、Look Aroundを用いる場合に困るケースをご紹介して、その解決方法も提示しておきます。

“abcd defg ghij jklm”の |-1| |-2| |-3| |-4| それぞれ、1から4を拾いたいばあい、どうすればよいかという

問題です。

temp <- "abcd defg ghij jklm"
str_view(temp, "^.+(?= )")
[1] │ <abcd defg ghij> jklm

とすると、実はLook Around、一番最後のスペースを拾ってしまうのでこの正規表現だとだめです。

そういう場合は、?メタ文字を利用しましょう。

範囲指定する+や{}の直後に?を置くことで、可能な限り短い範囲で拾うという動作が可能です

str_view("aabaaa","a+")
[1] │ <aa>b<aaa>
str_view("aabaaa","a+?")
[1] │ <a><a>b<a><a><a>
str_view("abaaaa","a{2,3}")
[1] │ ab<aaa>a
str_view("abaaaa","a{2,3}?")
[1] │ ab<aa><aa>

なので、

temp <- "abcd defg ghij jklm"
str_view(temp, "^.+(?= )")
[1] │ <abcd defg ghij> jklm
str_view(temp, "^.+?(?= )")
[1] │ <abcd> defg ghij jklm

このように一つ目を拾うことができました。

この?マークを利用した正規表現、もう少し先のstringrの練習問題ででてきますのでこんな記号があったなくらいで結構ですので覚えておいてください。。