Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

캡쳐 그룹

Groovy의 매우 유용한 기능 중 하나는 정규식을 이용하여 데이터에서 특정 부분을 잡아내는 것입니다. 아래 데이터에서 각 요소를 뽑아내고 싶다고 가정해봅시다:

물론 String의 split() 메서드를 이용하여 잘라낸 후 Livepool과 England 사이의 ","를 제거하고, 나머지 조각들에 대해서도 비슷한 작업을 반복해주면 되긴 합니다. 하지만 정규식을 이용하면 이 작업을 한번에 끝낼 수 있습니다. 이 작업을 위한 문법은 약간 특이합니다. 우선 정규식을 작성하고 뽑아내고자 하는 부분을 괄호 안에 넣으세요:

다음으로는 =~ 연산자로 "matcher"를 정의해야 합니다:

이제 matcher 변수는 java.util.regex.Matcher 클래스의 인스턴스를 갖게 됩니다. 물론 Groovy에 의해 확장된 추가 기능도 함께 들어 있습니다. 자바 방식으로 Matcher 객체를 사용할 수도 있지만, 더 Groovy 적인 방법은 Matcher 객체를 마치 2차원 배열인 것 처럼 취급하는 것입니다. 2차원 배열이란 단순히 배열의 배열을 말합니다. 배열의 첫번째 차원에는 이 정규직에 의해 일치된 문자열들이 담깁니다. 이 예제에서는 정규식에 대해 전체 문자열이 단 한번 일치되었기 때문에 첫번째 차원의 길이는 1 입니다. 따라서 다음과 같이 쓰면:

다음과 같이 평가됩니다:

이제 두번째 차원을 이용하여 각 캡쳐 그룹에 접근할 수 있습니다:

정규식을 이용해서 얻을 수 있는 추가적인 장점은 주어진 데이터의 형식이 적합한지를 검사할 수 있다는 사실입니다. 즉, *locationData*의 내용이 "Could not find location data for Lima, Peru" 였다면 위 if 문 안의 블록은 실행되지 않을 것입니다.

비 캡쳐 그룹(Non-matching groups)

가끔, 괄호로 표현을 묶고 싶기는 한데 그 괄호가 캡쳐 그룹으로 사용되는 것은 원하지 않을 때가 있습니다. 이럴때는 괄호 바로 다음에 ?: 를 붙여주면 됩니다. 예를 들어 사람들의 이름 표기를 바꾸고 싶은데 중간 이름(middle name)은 무시하고자 한다면 아래와 같이 쓸 수 있습니다:

결과는 아래와 같습니다:

이렇게 하면 성(last name)이 항상 두번째 캡쳐 그룹에 잡히게 됩니다.

바꾸기

정규식의 단순하지만 정말 강력한 기능 중 하나는 문자열에서 특정 패턴을 찾아서 다른 문자열로 바꿔주는 기능입니다. java.util.regex.Matcher의 replaceFirst() 혹은 replaceAll() 메서드를 이용하면 됩니다.

예를 들어, J.K. Rowlings의 책인 Harry Potter 시리즈를 다른 이름으로 팔아먹기 위해 문자열 중에 나오는 모든 "Harry Potter"를 "Tanya Grotter"로 치환해봅시다. (정말 이짓을 한 사람이 있습니다. 못 믿겠으면 구글에서 찾아보세요):

위 예제에서는 두 번의 치환 작업을 수행했습니다. 한번은 전체 이름인 "Harry Potter"에 대해, 또 한번은 이름인 "Harry"에 대해서만.

릴럭턴트(reluctant) 연산자

?, + 그리고 * 연산자는 모두 기본적으로 그리디(greedy - 욕심많은) 입니다. 즉, 가능하면 많은 부분과 일치시키려는 특성이 있습니다. 하지만 가끔은 이런 특성을 원치 않을 때가 있습니다. 15세기 교황을 나열한 다음 예제를 살펴보겠습니다:

A first attempt at a regular expression to parse out the name (without the sequence number or modifier) and years of each pope might be as follows:

Which splits up as:

/

Pope

(.*)

(?: .*)?

([0-9]+)

-

([0-9]+)

/

begin expression

Pope

capture some characters

non-capture group: space and some characters

capture a number

-

capture a number

end expression

We hope that then the first capture group would just be the name of the pope in each example, but as it turns out, it captures too much of the input. For example the first pope breaks up as follows:

/

Pope

(.*)

(?: .*)?

([0-9]+)

-

([0-9]+)

/

begin expression

Pope

Anastasius I

 

399

-

401

end expression

Clearly the first capture group is capturing too much of the input. We only want it to capture Anastasius, and the modifiers should be captured by the second capture group. Another way to put this is that the first capture group should capture as little of the input as possible to still allow a match. In this case it would be everything until the next space. Java regular expressions allow us to do this using "reluctant" versions of the *, + and ? operators. In order to make one of these operators reluctant, simply add a ? after it (to make *?, +? and ??). So our new regular expression would be:

So now let's look at our new regular expression with the most difficult of the inputs, the one before Pope Hilarius (a real jokester), breaks up as follows:

/

Pope

(.*?)

(?: .*)?

([0-9]+)

-

([0-9]+)

/

begin expression

Pope

Leo

I the Great

440

-

461

end expression

Which is what we want.

So to test this out, we would use the code:

Try this code with the original regular expression as well to see the broken output.

  • No labels