정규식

정규식 (Regular Expressions)

DuckDB는 [패턴 매칭 연산자]({% link docs/current/sql/functions/pattern_matching.md %}) ([LIKE]({% link docs/current/sql/functions/pattern_matching.md %}#like), [SIMILAR TO]({% link docs/current/sql/functions/pattern_matching.md %}#similar-to), [GLOB]({% link docs/current/sql/functions/pattern_matching.md %}#glob))뿐만 아니라 함수를 통한 정규식 지원도 제공해요.

출처: 문서

본문

정규식 문법 (Regular Expression Syntax)

DuckDB는 RE2 라이브러리를 정규식 엔진으로 사용해요. 정규식 문법에 대해서는 RE2 문서를 참고해요.

함수 (Functions)

모든 함수는 선택적 옵션 집합을 받아요.

이름 설명
regexp_extract(string, pattern[, group = 0][, options]) string이 정규식 pattern을 포함하면 선택적 파라미터 group이 지정한 캡처링 그룹을 반환하고, 그렇지 않으면 빈 문자열을 반환해요. group은 상수 값이어야 해요. group이 없으면 기본값 0이에요. 선택적 options 집합을 설정할 수 있어요.
regexp_extract(string, pattern, name_list[, options]) string이 정규식 pattern을 포함하면 name_list의 이름에 대응하는 캡처링 그룹들을 struct로 반환하고, 그렇지 않으면 같은 키와 빈 문자열 값을 가진 struct를 반환해요.
regexp_extract_all(string, regex[, group = 0][, options]) string에서 regex의 겹치지 않는 발생을 찾고 group에 해당하는 값을 반환해요.
regexp_extract_all(string, regex, name_list[, options]) string에서 regex의 겹치지 않는 발생을 찾고 name_list의 이름에 대응하는 캡처링 그룹들을 struct 리스트로 반환해요.
regexp_full_match(string, regex[, options]) 전체 stringregex와 일치하면 true를 반환해요.
regexp_matches(string, pattern[, options]) string이 정규식 pattern을 포함하면 true, 그렇지 않으면 false를 반환해요.
regexp_replace(string, pattern, replacement[, options]) string이 정규식 pattern을 포함하면 매치된 부분을 replacement로 교체해요. 기본적으로 첫 번째 발생만 교체돼요. 전역 플래그 g를 포함한 선택적 options 집합을 설정할 수 있어요.
regexp_split_to_array(string, regex[, options]) string_split_regex의 별칭이에요. regex를 따라 string을 분할해요.
regexp_split_to_table(string, regex[, options]) regex를 따라 string을 분할하고 각 부분에 대해 행을 반환해요.

regexp_extract(string, pattern[, group = 0][, options])

| 설명 | string이 정규식 pattern을 포함하면 선택적 파라미터 group이 지정한 캡처링 그룹을 반환하고, 그렇지 않으면 빈 문자열을 반환해요. group은 상수 값이어야 해요. group이 없으면 기본값 0이에요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_extract('abc', '([a-z])(b)', 1) | | 결과 | a |

regexp_extract(string, pattern, name_list[, options])

| 설명 | string이 정규식 pattern을 포함하면 name_list의 이름에 대응하는 캡처링 그룹들을 struct로 반환하고, 그렇지 않으면 같은 키와 빈 문자열 값을 가진 struct를 반환해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_extract('2023-04-15', '(\d+)-(\d+)-(\d+)', ['y', 'm', 'd']) | | 결과 | {'y':'2023', 'm':'04', 'd':'15'} |

regexp_extract_all(string, regex[, group = 0][, options])

| 설명 | string에서 regex의 겹치지 않는 발생을 찾고 group에 해당하는 값을 반환해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_extract_all('Peter: 33, Paul:14', '(\w+):\s*(\d+)', 2) | | 결과 | [33, 14] |

regexp_extract_all(string, regex, name_list[, options])

| 설명 | string에서 regex의 겹치지 않는 발생을 찾고 name_list의 이름에 대응하는 캡처링 그룹들을 struct 리스트로 반환해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_extract_all('Peter: 33, Paul: 14', '(\w+):\s*(\d+)', ['name', 'age']) | | 결과 | [{'name': Peter, 'age': 33}, {'name': Paul, 'age': 14}] |

regexp_full_match(string, regex[, options])

| 설명 | 전체 stringregex와 일치하면 true를 반환해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_full_match('anabanana', '(an)*') | | 결과 | false |

regexp_matches(string, pattern[, options])

| 설명 | string이 정규식 pattern을 포함하면 true, 그렇지 않으면 false를 반환해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_matches('anabanana', '(an)*') | | 결과 | true |

regexp_replace(string, pattern, replacement[, options])

| 설명 | string이 정규식 pattern을 포함하면 매치된 부분을 replacement로 교체해요. 기본적으로 첫 번째 발생만 교체돼요. 전역 플래그 g를 포함한 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_replace('hello', '[lo]', '-') | | 결과 | he-lo |

regexp_split_to_array(string, regex[, options])

| 설명 | string_split_regex의 별칭이에요. regex를 따라 string을 분할해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_split_to_array('hello world; 42', ';? ') | | 결과 | ['hello', 'world', '42'] |

regexp_split_to_table(string, regex[, options])

| 설명 | regex를 따라 string을 분할하고 각 부분에 대해 행을 반환해요. 선택적 options 집합을 설정할 수 있어요. | | 예시 | regexp_split_to_table('hello world; 42', ';? ') | | 결과 | 세 행: 'hello', 'world', '42' |

regexp_matches 함수는 SIMILAR TO 연산자와 비슷하지만, 전체 문자열이 일치할 것을 요구하지 않아요. 대신 regexp_matches는 문자열이 패턴을 포함하기만 하면 true를 반환해요 (정규식을 문자열의 시작과 끝에 고정하는 특수 토큰 ^$를 사용하지 않는 한). 몇 가지 예를 볼게요:

SELECT regexp_matches('abc', 'abc');       -- true
SELECT regexp_matches('abc', '^abc$');     -- true
SELECT regexp_matches('abc', 'a');         -- true
SELECT regexp_matches('abc', '^a$');       -- false
SELECT regexp_matches('abc', '.*(b|d).*'); -- true
SELECT regexp_matches('abc', '(b|c).*');   -- true
SELECT regexp_matches('abc', '^(b|c).*');  -- false
SELECT regexp_matches('abc', '(?i)A');     -- true
SELECT regexp_matches('abc', 'A', 'i');    -- true

정규식 함수 옵션 (Options for Regular Expression Functions)

정규식 함수는 다음 options를 지원해요.

옵션 설명
'c' 대소문자 구분 매칭
'i' 대소문자 구분 없는 매칭
'l' 정규식 토큰 대신 리터럴 매칭
'm', 'n', 'p' 개행 구분 매칭
'g' 전역 교체, regexp_replace에서만 사용 가능
's' 개행 구분 없는 매칭

예를 들어:

SELECT regexp_matches('abcd', 'ABC', 'c'); -- false
SELECT regexp_matches('abcd', 'ABC', 'i'); -- true
SELECT regexp_matches('ab^/$cd', '^/$', 'l'); -- true
SELECT regexp_matches(E'hello\nworld', 'hello.world', 'p'); -- false
SELECT regexp_matches(E'hello\nworld', 'hello.world', 's'); -- true

regexp_matches 사용

regexp_matches 연산자는 가능하면 LIKE 연산자로 최적화돼요. 최고 성능을 얻으려면 해당되는 경우 'c' 옵션(대소문자 구분 매칭)을 전달해야 해요. 기본적으로 RE2 라이브러리. 문자를 개행과 매치하지 않아요.

원본 최적화된 동등 형태
regexp_matches('hello world', '^hello', 'c') prefix('hello world', 'hello')
regexp_matches('hello world', 'world$', 'c') suffix('hello world', 'world')
regexp_matches('hello world', 'hello.world', 'c') LIKE 'hello_world'
regexp_matches('hello world', 'he.*rld', 'c') LIKE '%he%rld'

regexp_replace 사용

regexp_replace 함수는 정규식 패턴과 일치하는 문자열 부분을 교체 문자열로 바꾸는 데 사용해요. 교체 문자열에서 정규식으로 캡처된 그룹을 참조하려면 \d 표기법(d는 그룹을 나타내는 숫자)을 사용할 수 있어요. 기본적으로 regexp_replace는 정규식의 첫 번째 발생만 교체한다는 점을 참고해요. 모든 발생을 교체하려면 전역 교체(g) 플래그를 사용해요.

regexp_replace 사용 예시:

SELECT regexp_replace('abc', '(b|c)', 'X');        -- aXc
SELECT regexp_replace('abc', '(b|c)', 'X', 'g');   -- aXX
SELECT regexp_replace('abc', '(b|c)', '\1\1\1\1'); -- abbbbc
SELECT regexp_replace('abc', '(.*)c', '\1e');      -- abe
SELECT regexp_replace('abc', '(a)(b)', '\2\1');    -- bac

regexp_extract 사용

regexp_extract 함수는 정규식 패턴과 일치하는 문자열의 일부를 추출하는 데 사용해요. 패턴 내 특정 캡처링 그룹은 group 파라미터로 추출할 수 있어요. group을 지정하지 않으면 기본값 0으로, 전체 패턴으로 첫 번째 매치를 추출해요.

SELECT regexp_extract('abc', '.b.');           -- abc
SELECT regexp_extract('abc', '.b.', 0);        -- abc
SELECT regexp_extract('abc', '.b.', 1);        -- (empty)
SELECT regexp_extract('abc', '([a-z])(b)', 1); -- a
SELECT regexp_extract('abc', '([a-z])(b)', 2); -- b

regexp_extract 함수는 name_list 인자도 지원하는데, 이는 문자열의 LIST예요. name_list를 사용하면 regexp_extract가 해당 캡처 그룹들을 STRUCT의 필드로 반환해요:

SELECT regexp_extract('2023-04-15', '(\d+)-(\d+)-(\d+)', ['y', 'm', 'd']);
{'y': 2023, 'm': 04, 'd': 15}
SELECT regexp_extract('2023-04-15 07:59:56', '^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)', ['y', 'm', 'd']);
{'y': 2023, 'm': 04, 'd': 15}
SELECT regexp_extract('duckdb_0_7_1', '^(\w+)_(\d+)_(\d+)', ['tool', 'major', 'minor', 'fix']);
Binder Error:
Not enough group names in regexp_extract

열 이름의 개수가 캡처 그룹의 개수보다 적으면 첫 번째 그룹들만 반환돼요. 열 이름의 개수가 더 많으면 오류가 생성돼요.

제한 사항 (Limitations)

정규식은 캡처 그룹 9개만 지원해요: \1, \2, \3, ..., \9. 두 자리 이상의 캡처 그룹은 지원되지 않아요.

더 알아보기 (Learn more)

패턴 매칭 연산자 전반에 대해서는 [Pattern Matching]({% link docs/current/sql/functions/pattern_matching.md %}) 문서를 참고해요.