備忘ログ

チラシの裏的備忘録&メモ

Rで特定の記号に挟まれている文字列を記号ごと取り除きたいときの文字列処理のメモ

テキスト中の特定の記号(始点と終点で異なる)で挟まれているものを記号ごと取り除きたいということがあった(《消したい文字》の箇所を消したいと思った)。Rでやりたかった。

具体的に言うと青空文庫のテキストデータのルビの箇所や章の見出しの箇所の注釈を消したいと思った。ときどき似たような処理をするがやり方忘れるのでメモしておく。処理に需要ありそうだが、関数とか見つけられないのでいつもざっと書くときは{stringr}str_split()を重ねがけして処理ししてる。もっと良い処理があるに違いない。

サンプルテキストとして青空文庫でダウンロードできる夏目漱石の「こころ」をつかう。

# テキストに複数の処理を重ねるのでパイプで処理したいので{magrittr}読み込む
library(magrittr)
library(stringr)

# 夏目漱石の「こころ」の冒頭
# `str_split()`で始まりの記号《で切り取り、終わりの記号》はそれより前の文字列ごとワイルドカードをつかって切り捨てる。
# 最後に`str_c()`で記号の箇所で分割されている文字列をつなげる。
"私《わたくし》はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚《はば》かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執《と》っても心持は同じ事である。よそよそしい頭文字《かしらもじ》などはとても使う気にならない。" %>% 
  str_split(pattern = "《", simplify = TRUE) %>% 
  str_split(pattern = ".+》", simplify = TRUE) %>% 
  str_c(collapse = "")
## [1] "私はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執っても心持は同じ事である。よそよそしい頭文字などはとても使う気にならない。"

でできる。

str_split(text, pattern = "《.+》")や、str_remove(text, pattern = "《.+》")str_remove_all(text, pattern = "《.+》")でも一見良さそうだが、

"私《わたくし》はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚《はば》かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執《と》っても心持は同じ事である。よそよそしい頭文字《かしらもじ》などはとても使う気にならない。" %>% 
  str_split(pattern = "《.+》", simplify = TRUE) %>% 
  str_c(collapse = "")
## [1] "私などはとても使う気にならない。"

となってしまう。最初の《から、最後の》のところで区切られ切り捨てられてしまう。

同様にstr_remove()でも、

"私《わたくし》はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚《はば》かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執《と》っても心持は同じ事である。よそよそしい頭文字《かしらもじ》などはとても使う気にならない。" %>% 
  str_remove(pattern = "《.+》")
## [1] "私などはとても使う気にならない。"

となる。str_remove_all()でも同じ。

文中に《消したい文字列》が一つだけならこれでもいいが、何個あるのかわからないときはちょっと困る。

{base}だけで書くなら、strsplit()を使って次のようにできる。

# パイプ(|>)処理しているのでR4.1.x以降で動く。
# パイプ使わないで入れ子にするか中間オブジェクト作るといいと思う。
"私《わたくし》はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚《はば》かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執《と》っても心持は同じ事である。よそよそしい頭文字《かしらもじ》などはとても使う気にならない。" |>
  strsplit("《") |>
  unlist() |>
  strsplit(".+》") |>
  unlist() |>
  paste0(collapse = "")
## [1] "私はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執っても心持は同じ事である。よそよそしい頭文字などはとても使う気にならない。"

パイプ処理の問題よりも、strsplit()を非リスト化するのにunlist()を噛ませなければ行けないのがちょっとめんどくさい。

自分用にはこれをsapply()で回す関数str_remove_sandwich()を作っているので何度も処理するときには関数化したもの使って、複数の文字列を受けても処理できるようにしている。