[Java] CamelCase, Snake_Case 상호 변환, 그리고 replaceAll 의 한계

식빵·2022년 7월 1일
0

Java Lab

목록 보기
8/21
post-thumbnail

Camel Case <==> Snake_Case 상호변환하는 코드를 작성해본다.
그리고 이 과정에서 알아낸 replaceAll 의 한계점도 작성해둔다.
참고로 Spring boot(v.2.7.1) 프로젝트 생성 후, Junit5 Test 코드로 짰다.


📌 java 9 이상 코드

package hello.dailycode;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;

@Slf4j
public class ConvertingTest {

    final String SNAKE_CASE = "good_morning_everyone";
    final String CAMEL_CASE = "goodMorningEveryone";

    @Test
    @DisplayName("snake_case To camelCase")
    void name() {
        String snake_case = "good_morning_everyone";

        Pattern compile = Pattern.compile("([a-z])_([a-z])");
        Matcher matcher = compile.matcher(snake_case);
        String convert = matcher.replaceAll(matchResult -> {
            return String.format(
                    "%s%s",
                    matchResult.group(1).toLowerCase(),
                    matchResult.group(2).toUpperCase()
            );
        });

        log.info("convert result : [{}] ==> [{}]", snake_case, convert);
        assertThat(convert).isEqualTo(CAMEL_CASE);
    }

    @Test
    @DisplayName("camelCase To snake_case")
    void name2() {
        String camelCase = "goodMorningEveryone";
        Pattern pattern = Pattern.compile("([a-z])([A-Z])");
        Matcher matcher = pattern.matcher(camelCase);

        String convert = matcher.replaceAll(matchResult -> {
            return String.format("%s_%s", matchResult.group(1), matchResult.group(2));
        });


        log.info("convert result : [{}] ==> [{}]", camelCase, convert);
        assertThat(convert).isEqualTo(SNAKE_CASE);
    }
}



📌 java 9 미만 코드

package hello.dailycode;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;

@Slf4j
public class ConvertingTest {

    final String SNAKE_CASE = "good_morning_everyone";
    final String CAMEL_CASE = "goodMorningEveryone";

    @Test
    @DisplayName("snake_case To camelCase")
    void name() {
        String snake_case = "good_morning_everyone";

        Matcher matcher = Pattern.compile("([a-z])_([a-z])").matcher(snake_case);

        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String format = String.format(
                    "%s%s",
                    matcher.group(1).toLowerCase(),
                    matcher.group(2).toUpperCase()
            );
            matcher.appendReplacement(sb,format);
        }

        StringBuffer stringBuffer = matcher.appendTail(sb);
        String convert = stringBuffer.toString();

        log.info("convert result : [{}] ==> [{}]", snake_case, convert);
        assertThat(convert).isEqualTo(CAMEL_CASE);

    }

    @Test
    @DisplayName("camelCase To snake_case")
    void name2() {
        String camelCase = "goodMorningEveryone";
        Matcher matcher = Pattern.compile("([a-z])([A-Z])").matcher(camelCase);

        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String format = String.format(
                    "%s_%s",
                    matcher.group(1),
                    matcher.group(2)
            );
            matcher.appendReplacement(sb,format);
        }
        StringBuffer stringBuffer = matcher.appendTail(sb);
        String convert = stringBuffer.toString().toLowerCase();

        log.info("convert result : [{}] ==> [{}]", camelCase, convert);
        assertThat(convert).isEqualTo(SNAKE_CASE);
    }
}



📌 replaceAll(regex, replace)의 한계

참고로 camel ==> snake_case 로 변환하는 건 더 쉬운 방법도 있다.

"goodMorningEveryone".replaceAll("([a-z])([A-Z])", "$1_$2")"

그런데 이 replaceAll(regex, replace) 메서드만으로는 약간의 제약이 있다.

예를 들어 vs code 같은 편집기로 regex replace 기능을 사용할 때,
replace String 으로 "\L$1\U$2" 처럼 특수한 기능을 포함하는 변환 방식을 많이 쓴다.

참고1: \L$1를 소문자로, \U$2를 대문자로 변환하겠다느 뜻이다.

그런데 이런 특수한 변환 기능은 replaceAll(regex, replace)에서 지원하지 않는다.
즉, 이 메서드의 2번째 인자값으로 "\\L$1\\U$2" 처럼 사용할 수 없다는 의미다

하지만 이런 특수한 변환이 아니고 그냥 단순 문자열 치환이면 사용해도 무관하다.

참고2:
str.replaceAll(regex, repl)
Pattern.compile(regex).matcher(str).replaceAll(repl)을 요약한 메소드일 뿐이다.



참고) java 9 미만 코드 - 리팩토링

@Slf4j
public class ConvertingTest {

    final String SNAKE_CASE = "good_morning_everyone";
    final String CAMEL_CASE = "goodMorningEveryone";

    @Test
    @DisplayName("snake_case To camelCase")
    void name() {
        String snake_case = "good_morning_everyone";

        Matcher matcher = Pattern.compile("([a-z])_([a-z])").matcher(snake_case);

        String convert = getConvert(matcher, matchResult -> {
            return String.format(
                    "%s%s",
                    matcher.group(1).toLowerCase(),
                    matcher.group(2).toUpperCase()
            );
        });

        log.info("convert result : [{}] ==> [{}]", snake_case, convert);
        assertThat(convert).isEqualTo(CAMEL_CASE);

    }

    @Test
    @DisplayName("camelCase To snake_case")
    void name2() {
        //... 위처럼 작성하면 됩니다. 생략!
    }

    private String getConvert(Matcher matcher, Function<MatchResult, String> replacer){
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            MatchResult matchResult = matcher.toMatchResult();
            String format = replacer.apply(matchResult);
            matcher.appendReplacement(sb,format);
        }
        StringBuffer stringBuffer = matcher.appendTail(sb);
        return stringBuffer.toString();
    }
}
profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글