티스토리 뷰
특정 문자로 시작해서 특정 문자로 끝나는 문장을 찾은 후, 해당 문장에서 일부만 replace 하려고 한다.
평소에 잘 안쓰던 정규식을 쓰려고 하니 과정이 복잡해서 내가 나중에 볼겸 블로그에 저장한다.
(이거보다 쉬운 방법이 있다면 제발 알려주세요... 소스 수정하게...)
String originTxt = "HELLO WORLD TEST! <TEST 문자열1> 안녕 < TEST문자열2 > BYE WORLD";
String targetTxt = "HELLO WORLD TEST! <테스트 문자열1> 안녕 < 테스트문자열2 > BYE WORLD";
최종 목표는 위와 같은 텍스트에서 '<'로 시작해서 '>'로 끝나는 문장을 찾은 후, 그 문장 내에서만 일부 내용을 replace하는 것. ('TEST' → '테스트' / '<' → '<' / '>' → '>')
가장 먼저 '<'로 시작해서 '>'로 끝나는 문장을 찾기 위해 정규식을 작성했다.
<.*> // result : "<TEST 문자열1> 안녕 < TEST문자열2 >"
처음에는 이렇게 작성했더니 문자열1 부분과 문자열2 부분을 따로 찾는게 아니라 제일 처음 '<'와 제일 마지막 '>'를 찾아서 문장을 통째로 검색해버렸다.
<.*?>
<(.*?)> // capturing group
<(?:.*?)> // non-capturing group
내가 원하는 문장을 찾는 데는 세가지 정규식 모두 먹힌다. (테스트는 여기서 > https://regexr.com/)
처음엔 group을 활용해서 찾은 문장 내에서만 replace할 수 있지 않을까 싶었는데 정규식 숙련도 부족으로 일단 포기.
capturing과 non-capturing의 차이는 match 함수를 사용했을 때 결과배열에 그룹이 포함되냐 안되냐 차이라고 하는데 사실 무슨말인지 100퍼센트 이해하기가 어렵다.
group에 관련된 자세한 내용은 잘 정리해둔 블로그를 참고하자! (blog.rhostem.com/posts/2018-11-11-regex-capture-group)
정규식을 통해 문장은 찾았으니 그 문장 내에서만 replace를 해야하는데 어떻게 할까 고민을 하다가
찾은 문장과 그 문장의 앞뒤를 잘라서 따로 저장한 후, 찾은 문장만 replaceAll을 하고 다시 합쳐주는 방식으로 하기로 했다.
마침 Matcher 메소드 중에 찾은 문장의 시작과 끝 인덱스를 리턴해주는 메소드가 있어 이용하였고
replace를 하면 기존 문장과 index가 달라지기 때문에 matcher 함수를 한번 더 호출해서 다시 탐색했다.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String args[]) {
String originTxt = "HELLO WORLD TEST! <TEST 문자열1> 안녕 < TEST문자열2 > BYE WORLD";
String targetTxt = "HELLO WORLD TEST! <테스트 문자열1> 안녕 < 테스트문자열2 > BYE WORLD";
String newTxt = originTxt;
String reg = "<(?:.*?)>";
Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(originTxt);
while (matcher.find()) {
System.out.println("Full match: " + matcher.group(0));
System.out.println("Start index: " + matcher.start(0));
System.out.println("End index: " + matcher.end(0));
String matchStr = matcher.group(0);
String before = newTxt.substring(0, matcher.start(0));
String after = newTxt.substring(matcher.end(0));
matchStr = matchStr.replaceAll("<", "<");
matchStr = matchStr.replaceAll(">", ">");
matchStr = matchStr.replaceAll("TEST", "테스트");
newTxt = before + matchStr + after;
matcher = pattern.matcher(newTxt);
System.out.println("Change: " + newTxt);
System.out.println();
}
System.out.println("Origin: " + originTxt);
System.out.println("Result: " + newTxt);
}
}
위 코드를 실행해보면 아래와 같은 결과가 나온다.
Full match: <TEST 문자열1>
Start index: 18
End index: 35
Change: HELLO WORLD TEST! <테스트 문자열1> 안녕 < TEST문자열2 > BYE WORLD
Full match: < TEST문자열2 >
Start index: 32
End index: 50
Change: HELLO WORLD TEST! <테스트 문자열1> 안녕 < 테스트문자열2 > BYE WORLD
Origin: HELLO WORLD TEST! <TEST 문자열1> 안녕 < TEST문자열2 > BYE WORLD
Result: HELLO WORLD TEST! <테스트 문자열1> 안녕 < 테스트문자열2 > BYE WORLD
원하는 대로 '<'와 '>' 사이에 있는 'TEST'는 '테스트'로 잘 변경되었고, 그 외의 TEST는 변경되지 않고 그대로이다.
나름 시행착오가 많았는데(특히 정규식), 막상 블로그에 포스팅하니 엄청 간단해보여 약간 속상하다.
갈 길이 먼 개발자의 길...
'공부 > Java' 카테고리의 다른 글
[JAVA] MySQL executeUpdate 사용 시 리턴값 (0) | 2018.04.16 |
---|