호돌찌의 AI 연구소
Published 2021. 7. 17. 11:43
R 정규표현식 기본문법 Programming/R

정규표현식에 대한 이해


  • 하는 만큼 실력이 느는 Part, 많이 연습하면 외워지겠지만, 매번 검색해서 찾아보는 것을 추천.
  • 패턴(규칙)을 갖는 문자열의 집합을 표현하는 데 사용하는 언어
  • 복잡한 문자열에서 특정한 패턴과 일치하는 문자열을 찾고, 대체하기 위해 사용됨
  • 정규표현식 기능을 많은 프로그래밍 언어에서 제공하지만, 문법에 있어선 프로그래밍 언어간 다소 차이가 있음
  • R에서 사용되는 정규표현식의 특징은 escape 문자가 두번(\\) 사용된다는 것임
  • 정규표현식 내에서 띄어쓰기 하면 인식 X

 

정규표현식 기본 문법 1


한 글자만 해당됨

정규표현식 포함되는 패턴
\\w 영소문자, 영대문자, 숫자, _(언더바) 등 대부분의 문자
\\d 숫자
\\s 공백, \r\n(개행), \t(탭)
\\W \\w의 반대
\\D \\d의 반대
\\S \\s의 반대, 대문자는 모두 반대로 생각하면 된다.
\\p{Hangul} 한글
. 모든 문자 (공백 포함, 단, \r\n 제외)
| 앞 뒤 문자열들을 or 조건으로 지정
[] [ ] 안의 문자들을 'or' 조건으로 지정
[a-z], [A-Z] 영어 소문자, 영어 대문자
[0-9] 숫자
[ㄱ-ㅎ] 한글 자소 단위, 자음
[ㅏ-ㅣ] 한글 자소 단위, 모음
[가-힣] 한글 음절 단위
[ㄱ-힣] 한글
[^] 대괄호 안 패턴 제외, ^ 는 반대의 의미

 

탐욕적 수량자 vs 게으른 수량자


  • '탐욕적(greedy)'의 의미는 정규표현식에 매칭되는 가장 큰 덩어리의 문자열을 반환
  • 이에 반해 '게으른(lazy)'의 의미는 정규표현식에 매칭되는 가장 작은 작은 덩어리의 문자열 반환
  • Example

    [본문] '<p>이것은<br>HTML<br>입니다</p>'

    '<.+>' 적용했을 때 Greedy 결과 : '<p>이것은<br>HTML<br>입니다</p>'

    '<.+?>' 적용했을 때 Lazy 결과 : '<p>' , '<br>' , '<br>' , '</p>'

 

 

정규표현식 기본 문법 2


 

정규표현식 포함되는 패턴 비고
+ 앞 패턴이 1 ~ 무한대 연속 일치 탐욕적 수량자
* 앞 패턴이 0 ~ 무한대 연속 일치 탐욕적 수량자
? 앞 패턴이 0 ~ 1 번 일치 탐욕적 수량자
{n} 앞 패턴이 정확하게 n 번 연속 일치 탐욕적 수량자
{n,} 앞 패턴이 n ~ 무한대 연속 일치 탐욕적 수량자
{n,m} 앞 패턴이 n ~ m 번 연속 일치 탐욕적 수량자
게으른 수량자 탐욕적 수량자 뒤에 ' ? ' 를 붙임 게으른 수량자
\\메타 문자 메타 문자 모습 그대로 지정('\\[' 하면 '[' 로 인식)  
^ 문자열의 시작 위치를 지정  
$ 문자열의 끝 위치를 지정  
\\b 문자열 중 음절의 시작 위치를 지정  
\\B \\b의 반대  
() '()' 안 문자열을 그룹으로 지정  
\\숫자 그룹의 번호를 가리킴(역참조)  
A(? = B) B 패턴 앞의 A 패턴 지정 (전방탐색)  
(?< = A) B A 패턴 뒤의 B 패턴 지정 (후방탐색)  

Example


str_extract_all을 이용해서 패턴대로 추출을 해보자

> library(stringr)
# 자음 자소 단위 추출
> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㄱ-ㅎ]')
[[1]]
[1] "ㅋ" "ㅋ" "ㅋ"

[[2]]
[1] "ㅎ" "ㅎ" "ㅎ"

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㅏ-ㅣ]')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
[1] "ㅜ" "ㅜ" "ㅜ"

[[4]]
[1] "ㅠ" "ㅠ" "ㅠ"

[[5]]
character(0)

[[6]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[가-힣]')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
character(0)

[[4]]
character(0)

[[5]]
[1] "쿠" "쿠"

[[6]]
[1] "히" "히"

> 
> # + 붙이면 붙어서 나온다 , greedy 수량자 이용 
> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㄱ-ㅎ]+')
[[1]]
[1] "ㅋㅋㅋ"

[[2]]
[1] "ㅎㅎㅎ"

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㅏ-ㅣ]+')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
[1] "ㅜㅜㅜ"

[[4]]
[1] "ㅠㅠㅠ"

[[5]]
character(0)

[[6]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[가-힣]+')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
character(0)

[[4]]
character(0)

[[5]]
[1] "쿠쿠"

[[6]]
[1] "히히"

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㄱ-힣]+') # 자소, 음절 모두 반영 
[[1]]
[1] "ㅋㅋㅋ"

[[2]]
[1] "ㅎㅎㅎ"

[[3]]
[1] "ㅜㅜㅜ"

[[4]]
[1] "ㅠㅠㅠ"

[[5]]
[1] "쿠쿠"

[[6]]
[1] "히히"

> 
> # * 를 붙이면 NULL대신 값이 없게끔 나머지 나타나네요
> # * 은 앞 패턴이 0 ~ 무한대 연속 일치 
> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㄱ-ㅎ]*')
[[1]]
[1] "ㅋㅋㅋ" ""      

[[2]]
[1] "ㅎㅎㅎ" ""      

[[3]]
[1] "" "" "" ""

[[4]]
[1] "" "" "" ""

[[5]]
[1] "" "" ""

[[6]]
[1] "" "" ""

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㅏ-ㅣ]*')
[[1]]
[1] "" "" "" ""

[[2]]
[1] "" "" "" ""

[[3]]
[1] "ㅜㅜㅜ" ""      

[[4]]
[1] "ㅠㅠㅠ" ""      

[[5]]
[1] "" "" ""

[[6]]
[1] "" "" ""

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[가-힣]*')
[[1]]
[1] "" "" "" ""

[[2]]
[1] "" "" "" ""

[[3]]
[1] "" "" "" ""

[[4]]
[1] "" "" "" ""

[[5]]
[1] "쿠쿠" ""    

[[6]]
[1] "히히" ""    

> 
> # ?는 greedy 수량자 뒤에 붙여서 사용 (lazy 수량자)
> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㄱ-ㅎ]?')
[[1]]
[1] "ㅋ" "ㅋ" "ㅋ" ""  

[[2]]
[1] "ㅎ" "ㅎ" "ㅎ" ""  

[[3]]
[1] "" "" "" ""

[[4]]
[1] "" "" "" ""

[[5]]
[1] "" "" ""

[[6]]
[1] "" "" ""

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[ㅏ-ㅣ]?')
[[1]]
[1] "" "" "" ""

[[2]]
[1] "" "" "" ""

[[3]]
[1] "ㅜ" "ㅜ" "ㅜ" ""  

[[4]]
[1] "ㅠ" "ㅠ" "ㅠ" ""  

[[5]]
[1] "" "" ""

[[6]]
[1] "" "" ""

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히'),
+                 pattern = '[가-힣]?')
[[1]]
[1] "" "" "" ""

[[2]]
[1] "" "" "" ""

[[3]]
[1] "" "" "" ""

[[4]]
[1] "" "" "" ""

[[5]]
[1] "쿠" "쿠" ""  

[[6]]
[1] "히" "히" ""  

> 
> # 전화번호, 주민등록번호  같은 것은 {}를 이용하자 
> # 정규표현식에는 , 사이에 공백을 두어선 아니됩니다. 
> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히','하하하','크크크크'),
+                 pattern = '[ㄱ-ㅎ]{2,3}')
[[1]]
[1] "ㅋㅋㅋ"

[[2]]
[1] "ㅎㅎㅎ"

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

[[7]]
character(0)

[[8]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히','하하하','크크크크'),
+                 pattern = '[ㄱ-ㅎ]{2}')
[[1]]
[1] "ㅋㅋ"

[[2]]
[1] "ㅎㅎ"

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

[[7]]
character(0)

[[8]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히','하하하','크크크크'),
+                 pattern = '[ㄱ-ㅎ]{3}')
[[1]]
[1] "ㅋㅋㅋ"

[[2]]
[1] "ㅎㅎㅎ"

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

[[7]]
character(0)

[[8]]
character(0)

> str_extract_all(c('ㅋㅋㅋ','ㅎㅎㅎ','ㅜㅜㅜ','ㅠㅠㅠ','쿠쿠','히히','하하하','크크크크'),
+                 pattern = '[가-힣]{3}')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

[[7]]
[1] "하하하"

[[8]]
[1] "크크크"

> 
> 
> # greedy numerator : 패턴을 만족하는 최대한의 아웃풋을 들고옴
> # lazy numerator : 패턴을 만족하는 최소한의 아웃풋을 들고옴
> 
> str_extract_all(c('<p>지금<bk>나는<br>배가<bl>아픕니다</p>'),
+                 pattern = '<.+?>')
[[1]]
[1] "<p>"  "<bk>" "<br>" "<bl>" "</p>"

> 
> str_extract_all(c('[p]지금[bk]나는[br]배가[bl]아픕니다[/p]'),
+                 pattern = '[.+?]') # error뜬당
[[1]]
character(0)

> 
> str_extract_all(c('[p]지금[bk]나는[br]배가[bl]아픕니다[/p]'),
+                 pattern = '\\[.+?]') # 메타문자 지정을 해줘야한다. 
[[1]]
[1] "[p]"  "[bk]" "[br]" "[bl]" "[/p]"

> 
> # 문자열 시작과 중간과 끝!!
> str_extract_all(c('가방','가면','누가','뭐가','누가바','뭐가요'),
+                 '^가')
[[1]]
[1] "가"

[[2]]
[1] "가"

[[3]]
character(0)

[[4]]
character(0)

[[5]]
character(0)

[[6]]
character(0)

> 
> str_extract_all(c('가방','가면','누가','뭐가','누가바','뭐가요'),
+                 '가$')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
[1] "가"

[[4]]
[1] "가"

[[5]]
character(0)

[[6]]
character(0)

> 
> str_extract_all(c('가방','가면','누가','뭐가','누가바','뭐가요'),
+                 '.가.')
[[1]]
character(0)

[[2]]
character(0)

[[3]]
character(0)

[[4]]
character(0)

[[5]]
[1] "누가바"

[[6]]
[1] "뭐가요"

> 
> # 전 후방 탐색
> str_extract_all(c('100원','200엔','300위안','400달러'),
+                 '\\d+')
[[1]]
[1] "100"

[[2]]
[1] "200"

[[3]]
[1] "300"

[[4]]
[1] "400"

> 
> # 원만 들고 오고 싶은데 숫자만 나오고 원은 안나오게
> str_extract_all(c('100원','200엔','300위안','400달러'),
+                 '\\d+원') %>% 
+     str_remove(pattern = '[가-힣]') # 이거 좀 비효율적
[1] "100"          "character(0)" "character(0)" "character(0)"
> 
> # 전방탐색 , 원에서 100만 보고 싶어요 
> str_extract_all(c('100원','200엔','300위안','400달러'),
+                 '\\d+(?=원)')
[[1]]
[1] "100"

[[2]]
character(0)

[[3]]
character(0)

[[4]]
character(0)

> 
> # 후방탐색 , 200인데 화폐단위만 보고 싶어요 
> str_extract_all(c('100원','200엔','300위안','400달러'),
+                 '(?<=200)[가-힣]+')
[[1]]
character(0)

[[2]]
[1] "엔"

[[3]]
character(0)

[[4]]
character(0)

 

 

 

 

profile

호돌찌의 AI 연구소

@hotorch's AI Labs

포스팅이 도움이 되셨다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!