✔️ 패턴과 플래그
정규 표현식(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는 문자열인데, 문자열 안에 특수 문자를 넣어주면 독특한 방식으로 문자열을 교체할 수 있다.
👇 $&를 사용한 예시
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
g 플래그가 없으면 정규 표현식은 패턴과 일치하는 첫 문자만 찾기 때문에 7이 뜬다.
👇 g 플래그를 추가
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\d는 CSS 뒤에 숫자 하나가 있는 문자열과 일치한다.
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.B는 A와 B 사이에 \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, 된다.