정규식을 혐오했던 날들이여 이제는 안녕~ | 프로그래머스 신규 아이디 추천

2022-04-28

정규식, 유용한건 맞는데 너무 배우기 귀찮아

사용자의 입력값을 검증하거나 문자열을 원하는 형태로 고치기 위해 사용되는 정규식은 특히 프론트엔드에서 중요한 기능이다. 하지만 제2외국어도 배우기 싫어하는 나로써는 정규식을 배우는 것은 꽤나 고된 일이었다. 하지만 우아한테크캠프 코딩테스트를 준비해보겠답시고 코테 문제를 풀다가 정규식을 마주해버리고 말았다. 이제는 피할 수 없다. 피할 수 없다면 즐기자.

시작하기 앞서

원래 출처같은건 끝에 적지만, 이 글의 본문 내용이 사실상 거의 이 블로그를 내 언어로 정리한게 다라 출처가 필수일 것 같았다. “카레유" 님께 감사 인사를 전합니다. 참고 페이지: https://curryyou.tistory.com/234 (opens new window) [카레유]

정규식의 정의는 뭘까?

해당 블로그에 따르면, “특정 패턴의 문자열을 찾기 위한 표현 방식”이 정규표현식, 정규식이라고 한다고 한다. 또, MDN에서는 “특정 문자 조합을 찾기 위한 패턴” 이라고 한다. 그냥 사실상 같은 말이고, 사용자가 입력한 값이 시스템이 요구하는 것과 일치하는지를 검증하거나, 크롤링할 때, 하이픈이 포함된 전화번호에서 하이픈을 제거하는 등의 상황에서 아주 유용하게 쓰인다.

정규식의 기본 문법들

  1. 형식

    정규식은 기본적으로 /패턴/플래그 의 형태를 갖고 있다. “패턴" 자리에는 말 그대로 매칭시킬 패턴을, “플래그" 자리에는 하나만 찾을지 등에 대한 옵션을 지정해주는 곳이다.

  2. 매칭패턴

    그냥 검색해도 되겠지만, 아래의 패턴을 익혀두면 쉽게 표현할 수 있다. 물론 그냥 외우는 것은 머리에도 잘 안들어오고, 여러 예제를 풀어봐야할 것 같다. 아참, 역슬래시와 함께 사용하는 패턴들은 대문자로 쓰면 그 부정이 된다.

    패턴 의미
    a-zA-Z 영어알파벳(-으로 범위 지정)
    ㄱ-ㅎ가-힣 한글 문자(-으로 범위 지정)
    0-9 숫자(-으로 범위 지정)
    . 모든 문자열(숫자, 한글, 영어, 특수기호, 공백 모두! 단, 줄바꿈X)
    \d 숫자
    \D 숫자가 아닌 것
    \w 영어 알파벳, 숫자, 언더바
    \W /w 가 아닌 것
    \s space 공백
    \S space 공백이 아닌 것
    \특수기호 특수기호

    출처: https://curryyou.tistory.com/234 (opens new window) [카레유], 일부수정

  3. 검색 패턴/위치 지정 패턴

    해당 블로그에서는 이것들을 “검색 패턴"이라고 했지만, 다른 자료들을 더 찾아보다 보니 위치 지정이란 말도 쓰는 것 같고, 그냥 분류하기 나름인 것 같아서 ^, $ 등은 위치 지정이라는 말이 더 맞는 것 같다.

    기호 의미 예시
    | OR /^[0-9]{3} | [0-9]{4}$/g => 3자리 혹은 4자리의 수
    [] 괄호안의 문자들 중 하나 [0-9a-zA-Z] => 숫자, 영대소문자
    [^문자] 괄호안의 문자를 제외한 것 [^0-9] => 숫자를 제거한 모든 문자
    ^문자열, \A 특정 문자열로 시작 (어떤 언어에선 \A, 괄호 X) ^\. => 마침표로 시작
    문자열$, \Z 특정 문자열로 끝남 (어떤 언어에선 \Z) \.$ => 마침표로 끝냄
    () 그룹 검색 및 분류(match메서드에서 그룹별로 묶어줌)
    (?: 패턴) 그룹 검색(분류X)
    \b 단어의 처음/끝(경계). “test”.match(/st\b/g) ⇒ true, “tester”.match(/st\b/g) ⇒ false
    \B 단어의 처음/끝이 아님
  4. 갯수 패턴

    기호 의미
    ? 최대 한번(없음
    * 없거나 있거나 (없음
    + 최소 한개(한개
    {n} n개
    {Min,} 최소 Min개 이상
    {Min, Max} 최소 Min개 이상, 최대 Max개 이하
  5. 플래그

    플래그 의미
    g Global: 모든 문자 검색(안 쓰면 매칭되는 첫 문자만 검색, dart의 replaceAll같은 것.)
    i Ignore Case: 대소문자 구분 안함
    m Multi line: 여러 행의 문자열에 대해 검색

문제에 적용하기

문제에서 요하는 처리 과정은 다음과 같다.

  1. 대문자를 소문자로 치환
  2. 알파벳 소문자, 숫자, 하이픈, 언더바, 마침표 외의 문자 제거
  3. 두번 이상 사용된 마침표 제거
  4. 처음과 끝에 있는 마침표 제거
  5. 빈 문자열이면 a 대입
  6. 15자리에서 끊기, 마지막 마침표 제거
  7. 2자 이하면 마지막 문자를 3자 이상이 될 때 까지 삽입

1번까진 String 객체의 toLowerCase 메소드로 해결이 가능하다. 하지만 2번부턴 정규식 없이는 해결 불가능하다는걸 직감하고 찾아보기 시작한 것이다. 😇 1번부터 7번까지 필요한 String 메소드나 정규식을 적어보았다.

  1. String.toLowerCase()
  2. /[^a-z0-9-_.]/g ⇒ /[^\w-.]/g [] 안에 넣으면 or로 엮이고, \w는 영어 알파벳, 숫자, 언더바를 포함하는 패턴이므로 a-z0-9_를 \w로 대체할 수 있다.
  3. /.{2,}/g 마침표는 그냥 쓰면 모든 문자이므로 역슬래시를 붙여주고, {2,}로 2자 이상임을 표현한다.
  4. /^\./g, /\.$/g ⇒ /^\. | \.$/g 정규식 두개로 replace 하는걸 파이프라인으로 or로 엮어버릴 수 있다.
  5. String.length == 0이면 a를 넣을 수도 있지만, ^부터 $까지 아무것도 적지 않으면 빈 문자열을 표현하는 패턴이므로 이를 a로 치환해주면 깔끔하게 할 수 있다.
  6. String.substr(0,15) 혹은 String.slice(0,15) 후 /.$/g로 마지막에 있는 마침표 제거
  7. while로 길이가 2 이하일 때 문자열 마지막 원소를 인덱싱해 추가 ⇒ String.repeat 함수 사용

배운 것

배운 것이라고 하기엔 모든걸 다 배웠지만, 기본적 Rule 이외에도 여러가지 실전 응용법을 배웠다.

  1. a-z0-9_ ⇒ \w
  2. /^. | .$/g 으로 앞 혹은 뒤에 있는 패턴 탐색
  3. ^패턴$은 패턴과 문자열이 완전히 일치하는지를 나타낸다
  4. String 메소드들
    1. charAt(index) 메소드: 문자열을 인덱싱할 수 있다. String[index]와 같은 역할을 한다.
    2. repeat(times) 메소드: 문자열을 주어진 횟수만큼 반복해 붙인 새로운 문자열을 반환.

charAt(길이 - 1).repeat(3-길이) 를 통해 문자열의 마지막 문자를 3-길이 번 즉, 문자열의 길이가 3이 되게끔 삽입한다.

학습하는 방법론에 대하여

무언가를 학습하는 데에는, 특히 프로그래밍을 학습할 때는 실제 문제를 해결하기 위한 방안을 모색하는 식으로 학습해야 한다는 것을 느꼈다. “자주 쓰이는 정규식 모음"을 검색해서 쓰다가 정규식을 내가 직접 만들어 이용해야 하는 상황이 왔을 때에 비로소 정규식을 배우고자 하는 노력을 했고, 결론적으로 더 나은 개발자가 되기 위한 발판을 만들 수 있었다고 생각한다. 우아한테크캠프에 합격하지 못해도, 코딩테스트와 알고리즘을 공부할 수 있는 좋은 기회라고 생각되어 당분간 몰입해보려고 한다.