2015年4月8日水曜日

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] "佐藤町"   "高橋町"   "佐々木町" "伊藤町"   "山本町"   "長谷川町"




0 件のコメント:

コメントを投稿