정규표현식에 다양한 특수기호를 함께 사용하면 문자열을 다룰 때에 더 많은 옵션을 설정할 수 있다.
^는 문자열의 처음을 의미하며, 문자열에서 ^뒤에 붙은 단어로 시작하는 부분을 찾는다.
일치하는 부분이 있더라도, 그 부분이 문자열의 시작이 아니면 null을 리턴한다.
"Are you ready?".match(/^Ar/); // ['Ar']
"Are you ready?".match(/^rea/); // null
$는 문자열의 끝을 의미하며, 문자열에서 $앞의 표현식으로 끝나는 부분을 찾는다.
일치하는 부분이 있더라도, 그 부분이 문자열의 끝부분이 아니면 null을 리턴한다.
"Are you ready?".match(/y?$/); // ['y?']
"Are you ready?".match(/you$/); // null
"Are you ready?".match(/^Are you ready?$/);
// 문자열을 ^ 와 $ 로 감싸주면 그 사이에 들어간 문자열과 정확하게 일치하는 부분을 찾는다.
// ["Are you ready?"]
*
는 *
바로 앞의 문자가 0번 이상 나타나는 경우를 검색한다. 아래와 같은 문자열이 있을 때에 /you*/g
을 사용하게 되면 'yo'가 포함되면서 그 뒤에 'u'가 0번 이상 포함된 모든 문자열을 리턴한다.
"yo you youu youuu youuuu youuuuuu doyo".match(/you*/g);
// ["yo", "you", "youu", "youuu", "youuuu", "youuuuuu", "yo"]
+
는 *
과 같은 방식으로 작동하며, 다만 +
바로 앞의 문자가 1번 이상 나타나는 경우를 검색한다는 점이 다르다.
"yo you youu youuu youuuu youuuuuu doyo".match(/you+/g);
// ["you", "youu", "youuu", "youuuu", "youuuuuu"]
?
는 *
또는 +
와 비슷하지만, ?
앞의 문자가 0번 혹은 1번 나타나는 경우만 검색한다.
*?
또는 +?
와 같이 ?
는 *
혹은 +
와 함께 쓰는 것도 가능하다.
"yo you youu youuu youuuu youuuuuu doyo".match(/you?/g);
// ["yo", "you", "you", "you", "you", "you", "yo"]
"yo you youu youuu youuuu youuuuuu doyo".match(/you*?/g);
// ["yo", "yo", "yo", "yo", "yo", "yo", "yo"]
"yo you youu youuu youuuu youuuuuu doyo".match(/you+?/g);
// ["you", "you", "you", "you", "you"]
{}
는 *, *?, +, +?
의 확장판으로 생각할 수 있다. *, *?, +, +?
가 '0개 이상' 또는 '1개 이상' 검색이 전부라면, {}
는 직접 숫자를 넣어서 연속되는 개수를 설정할 수 있다.
"yo you youu youuu youuuu youuuuuu doyo".match(/you{3}/g);
// 3개의 'u'를 포함한 문자열을 검색한다.
// ["youuu", "youuu", "youuu"]
"yo you youu youuu youuuu youuuuuu doyo".match(/you{3,}/g);
// 3개 이상의 'u'를 포함한 문자열을 검색한다.
// ["youuu", "youuuu", "youuuuuu"]
"yo you youu youuu youuuu youuuuuu doyo".match(/you{3,5}/g);
// 3개 이상 5개 이하의 'u'를 포함한 문자열을 검색한다.
// ["youuu", "youuuu"]
|
는 or 조건으로 검색하여 |
의 왼쪽 또는 오른쪽의 검색 결과를 반환한다.
"Cc Oo Dd Ee".match(/O|D/g); // ["O", "D"]
"Cc Oo Dd Ee".match(/c|e/g); // ["c", "e"]
"Cc Oo Dd Ee".match(/D|e/g); // ["D", "e"]
"Ccc Ooo DDd EEeee".match(/D+|e+/g); // + 는 1번 이상 반복을 의미하기 때문에
// ["DD", "eee"] 를 반환한다.
대괄호 []
안에 명시된 값을 검색한다.
[abc] // a or b or c 를 검색한다. or(|) Operator 로 작성한 a|b|c 와 동일하게 작동한다.
[a-c] // [abc] 와 동일. - 로 검색 구간을 설정할 수 있다.
"Ccc Ooo DDd EEeee".match(/[CD]+/g); // [] 에 + 등의 기호를 함께 사용할 수도 있다.
// C or D 가 한 번 이상 반복된 문자열을 반복 검색하기 때문에
// ["C", "DD"] 가 반환된다.
"Ccc Ooo DDd EEeee".match(/[co]+/g); // ["cc", "oo"]
"Ccc Ooo DDd EEeee".match(/[c-o]+/g); // - 때문에 c ~ o 구간을 검색하여
// ["cc", "oo", "d", "eee"] 가 반환된다.
"AA 12 ZZ Ad %% Az !# dd 54 zz".match(/[A-Za-z]+/g);
// a~z 또는 A~Z 에서 한 번 이상 반복되는 문자열을 반복 검색하기 때문에
// ["AA", "ZZ", "Ad", "Az", "dd", "zz"] 를 반환된다.
"AA 12 ZZ Ad %% Az !# dd 54 zz".match(/[A-Z]+/gi);
// flag i 는 대소문자를 구분하지 않기 때문에 위와 동일한 결과를 반환된다.
// ["AA", "ZZ", "Ad", "Az", "dd", "zz"]
"AA 12 ZZ Ad %% Az !# dd 54 zz".match(/[0-9]+/g);
// 숫자도 검색 가능합니다.
// ["12", "54"]
"aAbB$#67Xz@9".match(/[^a-zA-Z]+/g);
// [] 안에 ^ 를 사용하면 anchor 로서의 문자열의 처음을 찾는것이 아닌
// 부정을 나타내기 때문에 [] 안에 없는 값을 검색한다.
// ["$#67", "@9"]
\d
& \D
\d
의 d 는 digit 을 의미하며 0 ~ 9 사이의 숫자 하나를 검색한다. [0-9]
와 동일.
\D
는 not Digit 을 의미하며, 숫자가 아닌 문자 하나를 검색한다. [^0-9]
와 동일.
"abc34".match(/\d/); // ["3"]
"abc34".match(/[0-9]/) // ["3"]
"abc34".match(/\d/g); // ["3", "4"]
"abc34".match(/[0-9]/g) // ["3", "4"]
"abc34".match(/\D/); // ["a"]
"abc34".match(/[^0-9]/); // ["a"]
"abc34".match(/\D/g); // ["a", "b", "c"]
"abc34".match(/[^0-9]/g); // ["a", "b", "c"]
\w
& \W
\w
는 알파벳 대소문자, 숫자, _
(underbar) 중 하나를 검색한다. [a-zA-Z0-9_]
와 동일.
\W
는 알파벳 대소문자, 숫자, _
(underbar)가 아닌 문자 하나를 검색한다. [^a-zA-Z0-9_]
와 동일.
"ab3_@A.Kr".match(/\w/); //["a"]
"ab3_@A.Kr".match(/[a-zA-Z0-9_]/) // ["a"]
"ab3_@A.Kr".match(/\w/g); //["a", "b", "3", "_", "A", "K", "r"]
"ab3_@A.Kr".match(/[a-zA-Z0-9_]/g) // ["a", "b", "3", "_", "A", "K", "r"]
"ab3_@A.Kr".match(/\W/); // ["@"]
"ab3_@A.Kr".match(/[^a-zA-Z0-9_]/); // ["@"]
"ab3_@A.Kr".match(/\W/g); // ["@", "."]
"ab3_@A.Kr".match(/[^a-zA-Z0-9_]/g); // ["@", "."]
()
()
는 그룹으로 묶는다는 의미 이외에도 다른 몇 가지 의미가 더 있다.
그룹화
표현식의 일부를 ()
로 묶어주면 그 안의 내용을 하나로 그룹화할 수 있다.
let co = 'coco';
let cooo = 'cooocooo';
co.match(/co+/); // ["co", index: 0, input: "coco", groups: undefined]
cooo.match(/co+/); // ["cooo", index: 0, input: "cooocooo", groups: undefined]
co.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined]
cooo.match(/(co)+/); // ["co", "co", index: 0, input: "cooocooo", groups: undefined]
co+
는 "c"를 검색하고 +
가 "o"를 1회 이상 연속으로 반복되는 문자를 검색해 주기 때문에 "cooo"가 반환되었다.
하지만 (co)+
는 "c" 와 "o" 를 그룹화하여 "co"를 단위로 1회 이상 반복을 검색하기 때문에 "coco"가 반환되었다. 여기서 특이한 점은 일치하는 문자열로 반환된 결과가 2개다.
캡처
()
로 그룹화한다고 하였고, 이를 캡처한다 라고 한다.
co.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined]
()
로 "co"를 캡처+
가 "co"의 1회 이상 연속 반복을 검색따라서 2번 과정에 의해 "coco" 가 반환되고, 3번에 의해 "co"가 반환되는 것이다.
"2021code".match(/(\d+)(\w)/);
// ["2021c", "2021", "c", index: 0, input: "2021code", groups: undefined]
()
안의 표현식을 순서대로 캡처 ⇒ \d+
와 \w
\d
로 숫자를 검색하되 +
로 1개 이상 연속되는 숫자를 검색 ⇒ 2021
\w
로 문자를 검색 ⇒ c
"2020c"
가 반환(\d+)
로 인해 2021
이 반환(\w)
로 인해 "c"
가 반환문자열 대체 시 캡처된 값 참조
캡처된 값은 replace() 메소드를 사용하여 문자 치환 시 참조 패턴으로 사용될 수 있다.
우선 첫 번째 (\w+)
가 code
를 캡처하고, 두 번째 (\w+)
가 dream
를 캡처한다. (/(\w+)\
와 (\w+)/\
사이의 .
은 .
앞에 역슬래시가 사용되었기 때문에 '임의의 한 문자'가 아닌 기호로서의 온점 . 을 의미한다.)
각 캡처된 값은 첫 번째는 $1
이 참조, 두 번째는 $2
이 참조하기 때문에 이 참조된 값을 "$2.$1"
이 대체하게 되어 code
와 dream
가 뒤바뀐 "dream.code"
가 반환된다.
"code.dream".replace(/(\w+)\.(\w+)/, "$2.$1"); //dream.code
()
를 사용하면 그룹화와 캡처를 한다. 하지만 (?:)
로 사용하면 그룹은 만들지만 캡처는 하지 않는다.
let co = 'coco';
co.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined]
co.match(/(?:co)+/);
// ["coco", index: 0, input: "coco", groups: undefined]
(?=)
는 검색하려는 문자열에 (?=여기)
에 일치하는 문자가 있어야 (?=여기)
앞의 문자열을 반환한다.
"abcde".match(/ab(?=c)/);
// ab 가 c 앞에 있기 때문에 ["ab"] 를 반환한다.
"abcde".match(/ab(?=d)/);
// d 의 앞은 "abc" 이기 때문에 null 을 반환한다.
(?!)
는 (?=)
의 부정이다.
"abcde".match(/ab(?!c)/); // null
"abcde".match(/ab(?!d)/); // ["ab"]