이번 시간에는 정규 표현식을 배웠다. ‘아니 어제까지만 해도 API 다루다가 갑자기 정규표현식이요?’라는 생각이 들었지만, 우리의 목적은 원하는 대상을 찾는 것이다.
그럼 찾기만 해요?
사실 중요한 것은 찾는 것이다. 하지만 찾은 다음에 그 결과를 가지고 할 수 있는 것은 매우 많다. 예를 들어 어떤 웹사이트의 패턴을 분석하고, 거기서 원하는 정보들만 출력해서 내 사이트에서 또 다른 디자인으로 끌어다 쓸 수도 있다. (물론 저작권은 필수!)
또는 특정 태그만 추출하는 기능을 구현할 수도 있고, 복잡하거나 애매한 문자열들을 조건문을 쓰지 않고 분석해낼 수 있다. 이처럼 정규식의 사용은 프로그래밍언어에 국한되지 않기 때문에 다양한 응용이 가능하다.
- 정규표현식
- 자바스크립트에서 사용
정규 표현식은 자바스크립트 전용이 아닌, 모든 언어에서 범용적으로 사용되는 문법이다.
이는 어떤 언어에 종속되지 않고 고유의 문법으로 일종의 검색 기법이라 할 수 있다.
그 특징은 다음과 같다.
/ 정규표현식 /flags
g: 하나의 검색이 성공해도 중단하지 않고 모든 텍스트에서 전부 검색하는 옵션(전체 검색 옵션)
일반적인 검색은 좌측에서 우측으로 가고, 대상을 찾으면 검색을 끝낸다
i : 대소문자를 가리지 않고 검색하는 옵션
s : .
기호가 개행문자 등의 특수 문자까지 모두 매칭시키게 변경하는 옵션
. 기호는 기본으로 \n, \t와 같은 특수 문자와 매칭되지 않음.
m : \n을 기준으로 모든 문단을 독립적으로 분석하기 위한 옵션
(1) .
: 개행문자와 특수문자를 제외한 모든 단일 문자를 가리킨다.
예시
t.e : t로 시작해서 아무거나 한 글자가 오고 e로 끝나는 패턴
t..e : t로 시작해서 아무거나 두 글자가 오고 e로 끝나는 패턴
이때, “.”을 검색하고 싶으면 .을 해줘야 한다.
(2) ^
$
^...
: 문단의 맨 처음에서 검색
→ 해당 기호를 사용하면 문장의 시작점에 있는 것만 검색한다.
...$
: 문단의 맨 마지막에서 검색
→ 해당 기호를 사용하면 문장의 끝에 있는 것만 검색한다.
^...$
: 문자 그 자체를 의미한다. 곧, 문장의 맨 앞이자, 맨 뒤어야 한다.
(3) []
예시
Gr[abcde∙]y = Gray, Grby, Grcy, Grdy, Grey, Gr∙y
[^abcd] : a, b, c ,d 가 안들어가는 것
응용
[a-z] : a~z까지
[a-zA-Z0-9] : 모든 글자
숫자 : \d == 0~9
(4) ?
기호의 바로 앞 단일 문자가 있어도 되고, 없어도 되는 상태를 검색.
예시) th?e : the or te
수량자( +, *, {} ) 뒤에서 사용하면 최단 매치(Lazy Match)를 하게한다.
?
을 사용해 최단 매치를 실행하게 한다.(5) +
: 기호 바로 앞에 한 문자에 대해 1개 이상일 때를 검색
.+
을 하게 되면 모든 문자가 1개 이상일 때를 탐색하므로 문장 전체를 찾는다. (/./과 다름).+?
을 사용한다.예시
/gskin+er/
gskinner, gskinnner, gskinnnner
(6) *
: 기호 바로 앞에 한 문자에 대해 0개 이상일 때를 검색
예시
/go*gle/
ggle, google, gogle
(7) {}
: 바로 앞 한 문자에 대해서 지정한 개수 일 때를 검색
예시
{2} : 2개
{2,} : 2개 이상
{2, 4} : 2개 이상 4개 이하
전화번호 : ^01[\d]-*[\d]{4}-*[\d]{4}$
ID : ^[a-z_][a-z0-9_]{5,9}$
(8) ()
예시
or 연산이 가능하다.
/Gr(a | e)y/
( a | e ) : ae 이거나 ed 이거나.
Gray, Grey
그룹화하여 한 단어로 만들어 줄 수 있다.
/go(og)+le/
googogle
googogogle
여기서부터는 실제 프로그래밍 상에서 사용법이랑 다르다. 자바스크립트에서 정규표현식은 하나의 객체로 검색을 하면, 그 결과를 주로 배열의 형태로 다루게 된다. 따라서 해당 결과들이 배열에 저장되는데, 그때 regex
의 멤버필드로 저장된다.
$&
(멤버 필드)변수명이 $&
으로 여기에는 검색에 성공한 모든 데이터를 저장하며 배열에선 index[0]이다.
이때 변수는 통제할 수 없는 only-read만 가능한 변수이다.
$n
검색에 성공하면 캡쳐 변수에 묶이는 것처럼 그룹화된 데이터는 따로 뽑혀 변수에 묶이며, 배열에선 index[1]부터 시작된다.
이런 정규표현식을 응용하는 법은 대상 HTML의 패턴을 분석하고, 그 패턴에 맞게 정규표현식을 작성하는 것이다. 이때, 요소의 id값이 어떻게 구성되었는지를 먼저 확인하는 것이 좋다.
그럼 실제 자바스크립트에선 어떻게 사용되는지 알아보자.
JS, JAVA 모두 정규표현식은 하나의 객체로서 존재하며, 그 안에 여러 메서드와 변수들이 존재한다. 또한 정적이라서 언제든지 사용할 수 있다.
자바스크립트에서 사용법은 두 가지이다.
1) 객체를 생성하여 사용하기
let regex = new RegeExp();
2) 쿼리문으로 사용하기
//
으로 쿼리문을 작성하면 자동으로 객체가 생성된다.// 검색 대상
let str = "안녕하세요. 저는 Jack 입니다. 010-1116-2131 또는 010-1234-1234 로 연락주세요.";
// 쿼리문
let regex = /(01[\d]-?[\d]{3,4}-?[\d]{4}) 또는 (01[\d]-?[\d]{3,4}-?[\d]{4})/g;
정규식에서 사용하는 함수는 대표적으로 두 가지가 있다.
1) .test
let = result = regex.test(str);
console.log(result); // 데이터가 존재함으로 true를 반환.
2) .exec
regex.exec(str);
console.log(RegExp.$); // 전체 캡처변수
console.log(RegExp.$1 + " : " + RegExp.$2); // 첫 번째, 두 번째 그룹 캡처 변수
g
flags ]실사용에서 g 플래그가 전체 탐색을 가능하게 하는 원리는 g가 검색 과정에서 세이브 포인트로 동작하기 때문이다. 실제 우리가 .exec
로 검색할 때는 무한 반복문을 통해서 다음과 같이 실행한다.
let regex = /alt="(.+?)".+?(\d.\d\d)</gs;
while (true) {
let result = regex.exec(str);
if (result == null) {
break;
}
console.log(result[1]);
console.log(result[2]);
}
위 상황에서 정규식은 하나의 패턴 안에서 2개의 그룹을 뽑아내고자 한다. 그러나 전체에서 해당 패턴은 열 번이 존재한다고 가정할 때, 출력값은 총 20개가 나와야 한다.
즉, 반복문에서 .exec
는 한 패턴씩 분석한다. 이때 g 플래그를 붙이지 않는다면 무한 반복에 빠지게 된다. 이는 검색 방식과 관련이 있는데 정규식은 검색이 완료되면 다시 처음으로 검색을 시작한 위치로 돌아가게 된다.
그렇게 된다면, 정규식은 계속 똑같은 대상을 검색하고 이를 배열로 반환할 것이기에 if문 안의 break가 동작하지 않는다. 따라서 g 플래그를 붙여주게 된다면 검색 완료 후, 그 위치에 세이브를 하게 된다.
그래서 검색 결과 이후로 또 새로운 검색을 해나가게 되며, 결과적으로 대상 문장의 끝까지 탐색하고 더 이상 결과가 없으면 null
을 반환한다. 실제로 위의 반복문을 없앨지라도 정규식은 한 패턴의 그룹들만 반환한다.