정규표현식

정규표현식은 참 어렵습니다. 제가 싫어하는 알고리즘 문제 같아요.
부스트캠프 때 여러번 접했었지만, 복잡한 정규식을 작성했을 때 테스트가 제대로 되지 않아 간단한 식만 이용했던 기억이 있습니다.
이번에는 제대로 공부해보고자 글을 적어봅니다.

정규표현식

정규표현식은 문자열에서 특정 문자 조합을 찾기 위한 패턴입니다. 자바스크립트에서는 정규 표현식도 객체로서, RegExp의 exec()와 test() 메서드를 사용할 수 있습니다. String의 match(), matchAll(), replace(), replaceAll(), search(), split() 메서드와도 함께 사용할 수 있습니다.

정규표현식은 다음과 같이 두 가지 방법으로 만들 수 있습니다.

  1. 정규 표현식 리터럴

     const re = /ab+c/;
    
  2. RegExp 객체의 생성자 호출: 바뀔 수 있는 패턴이나 사용자 입력 등에서 가져오는 패턴의 경우 사용합니다.

     const re = new RegExp('ab+c');
    

정규 표현식 패턴 작성

정규 표현식 패턴은 /abc/처럼 단순한 문자로 구성하거나, /ab+c//Chapter (\d+)\.\d*/처럼 단순한 문자와 특수 문자의 조합으로 구성할 수도 있습니다.

단순 패턴

단순 패턴은 문자열을 있는 그대로 탐색할 때 사용합니다. /abc/와 같은 패턴은 문자열에서 정확하게 "abc"라는 문자의 조합이 나타나는 부분과 일치합니다. "ab c"라는 문자와는 일치하지 않습니다.

```javascript
function rep(str, re){
    return str.match(re);
}
const re = /abc/;

console.log(re.test('abc', re));
console.log(rep('abc', re));
console.log(rep('abcabc', re));
console.log(rep('aaa', re));
/*
true
[ 'abc', index: 0, input: 'abc', groups: undefined ]
[ 'abc', index: 0, input: 'abcabc', groups: undefined ]
null
*/

```

특수 문자 사용

하나 이상의 “b”를 찾는다거나 공백 문자를 찾는 등 직접적인 일치 이상의 탐색이 필요할 때 특수 문자를 사용합니다.
‘하나의 “a” 이후에 0개 이상의 “b”, 그 뒤의 “c”와 일치해야 하는 경우 /ab*c/ 패턴을 사용할 수 있습니다. b*는 b를 0번 이상 반복한다는 의미입니다.
해당 패턴을 “cbbabbbbcdebc” 문자열에 대해 사용하면 결과는 “abbbbc”가 됩니다.

```javascript
const re = /ab*c/;

console.log(re.test('abc', re)); // true
console.log(re.test('ac', re)); // true
console.log(re.test('abbbc', re)); // true
console.log(re.test('a*c', re)); // false
console.log(re.test('a c', re)); // false

```

자주 사용되는 정규 표현식

  • () : 패턴들을 하나의 그룹으로 묶음. 주로 or 연산자 |와 묶어 사용한다.
    • (doc|hwp|txt)
  • + : 기호 앞의 패턴이 1번 이상 반복되는 패턴
    • ^\S+ : 공백없는 텍스트로 시작하고, 1글자 이상의 문자열
  • ? : 기호 앞의 패턴이 없거나, 1번 발생하는 패턴
    • \d? : 숫자가 없거나 1번 발생하는 패턴
  • * : 기호 앞의 패턴이 없거나, 1번 이상 발생하는 패턴
  • . : \와 개행 문자를 제외한 모든 문자
  • ^ : 문자열(패턴)이나 행의 처음
    • ^문자열 : 문자열로 시작
  • $ : 문자열(패턴)이나 행의 종료
    • 문자열$ : 문자열로 종료
  • [] : [] 사이에 들어간 문자 중 하나를 매치 (=[] 안에 있는 문자들로만 구성되어 있는가)
    • [abc]는 ab, bc, ac, ca 등을 모두 포함
  • [^] : []의 부정(=[] 안에 있는 문자가 없는가)
  • [a-zA-Z0-9] : 모든 영문자(소문자/대문자)와 모든 숫자로 이루어져 있는가
  • [ㄱ-ㅎ|ㅏ-ㅣ|가-힣] : 모든 한글이 있는가
  • \s : 공백 문자
  • \S : 공백을 제외한 나머지 문자들
  • \w : ‘_‘를 포함한 모든 영어와 숫자들
  • \W : ‘_‘를 제외한, 모든 영어와 숫자가 아닌 문자열들
  • \d : =[0-9]
  • \D : 숫자가 아닌 모든 문자
  • {n} : n개
  • {n,} : n개 이상
  • {M,N} : 최소 M번 이상, 최대 N번 이하로 발생하는 패턴
    • 주민등록번호(한국인): \d{6}-[1-4]\d{6}
  • | : OR 연산
  • (?i) : 대소문자를 구분하지 않음
  • 플래그
    • g : Global-모든 문자 검색
    • i : Ignore Case-대소문자 구분 안함
    • m : Multi Line-여러 행의 문자열에 대해 검색

자바스크립트 메소드

자바스크립트 코드 상에서는 아래 메소드를 통해 패턴을 검사하고, 매칭되는 문자열을 추출 및 변환합니다.

  1. (‘문자열’).match(/정규식/플래그) : ‘문자열’에서 ‘정규 표현식’에 매칭되는 항목들을 배열로 반환

     const txt = "q1w2e3r4!@";
     console.log(txt.match(/[a-zA-Z0-9]+/g, '')); // ['q1w2e3r4']
    
  2. (‘문자열’).replace(/정규식/, ‘대체문자열’) : ‘정규 표현식’에 매칭되는 항목을 ‘대체문자열’로 반환

     const txt = "q1w2e3r4!@";
     console.log(txt.replace(/[^a-zA-Z0-9]+/g, '')); // q1w2e3r4
    
  3. (‘문자열’).split(/정규식/) : ‘문자열’을 ‘정규 표현식’에 매칭되는 항복으로 쪼개어 배열로 반환

     const txt = "Hi, It's Me Mukho";
     console.log(txt.split(/,* /)); // ['Hi', 'It's', 'Me', 'Mukho']
    
  4. (/정규식/).test(‘문자열’) : ‘문자열’이 ‘정규 표현식’과 매칭되면 true, 아니면 false 반환
  5. (/정규식/).exec(‘문자열’) : match 메소드와 유사하나, 매칭된 첫 결과만 반환한다.

정규 표현식 응용

  1. 웹사이트 주소 : ‘http://’나 ‘https://’로 시작하고, 알파벳, _, -, .으로 이루어져 있다. 문자열이나 /로 끝난다.

     const txt = `http://www.naver.com https://www.google.com/ localhost`;
     console.log(txt.match(/https?:\/\/[\w\-\.(\S$|\/$)]+/g)); // ['http://www.naver.com', 'https://www.google.com/']
    
  2. 전화번호 : ‘2~3자리숫자’-‘3~4자리숫자’-‘4자리숫자’(단순화한 정규식)

     const txt = `010-1234-5678 033-123-4567 02-1234-5678`;
     console.log(txt.match(/\d{2,3}-\d{3,4}-\d{4}/g)); // ['010-1234-5678', '033-123-4567', '02-1234-5678']
    
  3. 이메일 주소

     const txt = `이메일 목록 : komogoon@gmail.com mukho@kakao.com`;
     console.log(txt.match(/[\w\-\.]+@[\w\-\.]+/g)); // ['komogoon@gmail.com', 'mukho@kakao.com']
    
  4. 특수 기호

     const txt = `이메일 목록 : komogoon@gmail.com, mukho@kakao.com~!@#$%^&*()`;
     console.log(txt.match(/[\[\]\{\}\<\>\(\),.~₩!@#\$%\^&\*-_+=\|\\\/]/g)); // [[':', '@', '.', ',', '@', '.', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')']
    
  5. 문자가 아닌 것(공백 포함)

     const txt = `이메일 목록 : komogoon@gmail.com mukho@kakao.com`;
     console.log(txt.match(/[^a-zA-Z0-9ㄱ-ㅎㅏ-ㅣ가-힣 ]/g)); // [':', '@', '.', '@', '.']
    
  6. 아이디 유효 및 길이 체크

     const txt = 'mukh0!';
     console.log(/^[\w-_]{4,20}$/gi.test(txt)); // false
     console.log(/^[\w-_]{4,20}$/gi.test('mukh0')); // true
    
    
  7. 아이디 유효 및 길이 체크

     const txt = 'mukh0!';
     console.log(/^[\w-_]{4,20}$/gi.test(txt)); // false
     console.log(/^[\w-_]{4,20}$/gi.test('mukh0')); // true
    
  8. 숫자만 체크

     const regExp = /^[0-9]+$/;
     console.log(regExp.test('1234')); // true
     console.log(regExp.test('m1234')); // false
    
  9. 휴대폰 번호

     const regExp = /^01([0|1|6|7|8|9])-?([2-9]{1})([0-9]{2,3})-?[0-9]{4}$/;
     console.log(regExp.test('010-2345-6789')); // true
     console.log(regExp.test('010-1234-5678')); // false
     console.log(regExp.test('m1234')); // false
    
    

마치며

정규표현식은 정말 강력한 요소이지만, 막상 쓰려니 복잡하기 그지없네요.. 그래도 부스트캠프 챌린지 시기보다는 면역이 생겨 쓸 만해졌습니다.

참고