/\\/
정규 표현식(reqular expression)은 일정한 패턴을 가진 문자열의 집합을 표현하기 위해 사용하는 형식 언어다.
정규 표현식은 문자열을 대상으로 패턴 매칭 기능을 제공한다.
패턴 매칭 기능이란 특정 패턴과 일치하는 문자열을 검색하거나 추출 또는 치환할 수 있는 기능
정규표현식은 문자의 패턴을 나타내는 객체다.
자바스크립트에서 정규 표현식은 RegExp 객체로 표현된다. 정규 표현식 리터럴은 한 쌍의 슬래시(/) 문자 사이에 위치한 문자들이다. 따라서 정규 표현식에 대한 자바스크립트 코드는 다음과 같은 형태를 지닌다.
var pattern = /s$/
RegExp 리터럴과 객체 생성
문자열, 숫자 같은 원시 자료형 리터럴은 프로그램 내에서 사용될 때마다 같은 값으로 평가된다. {}와 [] 같은 객체 리터럴은 사용될 때마다 새로운 객체를 생성한다. 예를 들어, 만약 루프 내에서 var a = []을 넣었다면, 루프를 순회할 때마다 새로운 빈 객체가 생성될 것이다.
정규 표현식 리터럴은 특별하다. ECMAScript3 명세에 따르면 RegExp 리터럴은 해당 코드가 분석(parse)될 때 RegExp 객체로 변환되는데, 코드가 같다면 같은 RegExp 객체가 만들어진다. ECMAScript5는 이런 규칙을 뒤집고 RegExp문은 평가될 때마다 새로운 객체를 반환한다. IE는 언제나 ECMAScript5와 같은 방식을 따랐고, 최근의 브라우저들 또한 대부분 ECMAScript5 표준 전체를 구현하고 있지는 않지만, RegExp에 대해서는 그 방식을 따르고 있다.
이 코드는 새 RegExp객체를 생성하고 이것을 변수 pattern에 할당한다. 이 RegExp객체는 "s"로 끝나는 모든 문자열과 매치된다. 이 정규 표현식은 다음과 같이 RegExp() 생성자를 사용하여 정의한 것과 같다.
var pattern = new RegExp("s$");
정규 표현식 패턴은 연속된 문자로 구성되어 있다. 영문자(alphanumeric)를 포함한 문자 대부분은 패턴에 적혀 있는 문자 그대로 매치된다. 따라서 정규 표현식 /java/는 문자열의 일부가 'java'인 모든 문자열과 매치된다. 몇몇 문자는 문자 그대로 매치되지 않으며, 특별한 의미를 지닌다. 예를 들어 정규 표현식 /s$/는 두 문자로 구성되 있는데, 먼저 "s"는 문자 그대로 s와 매치되고, 다으으로 "$"는 특수 메타 문자로 문자열의 끝과 매치된다. 따라서 이 정규 표현식은 "s"로 끝나는 모든 문자열과 매치된다.
다음 항목에서는 자바스크립트 정규 표현식에서 사용되는 다양한 문자와 메타 문자들을 살펴볼 것이다.
10.1.1 리터럴 문자
문자 | 의미 |
문자, 숫자, 영문자 | 문자 그 자신 |
\0 | NUL 문자(\u0000) |
\t | 탭(\u0009) |
\n | 줄바꿈(\u000A) |
\v | 수직 탭(\u000B) |
\f | 폼 피드(\u000C) |
\r | 캐리지 리턴(\u000D) |
\xnn | 16진수 nn으로 명시된 라틴 문자. 예로 \x0A는 \n과 같다. |
\uxxx | 16진수 xxxx로 명시된 유니코드 문자. 예로 \u0009는 \t와 같다 |
\cX | 제어 문자 ^X. 예로 \cJ는 줄바꿈 문자 \n과 같다. |
몇 가지 구두점 문자는 정규 표현식에서 특별한 의미를 지닌다. 다음은 이러한 구두점 문자들이다.
^$.*+?:|\/()[]{}
이 문자들의 의미는 다음 항목에서 다룰 것이다. 이 문자 중 몇 개는 오직 특정 정규 표현식 컨텍스트에서만 특별한 의미가 있고, 다른 컨텍스트에서는 보통의 리터럴로 취급된다. 그러나 일반적인 규칙은, 만약 이러한 구두점 문자 중 어떤 것이라도 일반 문잘 취급하려면 반드시 \를 문자 앞에 붙여야 한다는 것이다. 따옴표나 @같은 구두점 문자는 특별한 의미가 없고, 단순히 문자 그대로 매치된다.
만약, 어떤 구두점 문자를 역슬래시를 사용하여 이스케이프해야 하는지 정확히 기억하지 못한다면, 모든 구두점 문자 앞에 역슬래시를 붙여서 안전하게 처리할 수도 있다. 그러나 많은 글자와 숫자가 그 앞에 역슬래시가 놓이면 특별한 의미를 지닌다는 점을 유념하라. 따라서 글자 그대로 매칭되어야 하는 모든 글자의 숫자는 역슬래시를 사용하여 이스케이프되어서는 안된다. 그리고, 당연한 얘기지만 정규 표현식에 역슬래시 문자를 있는 그대로 넣으려면 역슬래시 자체를 이스케이프 해야 한다. 예로 다음 정규 표현식은 역슬래시를 포함하는 모든 문자열과 매치된다.
/\\/
10.1.2 문자 클래스
개별 리터럴 문자들은 그 문자들을 대활호로 묶어서 문자 클래스로 다룰 수 있다. 문자 클래스는 해당 클래스 내의 모든 문자에 매치된다. 따라서 정규 표현식 /[abc]/는 a, b, c 중 아무 글자에나 매치된다. 또한, 부정 문자 클래스도 정의될 수 있는데, 이는 대괄호 안에 있는 문자들을 제외한 모든 문자와 매치된다. 대괄호 안에서 캐럿(^)을 제일 앞에 두면 부정 문자 클래스를 정의할 수 있다. 정규 표현식 /[^abc]/는 a, b, c를 제외한 모든 문자와 매치된다. 문자 클래스는 하이픈(-)을 사용하여 문자의 범위를 지정할 수도 있다. 따라서 라틴 알파벳 소문자와 매치되게 하려면 /[a-z]/를 사용하고, 모든 라틴 알파벳 글자나 숫자와 매치하려면 /[a-zA-Z0-0]/을 사용하면 된다.
어떤 클래스들은 자주 사용되기 때문에, 자바스크립트 정규 표현식 문법에는 자주 사용되는 문자 클래스를 나타내는 특수 문자와 이스케이프 문자열이 정의되어 있다. 예를 들어 \s는 스페이스 문자와 탭 문자, 다른 모든 유니코드 공백 문자와 매치된다. \S는 유니코드 공백 문자를 제외한 모든 문자와 매치된다. 표 10-2에 이런 문자와 문자 클래스 문법을 나열했다. (문자 클래스를 나타내는 이스케이프 문자열 중 몇가지는 오직 ASCII 문자에 대해서만 유효하고 유니코드 문자에서는 작동하지 않는다는 점을 유의하라. 그러나 필요하다면 유니코드 문자 클래스를 직접 정의하여 사용할 수 있다. 예를 들어 임의의 한 키릴 문자를 나타내려면 /[\u0400-\\u04FF]/를 사용하면 된다.)
이스케이프된 특수 문자 클래스를 대괄호 내에서 사용할 수 있다는 점을 눈여겨보라. \s는 모든 공백 문자와 매치되고 \d는 모든 숫자와 매치된다. 따라서 /[\s\d]/는 공백 문자 또는 숫자 문자 하나와 매치된다. 그러나 한 가지 특별한 경우가 있다. 다음에 살펴보겠지만 \b는 별도의 특별한 의미를 지니고 있다. 그러나 문자 클래스 내에서 사용되면 백스페이스 문자를 나타낸다. 따라서 정규 표현식에서 백스페이스 문자 자체를 표현하려면 다음과 같이 하나의 요소만 있는 문자 클래스를 사용해야한다.
/[\b]/
문자 | 의미 |
[...] | 대괄호 사이에 있는 한 문자. |
[^...] | 대괄호 사이에 없는 한 문자. |
. | 줄바꿈 문자나 유니코드 라인 종료 문자를 제외한 모든 문자 |
\w | ASCII 워드 문자. [a-zA-Z0-9_]와 동등하다 |
\W | ASCII 워드 문자가 아닌 모든 문자. [^a-zA-Z0-9_]와 동등하다 |
\s | 모든 유니코드 공백 문자 |
\S | 유니코드 공백 문자가 아닌 모든 문자. 하지만 \w와 \S는 같지 않음을 주의하라. |
\d | 모든 ASCII 숫자 [0-9]와 동등하다. |
\D | ASCII 숫자가 아닌 모든 문자. [^0-9]와 동등하다. |
[\b] | 백스페이스 리터럴(특수한 경우이다) |
10.1.3 반복
지금까지 배운 정규 표현식 문법을 사용하면 여러분은 두 자리 숫자를 /\d\d/로, 네 자리 숫자를 /\d\d\d\d/로 표현할 수 있다. 그러나 임의 자리의 수 또는 세 개의 문자가 있고 뒤이어 생략 가능한 숫자로 이루어진 문자열을 표현하기는 어려울 것이다. 이런 복잡한 패턴을 작성할 때는 정규 표현식의 요소가 몇 번이나 반복되는지를 나타내는 문법을 사용해야 한다.
반복을 지정하는 문자는 언제나 반복을 지정할 패턴 뒤에 나온다. 어떤 종류의 반복은 매우 자주 사용되기 때문에, 이런 반복을 표현하는 몇 가지 특수 문자가 있다. 예를 들면, 더하기 기호(+)는 앞의 패턴이 한 번 이상 나타나는 경우를 의미하낟. 표 10-3에 이런 반복 문법을 요약해두었다.
/\d{2,4}/ // 두 자리에서 네 자리의 숫자와 매치된다.
/\w{3}\d?/ // 정확히 세 개의 문자와 생략 가능한 숫자와 매치된다.
/\s+java\s+/ // java와 앞뒤로 하나 이상의 공백 문자를 포함하 문자열과 매치된다.
/[^(]*/ // 시작 괄호가 아닌 0개 이상의 문자와 매치한다.
문자 | 의미 |
{n, m} | 앞의 항목이 적어도 n번 이상, m번 이하로 나타난다. |
{n,} | 앞의 항목이 n번 이상 나타난다. |
{n} | 앞의 항목이 정확하게 n번 나타난다. |
? | 앞의 항목이 0 또는 한 번 나타난다. 즉, 앞의 항목이 나오지 않을 수도 있다. {0, 1}과 동등하다. |
+ | 앞의 항목이 한 번 이상 나타난다. {1,}과 동등하다 |
* | 앞의 항목이 0번 또는 그 이상 나타난다. {0,}과 동등하다. |
*와 ? 반복 문자를 사용할 때는 조심해야 한다. 이 문자들은 그 앞에 오는 문자가 무엇이든 간에 0번 매치된다고 간주하기에, 반복 문자열 앞의 문자가 매치되지 않는 상황도 허용한다. 예를 들어 정규 표현식 /a*/는 실제로 문자열 "bbbb"와 매치되는데, 이는 이 문자열에 a가 0번 나타나기 때문이다.
비탐욕 반복
표10-3에 나열된 반복 문자는 주어진 정규 표현식의 나머지 부분인 매칭되는 한, 최대한 많은 문자와 매치된다. 우리는 이러한 반복을 "탐욕적(greedy)"이라 부른다. 이 반복을 비탐욕적으로 수행하게 지정할 수도 있는데, 이는 ??, +?, *?, {1,5}?와 같이 반복 문자나 일반 문자 뒤에 물음표를 추가해주면 된다. 예를 들어, 정규 표현식 /a+/는 한 개 이상의 a와 매치되는데, 이 정규 표현식을 "aaa"에 적용하면 세 문자 모두 매치한다. 그러나 /a+?/는 매칭에 필요한 a가 한 번 이상 나타난더라도, 최소한의 글자만 매칭한다. "aaa"에 이 정규 표현식을 적용하면 오직 첫 번째 글자 "a"만 매치된다.
비탐욕적 반복이 언제나 기대하 결과를 가져다주지는 않는다. /a+b/ 패턴은 하나 이상의 a와 그 다음에 b가 나타나는 경우와 매치한다. 이 패턴이 "aaab" 문자열에 적용되면 이 정규 표현식은 전체 문자열과 매치된다. 이제 비탐욕적 정규 표현식 /a+?b/를 살펴보자. 이 정규 표현식은 가능한 적은 수의 a와 b 한문자를 매치한다. 이 정규 표현식을 같은 문자열 "aaab"에 적용했을 때, 아마도 오직 하나의 a와 마지막 글자 b에 매치되기를 기대할 것이다. 그러나 사실 이패턴은 탐욕적 정규 표현식과 마찬가지로 전체 문자열과 매치된다. 왜냐하면, 매칭이 가능한 첫번째 위치부터 매치해 나가기 때문이다. 이 경우 문자열의 첫번째 문자부터 매칭이 가능하므로, 이후의 문자로부터 시작하는 더 짧은 매치는 전혀 고려되지 않늗다.
정규 표현식
일정한 패턴을 가진 문자열의 집합을 표현하기 위해 사용하는 형식언어다.
제공기능
- 문자열을 대상으로 패턴 매칭 기능: 특정 패턴과 일치하는 문자열을 검색하거나 추출 또는 치환할 수 있는 기능
- 반복문과 조건문 없이 패턴을 정의하고 테스트하는 것으로 간단히 체크 가능
- 주석이나 공백을 허용하지 않고 여러가지 기호를 혼합하여 사용하기 때문에 가독성이 좋지않다.
// 사용자로부터 입력받은 휴대폰 전화번호
const tel = '010-1234-567팔';
// 정규 표현식 리터럴로 휴대폰 전화번호 패턴을 정의한다.
const regExp = /^\d{3}-\d{4}-\d{4}$/;
// tel이 휴대폰 전화번호 패턴에 매칭하는지 테스트(확인)한다.
regExp.test(tel);
31.2 정규 표현식의 생성
정규 표현식 객체(RegExp 객체) 를 생성하기 위해서는 정규 표현식 리터럴과 RegExp 생성자 함수를 사용할 수 잇다. 일반적인 방법은 정규 표현식 리터럴을 사용하는 것이다. 정규 표현식 리터럴은 다음과 같이 표현한다. 이처럼 정규 표현식 리터럴은 패턴과 플래그로 구성된다. 정규 표현식 리터럴을 사용하여 간단한 정규 표현식 객체를 생성해 보자.
const target = 'Is this all there is?";
// 패턴 is
// 플래그: i => 대소문자를 구별하지 않고 검색한다.
const regexp = /is/i;
// test 메서드는 target 문자열에 대해 정규 표현식 regexp의 패턴을 검색하여 매칭 결과물
// 불리언 값으로 반환한다.
regexp.test(target); // true
RegExp 생성자 함수를 사용하여 RegExp 객체를 생성할 수도 있다.
// pattern: 정규 표현식의 패턴
// flags: 정규 표현식의 플래그(g, i, m, u, y)
new RegExp(pattern[, flags])
const taret = 'Is this all there is?"
const regexp = new RegExp(/is/i); // ES6
// const regexp = new RegExp(/is/, 'i');
// const regexp = new RegExp('is', 'i');
regexp.test(target);
RegExp 생성자 함수를 사용하면 변수를 사용해 동적으로 RegExp 객체를 생성할 수 있다.
const count = (str, char) => (str.match(new RegExp(char, 'gi')) ?? []).length;
count('Is this all there is?', 'is'); // 3
count('Is this all there is?', 'xx'); // 0
31.3 RegExp 메서드
정규 표현식을 사용하는 메서드는 RegExp.prototype.exec, RegExp.prototype.test, String.prototype.match, String.prototype.relace, String.prototype.search, String.prototype.split 등이 있다.
String.prototype.relace, String.prototype.search, String.prototype.split 메서드는 32장 "String"에서 살펴보고 지금은
31.3.1 RegExp.prototype.exec
exec 메서드는 인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 매칭 결과를 배열로 반환한다. 매칭 결과가 없는 경우 null을 반환한다.
const target = 'Is this all there is?';
const regExp = /is/;
regExp.exec(target);
// → ["is", index: 5, input: "Is this all there is?", groups: undefined]