[정규 표현식] 패턴과 플래그 & 문자 클래스

 

✔️ 패턴과 플래그

정규 표현식(regular expression)은 문자 검색과 교체에 사용되는 패턴으로 강력한 기능을 제공한다. 자바스크립트에선 RegExp 객체와 문자열 메서드를 조합해 정규표현식을 사용할 수 있다.

 

정규 표현식

정규 표현식(‘regexp’ 또는 'reg’라고 줄여서 사용)은 패턴(pattern) 과 선택적으로 사용할 수 있는 플래그(flag) 로 구성된다.

정규식 객체를 만들 땐 두 가지 문법이 사용된다.

 

긴 문법

regexp = new RegExp("pattern", "flags");

 

짧은 문법은 슬래시(빗금) "/"를 사용한다.

regexp = /pattern/; // 플래그가 없음 
regexp = /pattern/gmi; // 플래그 g, m, i가 있음

슬래시 "/"는 자바스크립트에게 정규 표현식을 생성하고 있다는 것을 알려준다. 문자열에 따옴표를 쓰는 것과 동일한 역할을 한다.

짧은 문법을 사용하던 긴 문법을 사용하던 상관 없이 위 예시에서의 regexp는 내장 클래스 RegExp의 인스턴스가 된다.

 

두 문법의 중요한 차이점은 /.../를 사용하면 문자열 템플릿 리터럴에서 ${...}를 사용했던 것처럼 중간에 표현식을 넣을 수 없다는 점이다. 슬래시를 사용한 방법은 완전히 정적이다.

 

슬래시를 사용한 짧은 문법은 코드를 작성하는 시점에 패턴을 알고 있을 때 사용한다. 반면 new RegExp를 사용한 긴 문법은 상황에 따라 동적으로 생성된 문자열을 가지고 정규 표현식을 만들어야 할 때 주로 사용한다.

let tag = prompt("어떤 태그를 찾고 싶나요?", "h2"); 
let regexp = new RegExp(`<${tag}>`); 
// 프롬프트에서 "h2"라고 대답한 경우, /<h2>/와 동일한 역할.

 

플래그

정규 표현식엔 검색에 영향을 주는 플래그를 선택적으로 붙일 수 있다.

자바스크립트가 지원하는 6개의 플래그

  • i

    i 플래그가 붙으면 대·소문자 구분 없이 검색한다. 따라서 A와 a에 차이가 없다.

  • g

    g 플래그가 붙으면 패턴과 일치하는 모든 것들을 찾는다. g 플래그가 없으면 패턴과 일치하는 첫 번째 결과만 반환된다. **m**다중 행 모드(multiline mode)를 활성화한다.

  • s

    .이 개행 문자 \\n도 포함하도록 ‘dotall’ 모드를 활성화한다.

  • u

    유니코드 전체를 지원한다. 이 플래그를 사용하면 서로게이트 쌍(surrogate pair)을 올바르게 처리할 수 있다.

  • y

    문자 내 특정 위치에서 검색을 진행하는 ‘sticky’ 모드를 활성화 시킨다.

ℹ️ 색상

정규 표현식을 설명할 때 사용되는 밑줄의 색상은 각각 다음과 같은 상징이 있다.

정규 표현식: 빨강
검색할 문자열: 파랑
검색 결과: 초록

 

str.match로 검색하기

str.match(regexp)를 호출하면 문자열 str에서 regexp와 일치하는 것들을 찾아내는 식으로 정규 표현식은 문자열 메서드와 조합하여 사용한다.

 

str.match 세 가지 모드

 

1. 정규 표현식에 플래그 g가 붙으면 패턴과 일치하는 모든 것을 담은 배열을 반환한다.

let str = "We will, we will rock you"; 

alert( str.match(/we/gi) ); // We,we (패턴과 일치하는 부분 문자열 두 개를 담은 배열)

대·소문자 구분 없이 검색을 할 수 있도록 하는 플래그 i를 사용했기 때문에 We와 we 모두가 반환되었다.

 

2. 플래그 g가 붙지 않은 경우엔 패턴에 맞는 첫 번째 부분 문자열만 담은 배열을 반환한다. 해당 문자열은 배열 인덱스 0에 저장되는데 이 문자열의 프로퍼티엔 상세한 추가 정보가 저장된다.

let str = "We will, we will rock you"; 

let result = str.match(/we/i); // 플래그 g 없음 

alert( result[0] ); // We (패턴에 일치하는 첫 번째 부분 문자열) 
alert( result.length ); // 1 

// Details: 
alert( result.index ); // 0 (부분 문자열의 위치) 
alert( result.input ); // We will, we will rock you (원본 문자열)

정규 표현식을 괄호로 둘러싼 경우엔 메서드 호출 시 반환되는 배열에 0 이외에도 다른 인덱스가 있을 수 있다.

 

3. 플래그 g의 유무와 상관없이 패턴과 일치하는 부분 문자열을 찾지 못한 경우엔 null이 반환된다. 빈배열이 아닌 null을 반환 하는 것은 중요한 차이점이다.

👇 무시하고 진행할 경우

let matches = "JavaScript".match(/HTML/); // matches엔 null이 저장됨 

if (!matches.length) { // TypeError: Cannot read property 'length' of null 
	alert("바로 윗줄에서 에러가 발생합니다."); 
}

👇 str.match 호출 결과가 항상 배열이 되게 하려면

let matches = "JavaScript".match(/HTML/) *|| []*; 

if (!matches.length) { 
	alert("정규 표현식과 일치하는 부분 문자열이 없습니다."); // 이제 에러없이 잘 동작한다. 
}

 

str.replace로 치환하기

메서드 str.replace(regexp, replacement)를 사용하면 str 내 부분 문자열 중 regexp에 일치하는 부분 문자열을 replacement로 교체할 수 있다. 이때 플래그 g가 있으면 모든 부분 문자열이 교체되고, 그렇지 않으면 첫 번째 부분 문자열만 교체된다.

// 플래그 g 없음 
alert( "We will, we will".replace(/we/i, "I") ); // I will, we will 

// 플래그 g 있음 
alert( "We will, we will".replace(/we/ig, "I") ); // I will, I will

여기서 두 번째 인수 replacement는 문자열인데, 문자열 안에 특수 문자를 넣어주면 독특한 방식으로 문자열을 교체할 수 있다.

특수 문자

 

특수 문자

A new tool for teams & individuals that blends everyday work apps into one.

www.notion.so

👇 $&를 사용한 예시

alert( "I love HTML".replace(/HTML/, "$& and JavaScript") ); // I love HTML and JavaScript

 

regexp.test로 일치 여부 확인하기

패턴과 일치하는 부분 문자열이 하나라도 있는 경우 메서드 regexp.test(str)을 호출하면 true가, 그렇지 않으면 false가 반환된다.

let str = "I love JavaScript"; 
let regexp = /LOVE/i; 

alert( regexp.test(str) ); // true

*메서드 정보: Methods of RegExp and String

 

 

 

✔️ 문자 클래스

문자 클래스(character class): 특정 집합에 포함된 모든 기호에 일치하는 특별한 표현

숫자(digit) 클래스: \d라고 쓰고 아무 숫자 하나에 대응한다.

let str = "+7(903)-123-45-67"; 

let regexp = /\\d/; 

alert( str.match(regexp) ); // 7

플래그가 없으면 정규 표현식은 패턴과 일치하는 첫 문자만 찾기 때문에 7이 뜬다.

👇플래그를 추가

let str = "+7(903)-123-45-67"; 

let regexp = /\\d/g; 

alert( str.match(regexp) ); // 일치하는 문자의 배열: 7,9,0,3,1,2,3,4,5,6,7 

// 위 배열로 숫자만 있는 전화번호를 만든다. 
alert( str.match(regexp).join('') ); // 79035419441

자주 사용하는 문자 클래스

  • \d ('digit(숫자)'의 ‘d’)

    숫자: 0에서 9 사이의 문자

  • \s ('space(공백)'의 ‘s’)

    스페이스, 탭(\t), 줄 바꿈(\n)을 비롯하여 아주 드물게 쓰이는 \v, \f, \r 을 포함하는 공백 기호

  • \w ('word(단어)'의 ‘w’)

    단어에 들어가는 문자로 라틴 문자나 숫자, 밑줄 _을 포함한다. 키릴 문자나 힌디 문자같은 비 라틴 문자는 \w에 포함되지 않는다.

    ex) \d\s\w 1 a처럼 ‘숫자’ 뒤에 ‘공백 문자’ 뒤에 '단어에 들어가는 문자’를 의미한다.

정규 표현식에 일반 기호와 문자 클래스를 같이 사용할 수 있다.

👇  CSS\dCSS 뒤에 숫자 하나가 있는 문자열과 일치한다.

let str = "Is there CSS4?"; 
let regexp = /CSS\\d/; 

alert( str.match(regexp) ); // CSS4

👇 문자 클래스를 여러 개 사용할 수도 있다.

alert( "I love HTML5!".match(/\\s\\w\\w\\w\\w\\d/) ); // ' HTML5'

 

반대 클래스

모든 문자 클래스에는 반대 클래스가 있다. 같은 글자로 표기하지만 대문자로 사용한다.

반대: 해당 문자를 제외한 모든 문자에 일치한다는 뜻.

  • \D
    숫자가 아닌 문자: \d와 일치하지 않는 일반 글자 등의 모든 문자

  • \S
    공백이 아닌 문자: \s와 일치하지 않는 일반 글자 등의 모든 문자

  • \W
    단어에 들어가지 않는 문자: \w와 일치하지 않는 비 라틴 문자나 공백 등의 모든 문자

 

\D로 숫자가 아닌 문자를 찾아 문자열에서 없애버리면 더 짧게 숫자만 있는 전화번호를 만들 수 있다.

let str = "+7(903)-123-45-67"; 

alert( str.replace(/\\D/g, "") ); // 79031234567

 

점은 '아무 문자’에나 일치

. : 줄 바꿈 문자를 제외한 모든 문자와 일치하는 특별한 문자 클래스

alert( "Z".match(/./) ); // Z

정규 표현식 안에서도 사용 가능하다.

let regexp = /CS.4/; 

alert( "CSS4".match(regexp) ); // CSS4 
alert( "CS-4".match(regexp) ); // CS-4 
alert( "CS 4".match(regexp) ); // CS 4 (공백도 문자)

점은 아무 문자에나 일치하지만 '문자의 부재’와 일치하지 않기 때문에 반드시 일치하는 문자가 있어야 한다.

alert( "CS4".match(/CS.4/) ); // null, 점과 일치하는 문자가 없기 때문에 일치 결과가 없다.

 

: ‘s’ 플래그와 점을 사용해 정말로 모든 문자 찾기

기본적으로는 점은 줄 바꿈 문자 \n과 일치하지 않는다.

= 정규 표현식 A.BAB 사이에 \n을 제외한 어떤 문자든 들어간 문자열과 일치한다.

alert( "A\\nB".match(/A.B/) ); // null (일치하지 않음)

점이 줄 바꿈 문자를 포함한 모든 문자와 일치해야 하는 상황이 자주 있다.

이럴 때 s를 사용한다. 정규 표현식에 s를 사용했을 때 점.은 모든 문자와 일치한다.

alert( "A\\nB".match(/A.B/s) ); // A\\nB (일치!)

 

 

⚠️ Firefox, IE, Edge에서 지원하지 않음

https://caniuse.com/#search=dotall에서 지원 여부의 최신 상황을 확인 가능하다. 이 글을 작성하는 시점에서 s 플래그는 Firefox, IE, Edge에서 지원하지 않는다.

대안으로 [\\s\\S]같은 정규 표현식을 사용해 '모든 문자’와 일치시킬 수 있다.


[\s\S]: 공백 문자 또는 공백 문자가 아닌 문자.(= 모든 문자)

반대되는 클래스를 같이 사용하는 다른 패턴을 사용할 수도 있다. 또는 아무 문자도 없는 경우를 제외하는 [^]을 사용할 수도 있다.

이 기술을 이용해 같은 정규 표현식 안에서 점의 두 가지 패턴을 모두 사용할 수 있다. 줄 바꿈 문자를 포함하지 않는 원래의 점.과 모든 문자와 일치하는 [\s\S]같은 패턴을 동시에 사용 가능하다.

alert( "A\\nB".match(/A[\\s\\S]B/) ); // A\\nB (일치!)

 

⚠️ 공백에 주의

정규 표현식에서 공백을 염두하지 않으면 원하는 결과를 얻지 못할 수 있다.

정규 표현식에서는 공백을 포함한 모든 문자가 중요하다. 공백을 추가하거나 지우면 다르게 작동한다.

👇 하이픈으로 구분된 숫자를 찾기

alert( "1 - 5".match(/\\d-\\d/) ); // null, 일치 결과 없음!



👇 정규 표현식에 공백을 넣어서 \\d - \\d로 바꾸기

alert( "1 - 5".match(/\\d - \\d/) ); // 1 - 5, 제대로 된다. // 
\\s 클래스를 사용해도 된다. 
alert( "1 - 5".match(/\\d\\s-\\s\\d/) ); // 1 - 5, 된다.