본문 바로가기
java

자바 정규식을 이용한 문자열 추출

by Gil 2020. 5. 9.
728x90

자바에서 문자열에서 특정 조건에 따라 배열로 변환하는 방법에 대해서 알아보겠습니다. 

주로 split() 함수를 사용하게 되는데요, 하나의 특정 문자열 기준으로 추출하는 방법은 쉽습니다. 

// 콤마(,) 기준으로 배열로 변환하기 

String text = "김갑수,이문수,박명수,유재석,노홍철";

String[] strList = text.split(",");

 

하지만 조건이 좀 더 복잡해진다면? 

예를들어 문자열 "1D2S#10S" 에서 연속된 숫자만 추출한다고 해봅시다. 

숫자사이에 '숫자를 제외한 영문 또는 특수문자' 를 기준으로 추출한다면... 음 좀 어려워집니다. 

그럴때! 정규표현식을 이용하는 방법을 사용하면 간단해집니다. 


먼저 기본적인 정규식의 문법을 살펴봅시다. 

<정규표현식 설명>

정규표현식 표현 설명
^x
문자열이 x로 시작합니다
x$
문자열이 x로 끝납니다. 
.x
임의의 한 문자를 표현합니다. 
[x가 마지막으로 끝납니다.]
x+
x가 1번 이상 반복합니다. 
x?
x가 존재하거나 존재하지 않습니다.
x*
x가 0번이상 반복합니다. 
[xy]
x,y중 하나를 찾습니다.
^가 존재하면 not을 나타냅니다.
[^xy]
x,y를 제외하고 문자 하나를 찾습니다.
[x-z]
x~z 사이의 문자중 하나를 찾습니다.
\^
^(특수문자)를 식에 문자 자체로 포함합니다. (문자열 escape)
(x)
괄호안의 문자를 하나의 문자로 인식합니다. (괄호안을 그룹화)
x|y
x 또는 y를 찾습니다. 
(or연산자를 의미합니다.)
\s
공백 문자
\S
공백 문자가 아닌 나머지 문자
\w
알파벳이나 문자
\W
알파벳이나 숫자를 제외한 문자
\d
[0-9] 숫자
\D
숫자를 제외한 모든 문자
x{n}
x를 n번 반복한 문자를 찾습니다.
x{n,}
x를 n번이상 반복한 문자를 찾습니다. 
x{n,m}
x를 n번 이상 m번 이하 반복한 문자를 찾습니다. 

 


 

그럼 다시 "1D2S#10S" 에서 연속된 숫자만 추출해봅시다. 

위의 표에서 조건을 살펴봅니다. 
우리의 목표는 연속된 숫자를 제외한 문자들을 delimiter(구분점)로 두려고 합니다. 

위의 표를 살펴보면, '\D' 가 숫자를 제외한 모든 문자라고 써있습니다.
또한 한자리 이상이 될 수 있기 때문에 조건을 더 찾아보면, '+' 가 앞 문자가 1개 이상인 조건이란걸 찾을 수 있습니다. 

그러면 위에 표에서 알아낸 두가지 조건으로 코드를 작성해보겠습니다. 

String num = "1D2S#10S*";
String regExp = "\\D+";

String[] arr = num.split(regExp);

출력결과


<잠깐! 위의 코드 regExp = "\\D+" 에서 백슬래쉬가 왜 두번 들어가냐고요?>

문자열 Escape 라는것을 알고 계셔야 합니다. 

자바에서는 String 문자열을 표현할 때, 따옴표(") 로 감싸주게 됩니다.  (따옴표는 영어로 double quote입니다.)

그런데, 문자열중에도 따옴표를 표시하고 싶다면? 

String text = "철수는 "안녕하세요!!" 라고 인사했습니다.";

위의 코드는 자바 문법에 어긋났기 때문에 컴파일 에러를 발생시킵니다. 

따라서 아래와 같이 변경해야합니다. 

String text = "철수는 \"안녕하세요!!\" 라고 인사했습니다.";

출력: 철수는 "안녕하세요!!" 라고 인사했습니다.

이렇게 문법에 어긋나는 특수문자들을 문자로 표현하기 위해서는 역슬래쉬(\)를 특수문자 앞에 붙이는 것을 문자열 Escape라고 합니다. 

문자열 안에 역슬래쉬를 표한하고 싶다면 아래와 같이 선언하면 됩니다. 

String text = "역슬래쉬는 \\입니다.";

출력: 역슬래쉬는 \입니다.

 

다시 돌아와서, 응용을 해보도록 하겠습니다. 

"1D2S#10S" 에서 연속된 숫자를 Delimiter로 배열을 추출해 보겠습니다.  (위의 문제와 반대)

String num = "1D2S#10S*";
String regExp = "[0-9]+";

String[] arr = num.split(regExp);

출력 결과

출력 결과에서 숫자로 시작할 때, 빈 배열이 0번째 index에 들어가긴 하지만, 어쨋든 배열 추출을 완료했습니다.

 

 


<특정 문자열 사이의 문자열 추출하기 >

수직선(|) 문자 사이의 회사명을 추출하는 예제를 살펴보겠습니다. ( |회사명| )

String str = "|마이크로소프트||애플||페이스북||네이버|";
Pattern pattern = Pattern.compile("[|](.*?)[|]");

Matcher matcher = pattern.matcher(str);

while (matcher.find()) {
    System.out.println(matcher.group(1));

    if (matcher.group(1) == null) break;
}


// 출력결과
마이크로소프트
애플
페이스북
네이버
String str = "|마이크로소프트||애플||페이스북||네이버|";
Pattern pattern = Pattern.compile("[|](.*)[|]");

Matcher matcher = pattern.matcher(str);

while (matcher.find()) {
    System.out.println(matcher.group(1));

    if (matcher.group(1) == null) break;
}


// 출력결과
마이크로소프트||애플||페이스북||네이버

 

<위의 정규식 "[|](.*?)[|]" 은 무슨 뜻인가요? >

양쪽이 수직선 [|] 이고, 그 사이의 문자열은 .*? 인 것을 찾으라는 의미입니다. (아래 예제는 .*)

 

< .*과 .*?의 의미란?>

위에 정규표현식에서 응용해서 해석해보겠습니다.  

1) .* : 임의의 문자가 0번이상 반복합니다. 

해당 정규식을 Greedy Operator라고 불립니다. 작대기(|) 사이의 모든것을 가져오는 것이죠. 

2) .*? : 임의의 문자가 0번이상 반복하거나, 존재하지 않습니다. (? 뭔말인지..)

해당 정규식은 Non-greedy Operator 라고 불립니다.  최소 조건 만족을 가져온다고 보면 됩니다. 

 


<심화> 

문자열 중, window98, windowXP, window7 의 문자열을 추출하시오. 

Matcher 클래스의 group() 함수란? 

group(0): 그루핑의 모든 조건을 가져온다.
group(1): 그루핑의 첫번째 조건을 가져온다. (그루핑 조건이 하나만 존재한다면, group(0) == group(1) 일 것이다.) 
group(2): 그루핑의 두번째 조건을 가져온다. 
... index는 그루핑 조건에 따라 증가될 수 있다. 


연습 문제 

요청사항: 아래 html 텍스트에서 "https://www.hello.co.kr/companies/123/testing" 형식의 포멧에서 123, 567 의 값을 뽑아낼 수 있는 정규식을 작성하시오. 

안녕하세요 테스트입니다. <a="https://www.hello.co.kr/companies/123/testing"> 안녕하세요 테스트2 입니다. "https://www.hello.co.kr/companies/567/testing">

Hint: 시작값이 "https://www.hello.co.kr/companies/" 이고 종료값이 "/testing" 인 것을 하나의 패턴으로 인식하기 

정답

더보기
https://www.hello.co.kr/companies/([0-9]+)/testing

<참고자료>

http://www.nextree.co.kr/p4327/

https://m.blog.naver.com/PostView.nhn?blogId=rorean&logNo=221582429295&proxyReferer=https:%2F%2Fwww.google.com%2F

https://unlimitedpower.tistory.com/entry/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EA%B3%BC-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90