『리눅스 학당-리눅스 강좌 / 연재 (go LINUX)』 467번 제 목:정규표현식 프로그래밍 강좌 [마지막] 올린이:엠브리오(유형목 ) 97/06/15 13:46 읽음:1809 관련자료 없음 ----------------------------------------------------------------------------- 한동훈님의 정규식 라이브러리 마지막 강좌입니다. ----------------------------------------------------------------------- #622 한동훈 (ddoch ) [강좌] Regex (정규표현식) 라이브러리 (끝 06/09 17:01 459 line GNU REGEX (정규표현식) 프로그래밍 강좌 (5) ------------------------------------------- 6.3 GNU Regex 함수 ------------------ 특별히 POSIX 나 버클리 UNIX 에 호환성을 생각하지 않아도 된다면, GNU regex 함수를 사용하는 것이 여러모로 좋을 지 모르겠습니다. GNU regex 함수도 이전에 설명드린 POSIX 나 BSD regex 함수의 기능을 포함하 고 나머지 여러개의 복잡한 기능을 추가한 것입니다. 그럼, 하나씩 알아보도록 하겠습니다. 6.3.1 GNU 패턴 버퍼 -------------------- GNU regex 는 GNU 패턴 버퍼를 이용하여 컴파일된 정규표현식을 활용합니다. 이 패턴 버퍼는 POSIX regex 에서 설명하였으므로 건너뛰겠습니다. 6.3.2 GNU 정규표현식 컴파일 ---------------------------- GNU regex 에서는 정규표현식을 검색하고 매칭하는 것을 둘다 할 수 있습니다. GNU regex 에서도 POSIX 나 BSD regex 처럼, 먼저 정규표현식을 컴파일하여, 패턴 버퍼에 마련해 두어야 합니다. 이전과 마찬가지로 패턴버퍼는 어떤 문법으로 컴파 일되느냐에 따라 매칭이나 검색의 결과가 달라지게 마련입니다. 이러한 문법을 지 정하는 변수는 re_syntax_options 입니다. 따라서 컴파일을 하기전에 정확한 문법 을 세팅해 두는 것이 중요합니다. GNU regex 에서 패턴을 컴파일하는 것은, re_compile_pattern 입니다. re_compile_pattern 은 패턴버퍼를 인자로 취하는 데, 패턴 버퍼의 다음의 필드 는 초기화를 시켜주어야 합니다. translate initialization translate 매칭이나 검색이전에 적용되는 변환테이블을 사용한다면 그 변환테이블 에 대한 포인터로 초기화 시키줍니다. 변환테이블이 없다면 NULL로 초 기화 시켜주면 됩니다. translate 는 GNU 패턴버퍼에서 char * 형 필드 임을 상기하세요. 변환 테이블에 대한 이야기는 뒷쪽에서 설명하겠습니다. fastmap fastmap (re_search 로 빠른 검색에 사용됨) 을 사용하려면 그 포인터를 지정하면 되며, 필요없다면 NULL로 지정하면 됩니다. 이 또한 char * 필 드 입니다. buffer allocated re_compile_pattern 으로 컴파일된 패턴에 필요한 메모리를 할당하고 자 할 경우에는 둘다 0이나 NULL로 초기화 하면 됩니다. (buffer 는 unsigned char *, allocated 는 unsignedlong 형입니다. 0이나 NULL이 나 결국에는 0입니다.) 여러분들이 이미 할당한 메모리 블록을 Regex 에 사용하려면, buffer 는 그것의 주소로, allocated 는 블록의 바이트 크기로 설정하면 됩니다. re_compile_pattern 은 컴파일된 패턴에 필요하다면 메모리를 확장하기 위해서 realloc 를 사용합니다. 패턴을 컴파일 하려면 다음과 같이 사용하면 됩니다. char *re_compile_pattern (const char *regex, const int regex_size, struct re_pattern_buffer *pattern_buffer) 'regex' 는 정규표현식 문자열의 주소이고, 'regex_size' 는 그것의 길이입니다. pattern_buffer 는 패턴버퍼의 주소입니다. re_compile_pattern 이 성공적으로 해당 정규표현식을 컴파일하였다면 0(NULL)을 리턴하고, *pattern_buffer 를 컴파일된 패턴으로 설정을 합니다. 아울러 아래의 패턴버퍼 내의 필드를 세팅합니다. buffer 컴파일된 패턴 used buffer가 가르키는 곳에서 사용중인 바이트 syntax re_syntax_options 의 현재값 re_nsub 'regex' 에서 보조표현식의 갯수 fastmap_accurate re_compile_pattern 이 'regex' 를 컴파일 할 수 없다면, '6.2.2 POSIX 정규표현식' 에서 설명한 에러 문자열을 돌려줍니다. 6.3.3 GNU 매칭 --------------- GNU 매칭은 문자열속에서 가능한한 시작위치에서 명시된데로 매칭을 시킵니다. 한번 패턴을 패턴버퍼로 컴파일을 했다면, 문자열에서 패턴을 매칭 시킬수 있습니 다. int re_match (struct re_pattern_buffer *pattern_buffer, const char *string, const int size, const int start, struct re_registers *regs) pattern_buffer 은 컴파일된 패턴버퍼의 주소이고, string 은 매칭을 하고자 하는 문자열입니다. 이 문자열에는 NULL 이나 newline 을 포함할 수 있습니다. size 는 그 문자열의 길이이며, start 는 매칭하기를 원하는 문자열속의 인덱스(문자열 첫 문자의 인덱스는 0)입니다. re_match 는 pattern_buffer 의 syntax 필드의 문법에 따라, 문자열 string 을, pattern_buffer의 정규 표현식과 매칭을 시키는 역할을 합니다. 문자열과 매칭할 수 없다면 -1을 리턴하고, 내부적인 에러일 경우에는 -2를, 성공적일 경우에는 문자열과 매칭된 횟수를 돌려줍니다. 예를 들면, pattern_buffer 이 'a*'를 컴파일한 패턴버퍼라고 하고, string 이 'aaaaab'이며, 따라서 size는 6이 되고, start 는 2라고 가정한다면, re_match 는 3을 리턴합니다. 'a*' 는 문자열에서 마지막 세개의 'a'를 매칭시킬 것입니다. start 가 0이라고 한다면, re_match 는 5를 리턴합니다. start 가 5나 6일 경우 에는 0을 반환합니다. start 가 0에서 size 사이가 아니라면, re_match 는 -1을 반환합니다. 6.3.4 GNU 검색 --------------- 검색하는 데 사용되는 함수는 re_search 입니다. re_search 를 사용하기 전에 정규표현식을 컴파일 하셔야 겠죠? re_search 의 정의는 다음과 같습니다. int re_search (struct re_pattern_buffer *pattern_buffer, const char *string, const int size, const int start, const int range, struct re_registers *regs) 이 인자들은 re_match 와 유사합니다. 여기서 start 와 range 는 re_match 의 start 를 대응합니다. range 가 양수이면, re_search 는 인덱스 start 에서 최초의 매칭을 시작하며 실패할 경우 start+1 에서 검색을 하며 계속 하나씩 나아가서 start+range 까지 수행합니다. range 가 음수라면, 인덱스 start 에서 첫 매칭을 수행하며, 이후에 -1씩 위치를 반대로 옮겨서 수행합니다. start 가 0에서 size 사이가 아니라면, re_search 는 -1을 돌려줍니다. range 가 양수일 경우에는 re_search 는, 필요하다면 range 를 조절해서 start+range-1 이 0에서 size 사이가 되도록 하여 검색이 문자열 바깥으로 나가지 못하도록 합니다. 유사하게, range 가 음수라면, re_search 는 범위를 start+range+1 이 0에서 size 사이가 되도록 필요할 경우 조절하게 됩니다. 패턴버퍼의 fastmap 필드가 NULL 이라면, re_search 는 연속적인 위치로 매칭을 시 작하며, NULL 이 아니라면, fastmap 을 사용하여 좀 더 효율적으로 검색을 수행합 니다. 매칭이 한번도 되지 않는다면, re_search 는 -1을 반환하고, 매칭이 된다면 매칭이 시작된 위치의 인덱스를 돌려주며, 내부에러일 경우에는 -2를 돌려줍니다. 6.3.5 분리된 데이터로 매칭과 검색하기 -------------------------------------- re_match_2 와 re_search_2 를 사용하면, 두개의 문자열로 나누어진 데이터를 매칭 하거나 검색할 수 있습니다. int re_match_2 (struct re_pattern_buffer *buffer, const char *string1, const int size1, const char *string2, const int size2, const int start, struct re_registers *regs, const int stop) 이 함수는 re_match 와, 두개 데이터의 문자열과 크기를 넘겨주고, 이후의 매칭을 원하지 않을 경우의 인덱스 stop 을 제외하면 유사합니다. re_match 처럼, re_match_2 가 성공적으로 수행되었다면, 문자열 string 에서 매 칭된 횟수를 돌려줍니다. re_match 는 string1 과 string2 를, start 와 stop 인 자를 설정하여 regs 를 사용할 때 에는 연속된 것으로 취급합니다. int re_search_2 (struct re_pattern_buffer *buffer, const char *string1, const int size1, const char *string2, const int size2, const int start, struct re_registers *regs, const int stop) 이것은 re_search 함수와 유사합니다. 6.3.6 fastmap 으로 검색하기 ---------------------------- 몇 십만바이트 이상 되는 문자열에서 검색을 하려면 fastmap 을 사용해야 합니다. 순차적으로 연속적인 위치에서 검색을 한다면 아마도 상당한 시간이 걸릴 것입니다 . fastmap 은 내부적인 알고리즘을 유지하면서 최적의 검색을 수행합니다. 문자열 검색 시 효율을 높이기 위한 알고리즘은 많이 우리들에게 알려져 있습니다. 그들의 많은 부분들은 strstr과 같이 순차적으로 검색하는 것이 아니라 검색의 효 율을 높이기 위해서 내부의 테이블을 갖추고 현재 위치의 문자가 검색의 시작점이 될 수 있는지를 검사하며 최대한의 포인터를 건너뛰도록 설계된 경우가 있습니다. fastmap 을 이러한 역할을 하는 테이블에 대한 포인터입니다. 즉, 여러분들의 문자셋(아스키문자 등)으로 인덱스된 하나의 배열입니다. 아스키 encoding 하에서는, 따라서, fastmap 은 256 개의 원소를 가집니다. 주어진 패턴 버퍼에 있어서 검색시 fastmap 을 사용하려고 할 때에는, 먼저 배열을 할당하고 배열의 주소를 패턴버퍼의 fastmap 에 지정해야 합니다. fastmap 은 일반적으로 사 용자가 직접 컴파일하거나 또는 re_search가 대신 할 수도 있습니다.fastmap 이 어떤 테이블을 가르키고 있다면, re_search 는, 컴파일된 패턴버퍼를 사용한 검색을 하기 이전에, 먼저 fastmap 을 자동적으로 컴파일합니다. 직접 수동으로 하려면 다음과 같이 사용하면 됩니다. int re_compile_fastmap (struct re_pattern_buffer *pattern_buffer) pattern_buffer 은 패턴버퍼의 주소입니다. 어떠한 문자 c 가 매칭에 있어서 시작 점이 될 수 있다면, re_compile_fastmap 은 'pattern_buffer->fastmap[c]'를 0이 아 닌 수로 지정을 합니다. 이 함수가 fastmap 을 컴파일 할 수 있다면 0을 리턴하고,내부에러일 경우에는 -2 를 리턴합니다. 예를 든다면, 패턴버퍼 pattern_buffer 가 'a|b' 를 컴파일한 패턴 을 보유하고 있다면, re_compile_fastmap 은 fastmap['a'] 와 fastmap['b'] 를 세트 한다는 것입니다. 'a' 와 'b' 는 매칭의 시작점이 될 수 있으니까요.. re_search 는 문자열의 각 원소들 중에서 fastmap 에 있는 것 중의 하나가 나올때 까지 차례로 비교합니다. 그리고 나서 그 문자에서부터 매칭을 시도합니다. 매칭이 실패할 경우에는 이러한 처리를 반복합니다. 따라서 이렇게 fastmap 을 사용할 경 우, re_search 는 매칭의 시작점이 될 수 없는 문자열의 위치에서 쓸데 없이 매칭 하려고 하는 시도를 줄임으로써 시간을 절약할 수 있는 것입니다. fastmap 을 re_search 에서 사용하기를 원치 않을 경우에는 fastmap 필드에 NULL (0)을 저장하면 됩니다. 물론 re_search 사용이전에 말이죠... 패턴버퍼의 fastmap 필드를 한번 초기화 했다면 다시 fastmap 을 컴파일 할 필요는 없습니다. re_search 는 fastmap 이 NULL 이면 컴파일을 하지 않으며, NULL 이 아 니라면 새로운 패턴에 새로운 fastmap 을 컴파일합니다. 6.3.7 GNU 변환 테이블 ---------------------- 패턴버퍼의 translate 필드를 변환 테이블로 설정하였다면, Regex 는 찾는 모든 문자열과 정규표현식에서 간단한 변환을 하기 위해 translate 로 지정된 변환 테이블을 사용합니다. "변환테이블" 은 아스키와 같은 문자세트의 원소들로 인덱스된 배열입니다. 따라서 아스키코드에서는 변환테이블은 256개의 원소를 가집니다. 이 배열의 원소 들도 마찬가지로 여러분의 문자세트에 포함이 됩니다. Regex 함수가 문자 c를 만 났다면, 문자 c 대신 translate[c] 를 사용합니다. 가령 대소문자를 무시한 Regex 일 경우, translate['A'] = 'A'; translate['a'] = 'A'; translate['B'] = 'B'; translate['b'] = 'B'; ............ 이렇게 값이 초기화 되어 있다면, 이후의 Regex 의 검색시 'a' 문자를 만난다면, 'a' 를 변환테이블의 인덱스로 하여 해당값으로 대신한다는 이야기입니다. ( translate['a'] 의 값은 'A' 이므로 'a' 대신 'A' 의 값을 적용함. ) 그러나, 단한가지의 예외가 있다면, '\' 문자 뒤에 따라오는 문자는 변환하지 않 는다는 것입니다. '\' 문자가 이스케이프의 역할을 한다면, '\B' 와 '\b' 는 항 상 구별되는 것입니다. 이제 위와 같이, 소문자를 대문자로 변환하는, 대소문자를 무시하는 변환테이블을 초기화 하는 예를 보이겠습니다. (메뉴얼에 나와 있는 내용입니다. ^^) struct re_pattern_buffer pb; /* 패턴 버퍼 */ char case_fold[256]; /* 변환 테이블 */ for (i = 0; i < 256; i++) case_fold[i] = i; for (i = 'a'; i <= 'z'; i++) /* 소문자를 대문자로 변환 */ case_fold[i] = i - ('a' -'A'); pb.translate = case_fold; 이렇게 translate 에 변환 테이블의 주소를 지정하면 이후에변환테이블을 사용합 니다. 변환테이블을 사용하고 싶지 않다면 translate 에 NULL 을 넣어주시면 됩 니다. 만일, 패턴버퍼를 컴파일할 때나, fastmap 을 컴파일할 때, 패턴버퍼로 매칭 이나 검색을 수행할 때 이러한 테이블의 내용을 바꾼다면 이상한 결과를 얻을 것 입니다. 6.3.8 레지스터 사용하기 ------------------------ 사실 이 부분이 regex 에서 중요한 부분입니다. 지금까지는 regex 를 사용하여 어떤 문자열내에 해당 패턴(정규표현식)이 있느냐 없느냐만 따졌으나 이 레지스터를 사용하면 세부 매칭의 결과를 저장하게 됩니다. 즉, 문자열 인덱스 어디에서 어디까지 패턴과 매칭이 되었는지에 대한 정보를 확 보함으로써 나아가서는 문자열 치환 작업까지도 생각할 수 있습니다. 정규표현식에서 하나의 그룹은 전체적으로 정규표현식과 매칭되는 문자열의 하나 의 부분문자열과 매칭할 수 있습니다. 매칭작업을 수행할 때 각각의 그룹과 매칭 된 보조문자열의 시작과 끝이 기억됩니다. 이러한 검색이나 매칭시에는 GNU 매칭 및 검색 함수에 0이 아닌 'regs' 인자를 넘 겨줘야 합니다. struct re_registers { unsigned num_regs; regoff_t *start; regoff_t *end; }; 레지스터 옵셋 타입(regoff_t) 는 'int'를 형정의 한 것 입니다. start 와 end 의 i번째 원소는 패턴에서의 i번째 그룹에 대한 정보를 기록합니다. 이 start 와 end 는 다양한 방법으로 할당되는 데, 이것은 패턴버퍼의 regs_allocated 필드에 의존합니다. 제일 간편하고 유용한 방법은 regex 의 매칭함수로 하여금 각각의 그룹에 대한 정 보를 기록할 공간을 충분히 할당하게 하는 것입니다. regs_allocated 가 REGS_UNALLOCATED 라면, 매칭함수는 1+re_nsub(패턴버퍼의 다른 멤버) 만큼을 할당 합니다. 여분의 원소는 -1 로 설정하고, regs_allocated 를 REGS_REALLOCATE 로 설정합니다. 이후에 다시 호출할 경우에, 필요하다면 매칭함수는 공간을 더 할당 할 수 있습니다. re_compile_pattern 은 regs_allocated 를 REGS_UNALLOCATED 로 설정하기 때문에, GNU 정규표현식 함수에서는 위와 같은 행동이 기본으로 되어 있습니다. POSIX 에서는 조금 다릅니다. 호출자에 매칭함수가 채울, 고정길이의 배열을 넘겨 줘야 합니다. 따라서 regs_allocated 가 REGS_FIXED 라면, 매칭함수는 그 고정배 열을 간단하게 채웁니다. 아래의 예제는 re_registers 구조체에 기록되는 정보를 보여줍니다. ('(' 와 ')' 이 그룹오퍼레이터라고 하고, 문자열 string 에서 첫번째 문자의 인덱스를 0이라 하겠습니다.) ㄱ. 정규표현식이 또다른 그룹을 포함하지 않는, i번째 그룹을 가지고 있다면, 함 수는 'regs->start[i]' 에 그룹과 매칭하는 보조문자열의 시작 인덱스를 저장 하고, 'regs->end[i]' 에는 보조문자열의 끝 인덱스를 저장합니다. 'regs->start[0]'과 'regs->end[0]' 에는 전체 패턴에 대한 정보가 들어갑니다. 예를 들면, 'ab' 에 대해 '((a)(b))' 를 매칭시킨다면, 다음의 결과를 얻을 것입 니다. * 0 in `regs->start[0]' and 2 in `regs->end[0]' * 0 in `regs->start[1]' and 2 in `regs->end[1]' * 0 in `regs->start[2]' and 1 in `regs->end[2]' * 1 in `regs->start[3]' and 2 in `regs->end[3]' ㄴ. 그룹이 반복오퍼레이터 등을 사용하여 한번보다 더 많이 매칭된다면, 함수는 마지막으로 매칭된 그룹에 대한 정보를 저장합니다. 예를 들면, 'aa' 에 대해 '(a)*' 를 매칭시킨다면, 다음의 결과를 얻을 것입니다. * 0 in `regs->start[0]' and 2 in `regs->end[0]' * 1 in `regs->start[1]' and 2 in `regs->end[1]' 여기에서 그룹 1 은 '(a)' 이지만, 뒤의 '*' 오퍼레이터로 인해 'aa' 와는 1번 초과 매칭되므로, 마지막에 매칭되는 'a'에 대한 인덱스를 기록합니다. ㄷ. i번째 그룹이, 어떤 성공적인 매칭에 관여하지 않는다면, 반복오퍼레이터는 0번 반복을 허용하고, 함수는 'regs->start[i]' 와 'regs->end[i]' 를 -1로 채웁니다. 예를 든다면, 'b' 에 대해 '(a)*b' 를 매칭하는 경우는 다음의 결과를 얻을 것 입니다. * 0 in `regs->start[0]' and 1 in `regs->end[0]' * -1 in `regs->start[1]' and -1 in `regs->end[1]' 여기에서 1번째 그룹인 '(a)' 는 매칭에 관여하지 않기 때문에 'regs->start[1]' 과 'regs->end[1]' 은 -1로 됩니다. ㄹ. i번째 그룹이 길이가 0인 문자열을 매칭한다면, 함수는 regs->start[i] 와 regs->end[i] 를 "길이가 0인 문자열"의 인덱스로 설정합니다. 예를 든다면, 'b' 에 대해 '(a*)b' 를 매칭하는 경우는 다음의 결과를 얻을 것 입니다. * 0 in `regs->start[0]' and 1 in `regs->end[0]' * 0 in `regs->start[1]' and 0in `regs->end[1]' 여기에서 '(a*)b' 는 위의 '(a)*b' 와는 다릅니다. 1번째 그룹인 '(a*)' 는 'b' 의 앞부분의 빈 문자열과 매칭이 되므로 regs->start[1]과 regs->end[1] 은 둘다 '0' 이 됩니다. ㅁ. i번째 그룹이 j번째 그룹을 포함하고, j번째 그룹은 i번째 그룹에만 포함되는 경우, 함수는 i번째 그룹의 매칭을 기록하고, regs->start[j] 와 regs->end[j] 에는 j번째 그룹과 마지막으로 매칭된 것에 대한 정보를 기록합니다. 조금 햇갈리기 쉬운 경우인데, 예를 들어보지요.. 'abb' 에 대해 '((a*)b)*'를 매칭시키는 경우를 봅시다. regs->start[0] 과 regs->end[0] 은 당연히 전체 문자열의 정보를 가지므로 0(첫번째 문자 인덱스), 3(시작인덱스 + 길이로 보는 것이 좋을 듯..) 이 됩니다. 1: ((a*)b)* abb : 1번째 그룹 첫 매칭 (0, 2) ^^^^^^^ ^^ ((a*)b)* abb : 2번째 그룹 첫 매칭 (0, 1) ^^^ ^ 처음의 매칭에서, 1번째 그룹은 'ab' 와 매칭되고, 2번째 그룹은 'a'와 매칭 됩니다. ^^^^^^^ 2: ((a*)b)* abb : 1번째 그룹 둘째 매칭 (2, 3) ^ ^ ^^^ ((a*)b)* a b b : 2번째 그룹 둘째 매칭 (2, 2) ^ ^ (빈문자열) 반복 오퍼레이터의 영향으로, 두번째 매칭에서, 1번째 그룹은 'b'와 매칭되고, 2번째 그룹은 마지막 'b' 의 바로 앞의 빈문자열과 매칭됩니다. 따라서, 위의 'ㄴ' 규칙 (그룹이 반복오퍼레이터 등을 사용하여 한번보다 더 많 이 매칭된다면, 함수는 마지막으로 매칭된 그룹에 대한 정보를 저장한다.) 에 따 라, regs->start[1], regs->end[1] 에는 2, 3 이 각각 기록되며, 그룹1은 그룹 2를 포함하고 그룹2는 그룹1에만 포함되기 때문에, 마지막으로 매칭된 2번째 그 룹의 기록값인 2, 2가 각각 regs->start[2] 와 regs->end[2] 에 각각 기록됩니 다. 결과를 정리하면, * 0 in `regs->start[0]' and 3 in `regs->end[0]' * 2 in `regs->start[1]' and 3 in `regs->end[1]' * 2 in `regs->start[2]' and 2 in `regs->end[2]' 'abb' 에 대해 '((a)*b)*' 를 매칭한다면, 그룹2(괄호안쪽의 '(a)') 는 마지막 매칭에 관여하지 않으므로 다음과 같은 결과를 얻습니다. * 0 in `regs->start[0]' and 3 in `regs->end[0]' * 2 in `regs->start[1]' and 3 in `regs->end[1]' * 0 in `regs->start[2]' and 1 in `regs->end[2]' 조금 햇갈리는 분도 계실 것이고, 재미를 느끼는 분도 계실 것입니다. :) ㅂ. 위와 같을 경우에, 매칭이 되지 않을 경우, 함수는 regs->start[i], regs->end[i]와 regs->start[j], regs->end[j] 를 모두 -1 로 설정합니다. 예를 들면, 'c' 에 대해 '((a)*b)*c' 를 매칭한다면, 다음의 결과를 얻을 것 입니다. * 0 in `regs->start[0]' and 1 in `regs->end[0]' * -1 in `regs->start[1]' and -1 in `regs->end[1]' * -1 in `regs->start[2]' and -1 in `regs->end[2]' 레지스터에 대한 이야기는 이걸로 마치겠습니다. 6.3.9 GNU 패턴버퍼를 free 하기 ------------------------------- 패턴버퍼에서 할당된 필드를 free 하기 위해서는 이전에 설명드린 POSIX 함수 (6.2.6 의 regfree) 를 사용할 수 있습니다. POSIX 에서 사용하는 regex_t 는 GNU 의 re_pattern_buffer 와 동일합니다. 패턴버퍼를 free 한 이후에는, 어떤 검색과 매칭작업을 다시 수행하려면, 정규표현식을 패턴버퍼로 다시 컴파일하여 야 합니다.