본문 바로가기
  • 2025
문제풀기/프로그래머스 문풀

[프로그래머스] 정규표현식을 공부해보자

by soonrang 2024. 1. 5.

문제 설명

카카오에 입사한 신입 개발자 네오는 "카카오계정개발팀"에 배치되어, 카카오 서비스에 가입하는 유저들의 아이디를 생성하는 업무를 담당하게 되었습니다. "네오"에게 주어진 첫 업무는 새로 가입하는 유저들이 카카오 아이디 규칙에 맞지 않는 아이디를 입력했을 때, 입력된 아이디와 유사하면서 규칙에 맞는 아이디를 추천해주는 프로그램을 개발하는 것입니다.

다음은 카카오 아이디의 규칙입니다.

  • 아이디의 길이는 3자 이상 15자 이하여야 합니다.
  • 아이디는 알파벳 소문자, 숫자, 빼기(``), 밑줄(_), 마침표(.) 문자만 사용할 수 있습니다.
  • 단, 마침표(.)는 처음과 끝에 사용할 수 없으며 또한 연속으로 사용할 수 없습니다.

"네오"는 다음과 같이 7단계의 순차적인 처리 과정을 통해 신규 유저가 입력한 아이디가 카카오 아이디 규칙에 맞는 지 검사하고 규칙에 맞지 않은 경우 규칙에 맞는 새로운 아이디를 추천해 주려고 합니다.

신규 유저가 입력한 아이디가 new_id 라고 한다면,

1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다. 2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다. 3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다. 4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다. 5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다. 6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다. 만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다. 7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복

 

 


 

 

이 문제는 정규표현식을 잘 활용하면 코드가 단순해진다.

나는 그것도 모르고 열심히 머리를 굴렸다. 뭐 코딩테스트 공부하는 과정이니까 시간이 아깝지는 않다..(정말로!)

 

class Solution {
    public String solution(String new_id) {
        String answer = "";

        new_id = new_id.toLowerCase();

        new_id = new_id.replaceAll("[^a-z0-9-_.]", "");
        new_id = checkContinuous(new_id);
        new_id = check_forth(new_id);
        new_id = check_forth2(new_id);

        if(new_id.isBlank()){
            new_id = "aaa";
        }

        if(new_id.length() < 3){
            char addChar = new_id.charAt(new_id.length() - 1);
            while (new_id.length() < 3){
                new_id += addChar;
            }
        }

        if(new_id.length()>15){
                new_id = new_id.substring(0,15);
            }
        
        new_id = check_forth2(new_id);

    
        answer = new_id;
        return answer;
    }

     static public String checkContinuous(String new_id) {
        new_id.replace("..",".");
        while (new_id.contains("..")){
            new_id = new_id.replace("..",".");
        }
        return new_id;
    }

    public static String check_forth(String new_id) {
        while (!new_id.isEmpty() && new_id.charAt(0) == '.') {
            new_id = new_id.substring(1); 
        }
        return new_id;
    }

    public static String check_forth2(String new_id) {
        while (!new_id.isEmpty() && new_id.charAt(new_id.length() - 1) == '.') {
            new_id = new_id.substring(0, new_id.length() - 1); 
        }
        return new_id;
    }
}

 

 

제출은 이 코드로 했는데 메서드 이름을 다시 정하고 분리를 해야겠다. 

 

import java.util.ArrayList;

public class pro_0104 {

    public static void main(String[] args) {
        Solution solution = new Solution();

        String new_id = "...!@BaT#*..y.abcdefghijklm";
        String new_id2 = "z-+.^.";
        String new_id3 = "=.=";
        String new_id4 = "123_.def";
        String new_id5 = "abcdefghijklmn.p";


        String result = solution.solution(new_id);
        System.out.println(result);

        String result3 = solution.solution(new_id3);
        System.out.println(result3);
    }

    static class Solution {
        public String solution(String new_id) {
            new_id = new_id.toLowerCase();
            new_id = new_id.replaceAll("[^a-z0-9-_.]", "");
            new_id = onlyDot(new_id);
            new_id = checkFirstDot(new_id);
            new_id = checkLastDot(new_id);
            new_id = checkBlank(new_id);
            new_id = checkMin(new_id);
            new_id = checkMax(new_id);
            
            new_id = checkLastDot(new_id);

            return new_id;
        }

        private String onlyDot(String s) {
            s.replace("..", ".");
            while (s.contains("..")) {
                s = s.replace("..", ".");
            }
            return s;
        }

        private String checkFirstDot(String s) {
            while (!s.isEmpty() && s.charAt(0) == '.') {
                s = s.substring(1); 
            }
            return s;
        }

        private String checkLastDot(String s) {
            while (!s.isEmpty() && s.charAt(s.length() - 1) == '.') {
                s = s.substring(0, s.length() - 1); 
            }
            return s;
        }

        private String checkBlank(String s) {
            if (s.isBlank()) {
                s = "aaa";
            }
            return s;
        }

        private String checkMin(String s) {
            if (s.length() < 3) {
                char addChar = s.charAt(s.length() - 1);
                while (s.length() == 3) {
                    s += addChar;
                }
            }
            return s;
        }

        private String checkMax(String s) {
            if (s.length() > 15) {
                s = s.substring(0, 15);
            }
            return s;
        }
    }


}

 

 

지난 우테코 6기를 지원할 때 미션 규칙들을 떠올려가며 적용하려 했다. 그때만큼 복잡하지는 않지만 최대한 규칙을 신경쓰려 노력했다. 

 

 


다른 사람들의 답변

class Solution {
    public String solution(String new_id) {

        String s = new KAKAOID(new_id)
                .replaceToLowerCase()
                .filter()
                .toSingleDot()
                .noStartEndDot()
                .noBlank()
                .noGreaterThan16()
                .noLessThan2()
                .getResult();


        return s;
    }

    private static class KAKAOID {
        private String s;

        KAKAOID(String s) {
            this.s = s;
        }

        private KAKAOID replaceToLowerCase() {
            s = s.toLowerCase();
            return this;
        }

        private KAKAOID filter() {
            s = s.replaceAll("[^a-z0-9._-]", "");
            return this;
        }

        private KAKAOID toSingleDot() {
            s = s.replaceAll("[.]{2,}", ".");
            return this;
        }

        private KAKAOID noStartEndDot() {
            s = s.replaceAll("^[.]|[.]$", "");
            return this;
        }

        private KAKAOID noBlank() {
            s = s.isEmpty() ? "a" : s;
            return this;
        }

        private KAKAOID noGreaterThan16() {
            if (s.length() >= 16) {
                s = s.substring(0, 15);
            }
            s = s.replaceAll("[.]$", "");
            return this;
        }

        private KAKAOID noLessThan2() {
            StringBuilder sBuilder = new StringBuilder(s);
            while (sBuilder.length() <= 2) {
                sBuilder.append(sBuilder.charAt(sBuilder.length() - 1));
            }
            s = sBuilder.toString();
            return this;
        }

        private String getResult() {
            return s;
        }
    }
}

 

 

위 코드를 확인해보면 정규표현식을 사용해서 코드가 깔끔해졌고 메서드 이름이 분명해보인다. 

정규표현식을 따로 정리할까 하다가 이미 정리된 좋은 글들이 많기 때문에 생략하기로 했다. 

https://hamait.tistory.com/342

 

정규표현식 (Regex) 정리

정규표현식은 아주 가끔 쓰기때문에 항상 다시 볼때마다 헷갈리곤 하기에 주요 사용예를 내가 나중에 다시 봤을 때 편하도록 정리하여 보았다. 정규 표현식의 용어들정규 표현식에서 사용되는

hamait.tistory.com

 

 

코딩테스트 문제를 풀면 풀 수록 우아한테크코스 6기 프리코스에서 얼마나 부족했는지 알게된다. 

하핫 이런 좋은 정규표현식을 활용할 줄도 몰랐던 나.. 오늘도 배워갑니다. 꾸벅