[Regex] JS
JS Regex
regex에서 "또는 or"을 표현하기 위해 ``js [(bar)(foo)]`` 처럼 사용하면 ``()``로 묶어도 한 char 씩 잘라서 인식하기 때문에
``js ((bar)|(foo))``를 사용해야 한다.
()가 추가되기 때문에 캡쳐링 그룹 인덱스가 틀려질 수 있다는 점에 주의한다.
JS에서 정규 표현식은 보통 같은 문자열에 대해 반복해서 연산을 수행할 때 주목할 만한 성능상의 이점이 있다.
그래서 ``indexOf``를 쓰는 것 보다는 정규 표현식을 쓰는 것이 좋다.
정규식은 inline 보다는 변수에 저장해서(stored) 사용하는 것이 좋고,
RegExp 객체를 사용하는 것 보다 literal을 사용하는 것이 좋다.
또한, 반복문 안에 넣으면 계속 컴파일 되는 경우가 있으므로 반복문 외부에 선언한다.
literal
```js
const re = /ab+c/;
```
스크립트가 로드되었을 때 정규식의 컴파일을 제공한다.
정규식이 계속 지속 될것이라면 이렇게 사용하는 것이 좋다.
RegExp 객체의 생성자 함수
```js
const re = new RegExp("ab+c");
```
정규식의 런타임 컴파일을 제공한다.
정규식 패턴이 바뀔 것이라고 알고 있거나, 패턴을 잘 모르거나, 사용자 입력과 같이 다른 소스로부터 패턴을 얻어 올 때에 생성자 함수를 쓰는 것이 좋은데, stored literal에 비해 많이 느리다.
역슬래시가 literal에서와는 조금 다른 의미로 보통 두 개의 역슬래시를 사용해야 하며 따옴표도 이스케이프시켜야 한다.
capturing group
`` = bar foo=``는 객체가 반환되기는 했으나 `` length == 0``
``js [a-z]*``가 0개이고 ``\s``가 붙는 것으로 잡혀서 객체가 반환되기는 된다.
``=bar=``는 객체가 반환되지 않는다.
``js ([a-z]*)\s``가 아예 없는 것으로 잡힌다.
중요한 것은 둘 다 false로 취급된다는 것.
JS는 ``js null, undefined, NaN, 빈 문자열 '', 0, false``를 ``js false``로 본다.
* 이 외의 값은 모두 참이다.
해서 ``js test()``를 사용하지 않아도 ``js if(matches[1])``로 검사할 수 있다.
RegExp.$_ / $1...9
``RegExp.$_`` : 최근에 탐색한 문자열 전체를 저장하고 있다.
``RegExp.$1...$9`` : 가장 최근에 매치된 capturing group을 저장하고 있다.
``js test()``를 사용했더라도, group으로 묶었다면 RegExp.$1 ~ $9에 저장된다.
또한 ``js exec()``의 반환값 ``[매칭된 부분 전체, captured group1, captured group2, ...]``은
``[RegExp.$_, RegExp.$1, RegExp.$2, ...]`` 와 동일하다.
그래서 ``js exec()``대신 ``js text()``를 사용해도 구현상 문제는 없으며
``js text()``를 사용하는 것에 성능 이득을 보려면 캡쳐링 하지 않도록 ``(?:...)``을 지정해야 한다.
match()
그래서 matched substrings을 반환받는 동시에 captured groups도 반환받고 싶다면, ``js exec()``를 반복 호출해야 한다.
exec()
``g`` flag를 설정하는 경우 몇 가지 주의할 점이 있다.
```js
var re = /Hello/g;
var matches = re.exec("Hello world! Hello world2! Hello world3!);
```
직관적으로 생각해 보면 ``matches``에 ``[Hello, Hello, Hello]``가 반환될 것 같지만 아니다. 그건 ``js match()``를 사용했을 경우다.
``js exec()``가 반환하는 값은 항상 ``[매칭된 부분 전체, captured group1, captured group2, ...]`` 이다.
뒤에 있는 Hello를 잡고 싶다면, global flag를 그대로 두고 ``js exec()``를 연속으로 호출해야 한다.
``g`` flag는 다음 패턴 검색을 위해 어디서 부터 패턴 매칭을 수행해야 하는지를 저장(``js RegExp.lastIndex``)해 두는 역할을 하기 때문에 ``js exec()``를 연속으로 호출하면 뒤에 있는 Hello가 차례로 검출된다.
이상하게 동작하는 경우
이상하게 동작하는 경우는 보통 다음 두 가지 이유로 일어난다.
#1 각 ``js exec()``를 호출하는 사이에 문자열이 변경되는 경우
#2 각 ``js exec()``를 호출하는 사이에 정규표현식 객체를 재사용하는 경우.
정상적인 예제는 다음과 같다.
#1 각 exec()를 호출하는 사이에 문자열이 변경되는 경우
그래서 문자열을 변경해야 하는 경우, 다음과 같이 원본 문자열을 buffer에 넣어놓고 이를 대상으로 match 검사를 수행하는 것이 좋다.
```js
var match_buf = line;
var matches = re.exec(match_buf);
```
#2 각 exec()를 호출하는 사이에 정규표현식 객체를 재사용하는 경우.
``g`` flag가 있지만, 반복을 거듭해도 첫 번째 매치된 문자열만 계속 매치되며 무한 루프에 빠지게 된다.
이는 ``re``객체를 ``replace()``에서 재사용해서 어디까지 읽었는지 저장해놓은 index가 초기화되어, ``g`` flag가 없을 때 처럼 매번 처음부터 다시 매치하기 때문이다.
그래서 ``re``객체를 재사용하는 것이 아니라, 반환된 matched substring과 captured group을 사용해야 한다.
```js
line = line.replace(matches[0], "REP"+matches[1]);
```
'JS Stack > JS' 카테고리의 다른 글
[JS] 함수 #2. Call pattern, 생성자 대안(함수형 패턴), 상속 (0) | 2017.05.31 |
---|---|
[JS] 함수 #1. 유효범위(scope), callback, 클로저(closure), 접근 제어 (0) | 2017.05.31 |
[JS] 객체, 프로토타입, 기본 타입에 기능 추가 (0) | 2017.05.29 |
[JS] TIP (0) | 2017.05.25 |
[JS] 리터럴, 자료형, 반복문 (0) | 2017.03.01 |