자바에서 문자열에서 특정 조건에 따라 배열로 변환하는 방법에 대해서 알아보겠습니다.
주로 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
<참고자료>