Rの正規表現で最短マッチ(最短一致)させる
特にRに固有のことというわけではありませんし、正規表現に詳しい人にとっては当たり前のことだとは思いますが、私自身が結構つまづいたので・・・
↓こんな感じのベクトル(住所リストのイメージ)があったとします。
住所リスト <- c( "佐藤町中村1-2",
"高橋町新田2-3",
"佐々木町本町3-4",
"伊藤町原4-5",
"山本町町田5-6",
"長谷川町本郷6-7" )
住所リスト
[1] "佐藤町中村1-2" "高橋町新田2-3" "佐々木町本町3-4"
[4] "伊藤町原4-5" "山本町町田5-6" "長谷川町本郷6-7"
で、それぞれから冒頭の「○○町」の部分を取り除きたいとします。
町名の長さも一律ではないので、こういうときは正規表現が便利ですよね。
私が最初に思いついた正規表現が以下です。
sub("^.+町", "", 住所リスト, useBytes=F)
冒頭(^)から、任意の文字(.)が、1つ以上(+)あって、「町」で終わるような文字列、というつもりです。
ですが、うまく行きませんでした。↓実行してみると・・・
sub("^.+町", "", 住所リスト, useBytes=F)
[1] "中村1-2" "新田2-3" "3-4" "原4-5" "田5-6" "本郷6-7"
町名より下のレベルの住所に「町」という文字を含んでいると、そこまで余分にマッチしてしまったというわけです↓
"佐々木町本町3-4"
"山本町町田5-6"
これは正規表現のデフォルトの動きが最長マッチ(最長一致)になっているからなんですね。
これを最短マッチ(最短一致)で動くように指定してやれば、意図通りに動いてくれそうです。
最短マッチにするには1個以上の繰り返しの「+」に「?」を付け足してやればOKです。
sub("^.+?町", "", 住所リスト, useBytes=F)
[1] "中村1-2" "新田2-3" "本町3-4" "原4-5" "町田5-6" "本郷6-7"
意図通りの結果になりました。同様に、0個以上の繰り返しの場合には「*?」を使えばOKです。
上記は、マッチした文字列を除去する例でした。では、マッチした文字列を抽出するにはどうすればいいでしょうか。
私はこの方法くらいしか思いつかなかったのですが、なんだかまどろっこしい。もっといいやり方があるのではないかという気もしていますが・・・
# マッチする開始位置を得る
位置 <- regexpr("^.+?町", 住所リスト, useBytes=F)
位置
[1] 1 1 1 1 1 1
attr(,"match.length")
[1] 3 3 4 3 3 4
# マッチする長さも、regexprの戻り値に入っているので、取り出す
長さ <- attr(位置, "match.length")
長さ
[1] 3 3 4 3 3 4
# マッチ位置とマッチ長さを使って、substrで抽出
substr(住所リスト, 位置, 長さ)
[1] "佐藤町" "高橋町" "佐々木町" "伊藤町" "山本町" "長谷川町"
無事、町名を取り出せたようです。
ちなみに、substrの第3引数には終了位置を指定する必要があります。上記の例では開始位置が1固定なので、終了位置=長さであるため、そのまま指定しましたが、開始位置が1固定でない場合は、
substr(住所リスト, 位置, 位置 + 長さ - 1)
とする必要があります。今回の例だと、実行結果は同じになります。
substr(住所リスト, 位置, 位置 + 長さ - 1)
[1] "佐藤町" "高橋町" "佐々木町" "伊藤町" "山本町" "長谷川町"
↓こんな感じのベクトル(住所リストのイメージ)があったとします。
住所リスト <- c( "佐藤町中村1-2",
"高橋町新田2-3",
"佐々木町本町3-4",
"伊藤町原4-5",
"山本町町田5-6",
"長谷川町本郷6-7" )
住所リスト
[1] "佐藤町中村1-2" "高橋町新田2-3" "佐々木町本町3-4"
[4] "伊藤町原4-5" "山本町町田5-6" "長谷川町本郷6-7"
で、それぞれから冒頭の「○○町」の部分を取り除きたいとします。
町名の長さも一律ではないので、こういうときは正規表現が便利ですよね。
私が最初に思いついた正規表現が以下です。
sub("^.+町", "", 住所リスト, useBytes=F)
冒頭(^)から、任意の文字(.)が、1つ以上(+)あって、「町」で終わるような文字列、というつもりです。
ですが、うまく行きませんでした。↓実行してみると・・・
sub("^.+町", "", 住所リスト, useBytes=F)
[1] "中村1-2" "新田2-3" "3-4" "原4-5" "田5-6" "本郷6-7"
町名より下のレベルの住所に「町」という文字を含んでいると、そこまで余分にマッチしてしまったというわけです↓
"佐々木町本町3-4"
"山本町町田5-6"
これは正規表現のデフォルトの動きが最長マッチ(最長一致)になっているからなんですね。
これを最短マッチ(最短一致)で動くように指定してやれば、意図通りに動いてくれそうです。
最短マッチにするには1個以上の繰り返しの「+」に「?」を付け足してやればOKです。
sub("^.+?町", "", 住所リスト, useBytes=F)
[1] "中村1-2" "新田2-3" "本町3-4" "原4-5" "町田5-6" "本郷6-7"
意図通りの結果になりました。同様に、0個以上の繰り返しの場合には「*?」を使えばOKです。
上記は、マッチした文字列を除去する例でした。では、マッチした文字列を抽出するにはどうすればいいでしょうか。
私はこの方法くらいしか思いつかなかったのですが、なんだかまどろっこしい。もっといいやり方があるのではないかという気もしていますが・・・
# マッチする開始位置を得る
位置 <- regexpr("^.+?町", 住所リスト, useBytes=F)
位置
[1] 1 1 1 1 1 1
attr(,"match.length")
[1] 3 3 4 3 3 4
# マッチする長さも、regexprの戻り値に入っているので、取り出す
長さ <- attr(位置, "match.length")
長さ
[1] 3 3 4 3 3 4
# マッチ位置とマッチ長さを使って、substrで抽出
substr(住所リスト, 位置, 長さ)
[1] "佐藤町" "高橋町" "佐々木町" "伊藤町" "山本町" "長谷川町"
無事、町名を取り出せたようです。
ちなみに、substrの第3引数には終了位置を指定する必要があります。上記の例では開始位置が1固定なので、終了位置=長さであるため、そのまま指定しましたが、開始位置が1固定でない場合は、
substr(住所リスト, 位置, 位置 + 長さ - 1)
とする必要があります。今回の例だと、実行結果は同じになります。
substr(住所リスト, 位置, 位置 + 長さ - 1)
[1] "佐藤町" "高橋町" "佐々木町" "伊藤町" "山本町" "長谷川町"
コメント
コメントを投稿