R
본 정규표현식 교재는 Software Carpentry Regular Expression을 번역한 것입니다. 기본적인 프로그래밍 개념을 이미 이해하고 있고, 파이썬 기본구성요소에 친숙하거나 신속히 숙달할 수 있음을 가정한다. 또한, 데이터 사이언스 R 언어도 동일한 사항을 다루고 있어 파이썬 코드를 R코드로 변역을 하였고 텍스트에 정규표현식 교육을 위해서 개발된 R 학습교재와 팩키도 함께 반영한 것입니다.
정규표현식(Regular Expression)은 연관된 문자열 집합을 매칭하는 패턴이다. 정규표현식이 매칭에 실패하는 패턴도 있지만, 레거시 텍스트 파일에서 정보를 추출할 때, 프로그래머 대부분이 사용하는 강력한 도구다.
주요점(key points)
- 정규표현식은 문자열로 작성한다(따라서, 표기법이 다소 세련되지 못하다).
- 알파벳과 숫자는 그 자체로 매칭되고, 한글도 매칭된다.
- 반복되는 문자에 대해
*
,+
,?
특수기호를 사용한다.|
을 사용해서 또는 혹은 문자 집합을 매칭한다.- 괄호를 사용해서 문자열을 집단으로 묶고, 매칭되는 정보를 추출한다.
- 정규표현식 라이브러리를 사용해서 매칭되는 모든 것을 찾고, 문자열을 바꾸고, 기타 연산작업을 수행한다.
소개 및 간단한 패턴 | 연산자 | 동작원리 |
---|---|---|
추가 패턴 | 참고문헌 사례 |
---|---|
자주 사용되는 한국어 정규표현식을 차근차근 정리해보자.
* 전자우편 주소: /^[a-z0-9_+.-]+@([a-z0-9-]+\.)+[a-z0-9]{2,4}$/
* URL: /^(file|gopher|news|nntp|telnet|https?|ftps?|sftp):\/\/([a-z0-9-]+\.)+[a-z0-9]{2,4}.*$/
* HTML 태그 - HTML tags: /\<(/?[^\>]+)\>/
* 전화 번호 - 예, 123-123-2344 혹은 123-1234-1234: /(\d{3}).*(\d{3}).*(\d{4})/
* 날짜 - 예, 3/28/2007 혹은 3/28/07: /^\d{1,2}\/\d{1,2}\/\d{2,4}$/
* jpg, gif 또는 png 확장자를 가진 그림 파일명: /([^\s]+(?=\.(jpg|gif|png))\.\2)/
* 1부터 50 사이의 번호 - 1과 50 포함: /^[1-9]{1}$|^[1-4]{1}[0-9]{1}$|^50$/
* 16 진수로 된 색깔 번호: /#?([A-Fa-f0-9]){3}(([A-Fa-f0-9]){3})?/
* 적어도 소문자 하나, 대문자 하나, 숫자 하나가 포함되어 있는 문자열(8글자 이상 15글자 이하) - 올바른 암호 형식을 확인할 때 사용될 수 있음: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,15}/
R
정규표현식가장 먼저 R
정규표현식으로 작업할 텍스트 데이터를 R 작업 환경으로 가져온다.
Lucy Park (박은정), 2015-08-29, “한국어와 NLTK, Gensim의 만남 (부제: 영화 리뷰를 컴퓨터가 이해할 수 있는 형식으로 표현해서 센티멘트 분석하기)”, PyCon Korea 2015 네이버 영화 리뷰 데이터를 자져와서 작업을 시작해 본다.
데이터프레임에서 텍스트, 즉 영화리뷰 20개만 뽑아 텍스트 벡터로 변환시킨 후 stringr
팩키지의 정규표현식 pattern=
을 적용시켜서 “영화”만 선택한다.
## 팩키지와 재현가능하도록... 난수 seed 고정
library(tidyverse)
library(glue)
set.seed(777)
## 데이터 가져와서 20개 리뷰만 추출
review <- read_delim('https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt', delim="\t") %>%
sample_n(20)
## 데이터프레임으로 ... 텍스트 벡터로 변환
review %>%
pull(document) %>%
str_view(pattern="영화")
검색어(“영화”)만 포함된 영화리뷰를 뽑아보자. 이를 위해서 str_detect()
함수 내부에 정규표현식을 전달시켜 TRUE
/FALSE
마스크(mask)를 생성시킨다. 그리고 나서, 텍스트 벡터에 마스크 벡터를 넣어 TRUE
만 즉, “영화” 단어가 포함된 애들만 추출한다.
[1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE TRUE
[13] FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE
[1] "정말. 좋은 영화였습니다 감동!"
[2] "왜이리 평점이 낮지..베드신만을 생각하지말고전체적인 영화스토리에선 너무 재미있었다.영상도 너무 이뻣고.마지막 영상하나하나 기억에 남는다"
[3] "뭐하자는 플레이냐. 포르노 배우로 애로영화를 찍는 사치는 뭐냐?"
[4] "케이블로 막 봤는데 재미지다~ 요새 할리우드 액션 영화에선 없는 맛이 있음!"
[5] "정말 이영화가 6점대라니ㅠ분이 옛날시절 사랑얘기 잠깐나오는데 왜이렇게 슬플까요 그리고 중간중간 웃기부분도 있고..정말 천국이 있다면 이런곳일까요?천국이있다면 저런모습이였으면좋겠어요 지금 하늘에 있을 아이들이나 많은 사람들이 하늘에서라도 편햇으면좋겟네요"
[6] "OOO기영화"
이제 이를 확장시켜 전체 영화 리뷰 중 “영화” 단어가 포함된 리뷰는 몇개나 될까? 전체 데이터를 가져와서 앞서 적용시킨 알고리즘으로 마스크를 생성하는데, 재미있는 사실은 R에서 TRUE
는 1, FALSE
는 0으로 내부적으로 자연수로 저장되는 성질을 이용하여 간단히 mean()
함수를 적용시키면 평균을 구할 수 있다. filter()
함수로 결측값이 있는지 확인해보자. 이런 연유로 mean(..., na.rm=TRUE)
가 사용된 것이다.
## 전체 영화 리뷰 데이터
full_review <- read_delim('https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt', delim="\t")
word_mask <- str_detect(full_review$document, "영화")
round(mean(word_mask, na.rm = TRUE) * 100, 1)
[1] 31.3
# A tibble: 5 x 3
id document label
<dbl> <chr> <dbl>
1 2172111 <NA> 1
2 6369843 <NA> 1
3 1034280 <NA> 0
4 5942978 <NA> 0
5 1034283 <NA> 0
정규표현식(regular expression)을 사용하려면 키보드에 있는 특수문자를 이용한다는 사실 하나와 이를 사용해서 텍스트의 패턴을 만들어서 원하는 작업을 수행할 수 있다는 점이다. 가장 기본이 되는 정규표현식 기초에 대한 내용을 살펴보자.
특수 문자 | 의미 |
---|---|
^ |
라인 혹은 문자열의 시작을 의미함 |
$ |
라인 혹은 문자열의 끝을 의미함 |
. |
문자, 숫자, 공백문자를 가리지 않고 어떤 것이라도 매칭함을 의미함 |
\\ |
특수문자(^ , $ , . , …) 을 매칭하고자 할 때 정규표현식 탈출(escape)하는 의미 |
예를 들어, 영화.스토리
텍스트가 있는 첫번째 단어를 뽑고자 할 경우 ^
으로 위치를 지정하고 .
으로 앞서 의미하는 문자, 숫자, 공백문자 아무것이나 하나를 뽑아낸다. 즉, ^.
을 조합하게 되면 첫번째 오는 아무 문자나 추출시킨다. .$
는 아무 문자나 하나 마지막에 온다는 의미가 된다. 주의할 점은 정규표현식에 사용되는 특수문자는 \\.
와 같이 역슬래쉬를 두번 사용해야 한다. 이점이 일반 정규표현식과 차이가 나는 점이다.
정규표현식 R 코드 | 실행 결과 |
---|---|
str_match("영화.스토리", "^.") |
영 |
str_match("영화.스토리", ".$") |
리 |
str_match("영화.스토리", ".") |
영 |
str_match("영화.스토리", "\\.") |
. |
str_match("영화스토리", "\\.") |
NA |
다음으로 텍스트는 문자 클래스를 갖는데 다음과 같이 구분하면 좋을 듯 싶다. 한글, 영문, 일본어, 숫자, 특수문자 등 다양한 문자 클래스가 존재하지만, 이를 추출하기 위한 정규표현식 패턴도 함께 존재한다.
ㄱ-ㅎ가-힣
]
ㄱ-ㅎ
ㅏ-ㅣ
가-힗
あ-んァ-ソ
]
あ-ん
ァ-ソ
a-z
, A-Z
, [:word:]
, [:alpha:]
[aeiou]
[b-df-hj-np-tv-z]
0-9
, [:digit:]
!@#$%^&*(),.?":{}|<>
, [:punct:]
\\p{script=Han}
4가장 관심가는
str_match_all('원문: 其戰勝不復, 而應形于無窮 \n
읽는 법: 기전승불복 이응형어무궁 \n
뜻: 그렇게 달성되는 전승에는 동일한 양상이 반복되지 않으며, 형세에 따라 융통성 있게 적용되는 방법이 무궁무진한 것이다.', pattern = "\\p{script=Han}+")
[[1]]
[,1]
[1,] "其戰勝不復"
[2,] "而應形于無窮"
문자 클래스 | 실제 예제 | R 코드 | 실행 결과 |
---|---|---|---|
\\d , [:digit:] |
0, 1, 2,... |
str_match_all("저는 37살 입니다.", "\\d") |
3, 7 |
\\w , [:word:] |
V, i, c, t, o, r |
str_match_all("Victor", "\\w") |
V, i, c, t, o, r |
[ㄱ-ㅎ가-힣] |
저" "는" "살" "입" "니" "다" |
str_match_all("저는 37살 입니다.", "[ㄱ-ㅎ가-힣]") |
저, 는, 살, 입, 니, 다 |
\\s |
" " " " |
str_match_all("저는 !@#$%^&* 입니다.", "\\s") |
, |
[:punct:] |
"!" "@" "#" "%" "&" "*" "(" ")" "," "." "?" "\"" ":" "{" "}" |
str_match_all('!@#$%^&*(),.?":{}|', "[:punct:]") |
!, @, #, %, &, *, (, ), ,, ., ?, ", :, {, } |
.
은 임의 문자 하나를 의미했다. \\d
이것도 마찬가지로 숫자 하나를 의미한다. 전화번호는 일반적으로 000-0000-0000
와 같은 형태를 띄게 된다. 즉 숫자를 3번, -
4번, -
4번과 같은 순서로 쭉 매칭이 되어야 한다. 정규표현식을 \\d{3}-\\d{4}-\\d{4}
와 같이 패턴을 적게 되면 "010-7622-2319"
번호를 추출할 수 있게 된다.
phone_number <- c("0221120001", "02-2112-2327", "0107775342", "010-7622-2319", "010-4352-2319")
str_match_all(phone_number, pattern = "\\d{3}-\\d{4}-\\d{4}") %>% unlist
[1] "010-7622-2319" "010-4352-2319"
이번에 약간 틀어서 핸드폰 뿐만 아니라 유선 전화번호도 추출하고자 하면 어떨까? \\d{2,3}
와 같이 정의하면 숫자 두자리부터 3자리만 패턴을 매칭시킨다.
[1] "02-2112-2327" "010-7622-2319" "010-4352-2319"
구문 | 의미 |
---|---|
\\w{3} |
정확히 연속한 문자 3개 매칭 |
\\w{1,3} |
최소 문자 1개부터 최대 문자 3개 매칭 |
\\w{3, } |
최소 문자 3개 이상 최대값 없이 최대한 매칭 |
\\w+ |
+ 는 1개 이상 문자를 매칭 |
\\w* |
* 는 0개 이상 문자를 매칭 |
\(\{1, 2, 3, 4, 5, 6\}\)을 전체집합이라 하면, \(\{2, 3, 4, 5\}\)의 여집합은 \(\{1, 6\}\)이 되는 것처럼 매칭된 패턴을 도치하여 여집합을 구하고자 하는 경우 사용한다.
예를 들어, \\D
는 숫자에 대한 여집합을 구하고자 할 때 사용하는 패턴이다. 숫자를 뺀 나머지 모든 문자를 추출해보자.
mixed_string <- "한글과 English가 fancy하게 숫자도 777개나 뒤섞여 無窮 있어요"
str_match_all(mixed_string, "\\D") %>% unlist %>% str_c(collapse = "")
[1] "한글과 English가 fancy하게 숫자도 개나 뒤섞여 無窮 있어요"
숫자만 추출하고자 할 때는 [^]
와 같이 []
내부에 ^
을 넣게 되면 a-zA-Z가-힣
을 만족시키지 않는 패턴을 반환시킨다. 즉, 한자와 숫자만 추출시킨다.
[1] " 777 無窮 "
[^a-zA-Z]+
와 같이 패턴을 정의하게 되면 영문 대소문자를 제외한 나머지 문자를 모두 추출할 수 있다.
[1] "한글과 가 하게 숫자도 777개나 뒤섞여 無窮 있어요"
[]
꺽쇄괄호 내부에 정규표현식을 적게 되면 사용자 정의 정규표현식을 사용할 수 있다. 즉, \\d
, \\w
, \\D
등은 마치 내장함수처럼 기본으로 지원되는 정규표현식이고, []
을 사용하게 되면 임의 사용자 정의 정규표현식을 작성할 수 있다. 숫자 혹은 공백문자(\\s
)를 찾는 사용자 정의 정규표현식을 다음과 같이 작성할 수 있다.
[1] " " " " " " " " "7" "7" "7" " " " " " "
특정 단어 예를 들어, “왕가위”를 매칭하고자 할 때, 양 옆에 뭔가 단어가 붙은 경우 이를 무시하고 특정 단어만 단어경계를 가진 문자열만 추출하고자 할 때 유용하다. 이를 위해서 \\b
를 추출하고자 하는 문자열 앞뒤로 붙인다.
?
)파이프(|
)와 물음표(?
)를 사용하게 되면 좀더 다양한 패턴을 매칭시킬 수가 있다. 파이프(|
)는 혹은(or
)에 해당되어 A 혹은 B 혹은 C 와 같은 부울식을 구현할 수 있다. 예를 들어, 영화배우 이름이 포함된 리뷰를 찾고자 할 때, 영화 배우면을 파이프(|
)로 쭉 연결시키게 되면 영화배우가 포함된 리뷰를 걸러낼 수 있다.
[1] "왕가위, 장국영, 비포 선라이즈. 서울의 하얀 밤을 떠다니는 외로운 그들..그리고 우리..."
[2] "역시 난 덴젤의 연기가 좋다. 점점 주름이 늘어나는 모습에 슬플뿐ㅠㅠ"
경기, 경기도는 같은 시도지만, 이를 파이프(|
) 연산자를 사용해서 작업할 경우 코드가 길어진다. 선택(?
)을 넣게 되면 간결한 코드를 남길 수가 있다.
glue
팩키지glue
를 정규표현식, 데이터프레임과 엮어서 사용하게 될 경우 복잡한 많은 작업을 수월하게 진행시킬 수 있다. 전자우편 주소 벡터가 있을 때, glue()
함수를 사용하게 되면 코드를 간결하게 하여 데이터베이스에 저장된 전자우편 주소로부터 예를 들어 메일을 보내는데 도움을 받을 수 있다.
library(glue)
email <- c("kwangchun@gmail.com", "lee@live.com", "kwang@naver.com")
glue("TO: {email}")
TO: kwangchun@gmail.com
TO: lee@live.com
TO: kwang@naver.com
앞선 사례는 글로벌 환경에서 선언된 변수를 glue()
함수에서 가져와서 문자열을 새로 생성했다. glue()
내부에서 변수를 선언하는 것도 가능하다.
현재 온도: 38.5
“{}” 내부에 표현식을 넣어서 연산작업을 수행하는 것도 가능하다.
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
2 * 5 = 10
2 * 6 = 12
2 * 7 = 14
2 * 8 = 16
2 * 9 = 18
glue()
함수와 데이터프레임을 조합하게 되면 정형데이터로부터 텍스트 문장을 생성할 수 있다.
baseball <- tribble(~"team", ~"win", ~"lose",
"해태", 57, 56,
"롯데", 55, 53,
"넥슨", 71, 67)
baseball %>%
mutate(text = glue("올시즌 {team}는 {win}승 {lose}패를 기록했다"))
# A tibble: 3 x 4
team win lose text
<chr> <dbl> <dbl> <glue>
1 해태 57 56 올시즌 해태는 57승 56패를 기록했다
2 롯데 55 53 올시즌 롯데는 55승 53패를 기록했다
3 넥슨 71 67 올시즌 넥슨는 71승 67패를 기록했다
glue_collapse()
함수는 벡터를 스칼라로 변환시키는 역할을 하는 유용한 함수다. 예를 들어 영화배우가 텍스트 벡터로 된 경우 이를 glue_collapse()
함수로 사용할 경우 출연배우명을 쭉 붙여 텍스트 스칼라로 만들 수 있다.
movie <- "유열의 음악앨범"
actors <- c("송강호", "김고은", "정해인")
glue(
"{movie} 출연배우: ",
glue_collapse(actors,
sep=", ",
last=", 그리고 ")
)
유열의 음악앨범 출연배우: 송강호, 김고은, 그리고 정해인
baseball
데이터프레임의 변수를 하나 뽑아서 glue_collapse()
함수에 넣을 경우 텍스트 스카라로 만들 수 있다.
해태 롯데 넥슨
glue_collapse()
국가명을 country 벡터에 저장한 후, 앞서 학습한 glue_collapse()
함수와 결합하여 country_regex
정규표현식 벡터로 만든 후 뉴스에 언급된 국가만 추추하는 것이 가능하다.
country <- c("한국", "중국", "일본")
news <- c("한국은 미국과 중국의 무역갈등으로 인해 힘든 시간을 보냈지만, 한국과 일본의 경제문제도 또 다른 현안으로 떠오르고 있다.")
(country_regex <- glue_collapse(country, sep="|"))
한국|중국|일본
텍스트가 깔끔하게 준비된 경우, 정규표현식을 glue_collapse()
함수로 작성할 경우 가독성 높은 코드를 얻을 수 있어, 나중에 코드를 다시 볼 때 보기 좋게 된다.
baseball_text <- c("올시즌 해태는 57승 56패를 기록했다
올시즌 롯데는 55승 53패를 기록했다
올시즌 넥슨는 71승 67패를 기록했다")
(baseball_regex <- glue_collapse(c(
"team" = "[가-힣]+",
"\\s+",
"win" = "\\d+", "승",
"\\s+",
"lose" = "\\d+", "패")))
[가-힣]+\s+\d+승\s+\d+패
[1] "해태는 57승 56패" "롯데는 55승 53패" "넥슨는 71승 67패"
앞서 프로야구 경기팀의 승패를 기록한 텍스트에 오류가 있어, 승과 패가 뒤바뀐 일이 발생했다. 이를 바로잡고자 그룹(group)을 동원해서 문제를 풀어보자. 정규표현식에 ()
를 사용하면 그룹을 패턴에 부여할 수 있게 된다.
(regex_group <- glue_collapse(c(
"team" = "([가-힣]+)",
"\\s+",
"win" = "(\\d+)", "승",
"\\s+",
"lose" = "(\\d+)", "패")))
([가-힣]+)\s+(\d+)승\s+(\d+)패
[[1]]
[,1] [,2] [,3] [,4]
[1,] "해태는 57승 56패" "해태는" "57" "56"
[2,] "롯데는 55승 53패" "롯데는" "55" "53"
[3,] "넥슨는 71승 67패" "넥슨는" "71" "67"
\\1
, \\2
, \\3
을 지칭해서 위치를 바꿀 수 있다.
(regex_group <- glue_collapse(c(
"team" = "([가-힣]+)",
"\\s+",
"win" = "(\\d+)", "승",
"\\s+",
"lose" = "(\\d+)", "패")))
([가-힣]+)\s+(\d+)승\s+(\d+)패
[1] "올시즌 해태는 57승 56패를 기록했다\n 올시즌 롯데는 55승 53패를 기록했다\n 올시즌 넥슨는 71승 67패를 기록했다"
[1] "올시즌 해태는 56승 57패를 기록했다\n 올시즌 롯데는 53승 55패를 기록했다\n 올시즌 넥슨는 67승 71패를 기록했다"
DF
+ regex
: extract
데이터프레임과 정규표현식이 만나는 교차점에 extract()
동사가 있다. 먼저 특정 단어를 하나 뽑아 mutate()
동사로 새로운 변수를 생성하는 사례를 살펴보자.
# A tibble: 20 x 4
id document label is_movie[,1]
<dbl> <chr> <dbl> <chr>
1 9212028 정말. 좋은 영화였습니다 감동! 1 영화
2 1659748 아놔 진짜 더럽게 재미없네 0 <NA>
3 1077998 내 안에도 천사가 있을까. 1 <NA>
4 8747167 조잡스런 장면과 억지스런 설정에 사라진 웃음, 쌓여가는 짜증… 0 <NA>
5 6110214 누구나 공감가는 이야기를 소소하게 잘 그려냈네요. 보면서 엄청 울었네요.… 1 <NA>
6 10276348 인간들아 욕 좀 그만해라 금발의 여 주인공 쌕시하고 예뻐서 시종 눈을 뗄 수 없는 명작… 1 <NA>
7 8241466 귀엽고 따뜻한 예능..☆ 재미있어요! 샘들 힘내세요~~~~… 1 <NA>
8 7631300 왜이리 평점이 낮지..베드신만을 생각하지말고전체적인 영화스토리에선 너무 재미있었다.영상… 1 영화
9 5453346 뭐하자는 플레이냐. 포르노 배우로 애로영화를 찍는 사치는 뭐냐?… 0 영화
10 6682980 평점이 아직도 올라온다 ㄷㄷㄷ 째든 이건 내가봐도 명작임. 아주 어렸을적에 봤는데 그때… 1 <NA>
11 3775688 아... 진짜.... 시간 날렸당 0 <NA>
12 9677551 케이블로 막 봤는데 재미지다~ 요새 할리우드 액션 영화에선 없는 맛이 있음!… 1 영화
13 4649132 난 1편을 생일에 보고 2편도 생일에 봐서 꽤 재미있는듯?… 1 <NA>
14 8693988 정말 이영화가 6점대라니ㅠ분이 옛날시절 사랑얘기 잠깐나오는데 왜이렇게 슬플까요 그리고 … 1 영화
15 7616081 다소 지루하다. 하지만 담고 있는 내용이 워낙 훌룡하다. 평점 조절이 필요하다.… 1 <NA>
16 2183087 OOO기영화 0 영화
17 728878 마지막 장면은 정말 잊지못함.. 1 <NA>
18 7285865 왕가위, 장국영, 비포 선라이즈. 서울의 하얀 밤을 떠다니는 외로운 그들..그리고 우리… 1 <NA>
19 9394907 미이라, 잃어버린세계를 찾아서..등등 좋아하니까 1 <NA>
20 8189082 역시 난 덴젤의 연기가 좋다. 점점 주름이 늘어나는 모습에 슬플뿐ㅠㅠ… 1 <NA>
그런데, 이번에는 “평점”, “영화” 두 단어를 동시에 찾아 새로운 변수로 넣는 경우를 상정해보자. 이를 위해서 필요한 동사가 extract()
가 되고 앞서 학습한 정규표현식 그룹의 개념이 포함된다. 다음과 같이 review
데이터프레임에서 document
컬럼을 뽑아 그룹으로 정규표현식을 구성하고 이를 into =
의 새로운 칼럼으로 넣는다.
review %>%
extract(col = "document",
into = c("is_score", "is_movie"),
regex = "(평점).*?(영화)",
remove=TRUE)
# A tibble: 20 x 4
id is_score is_movie label
<dbl> <chr> <chr> <dbl>
1 9212028 <NA> <NA> 1
2 1659748 <NA> <NA> 0
3 1077998 <NA> <NA> 1
4 8747167 <NA> <NA> 0
5 6110214 <NA> <NA> 1
6 10276348 <NA> <NA> 1
7 8241466 <NA> <NA> 1
8 7631300 평점 영화 1
9 5453346 <NA> <NA> 0
10 6682980 <NA> <NA> 1
11 3775688 <NA> <NA> 0
12 9677551 <NA> <NA> 1
13 4649132 <NA> <NA> 1
14 8693988 <NA> <NA> 1
15 7616081 <NA> <NA> 1
16 2183087 <NA> <NA> 0
17 728878 <NA> <NA> 1
18 7285865 <NA> <NA> 1
19 9394907 <NA> <NA> 1
20 8189082 <NA> <NA> 1
actors <- c("덴젤", "장국영", "왕가위")
actor_pattern <- glue_collapse(actors, sep = "|")
surronding_text <- "([\\w[:punct:]]+\\s){0,5}"
actor_surrounding_text <- glue(
"{surronding_text}?({actor_pattern})\\s?{surronding_text}"
)
str_extract_all(
review$document,
pattern = actor_surrounding_text
) %>% unlist
[1] "왕가위, 장국영, 비포 선라이즈. 서울의 "
[2] "역시 난 덴젤의 연기가 좋다. 점점 주름이 "