정규표현식
정규표현식
문자열에서 특정한 문자를 찾아내는 도구.
- 이메일 유효성 검사
let regExp = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
- 핸드폰 번호 유효성 검사
let regExp = /^01([0|1|6|7|8|9]?)-?([0-9]{3,4})-?([0-9]{4})$/;
- 문자열의 길이가 5 또는 7이면서 숫자로만 구성되어 있는지 검사
function solution(str) {
return /^\d{5}$|^\d{7}$/.test(str);
}
정규표현식 사용방법
- 리터럴 패턴
정규표현식 규칙을
/
로 감싸 사용한다.//
안에 들어온 문자열이 찾고자 하는 문자열.let pattern =/c/; // c를 찾기 위한 명령어
- 생성자 함수 호출 패턴
let pattern = new RegExp('c') // new를 이용해서 정규표현식 객체 생성
정규표현식 내장 메소드
RegExp 객체 메소드
- exec()
원하는 정보를 뽑아내고자 할 때 사용. 검색의 대상이 찾고자 하는 문자열에 대한 정보를 가지고 있다면 이를 배열로 반환하며, 찾는 문자열이 없다면 null을 반환
let pattern = /c/; // 찾고자 하는 문자열 pattern.exec('codestates') // 검색하려는 대상을 exec 메소드의 첫 번째 인자로 전달 // 'codestates' 가 'c' 를 포함하고 있는지를 확인 // 이 경우 'c' 가 포함되어 있으므로, ['c'] 를 반환
- test()
찾고자 하는 문자열이 대상 안에 있는지의 여부를 boolean 으로 리턴
let pattern = /c/; pattern.test('codestates'); // 이 경우는 'codestates'가 'c'를 포함하고 있으므로 true 리턴
String 객체 메소드
- match()
RegExp.exec() 와 비슷한 기능. 정규 표현식을 인자로 받아 주어진 문자열과 일치된 결과를 배열로 반환. 일치되는 결과가 없으면 null 리턴
let pattern = /c/; let str = 'codestates'; str.match(pattern); // str 안에 pattern 이 포함되어 있으므로, ['c'] 를 반환
- replace()
‘검색 후 바꾸기’ 수행. 첫 번째 인자로는 정규표현식을 받고, 두 번째 인자로는 치환하려는 문자열을 받는다. 문자열에서 찾고자 하는 대상을 검색해서 이를 치환하려는 문자열로 변경 후 변경된 값을 리턴.
let pattern = /c/; let str = 'codestates'; str.replace(pattern, 'C'); // str 안에서 pattern 을 검색한 후 'C' 로 변경하여 그 결과를 리턴 // 여기서는 'Codestates'가 반환된다.
- split()
주어진 인자를 구분자로 삼아, 문자열을 부분 문자열로 나누어 그 결과를 배열로 반환
"123,456,789".split(",") // ["123", "456", "789"] "12304560789".split("0") // ["123", "456", "789"]
- search()
정규표현식을 인자로 받아 가장 처음 매칭되는 부분 문자열의 위치를 반환. 매칭되는 문자열이 없으면 -1을 반환한다.
"JavaScript".search(/script/); // -1 대소문자 구분 "JavaScript".search(/Script/); // 4 "codestates".search(/ode/); // 1
Flag
정규표현식은 플래그를 설정해 줄 수 있으며, 플래그는 추가적인 검색 옵션의 역할을 한다. 플래그들은 각자 혹은 함께 사용하는 것이 모두 가능하며, 순서에 구분이 없다.
- i
i를 붙이면 대소문자를 구분하지 않는다.
let withi = /c/i; let withouti = /c/; "Codestates".match(withi); // ['C'] "Codestates".match(withouti); // null
- g
검색된 모든 결과 리턴
let withg = /c/g; let withoutg = /c/; "coolcodestates".match(withg); // ['c', 'c'] "coolcodestates".match(withoutg); // ['c'] g 가 없으면 첫 번째 검색 결과만 반환
- m
다중행 검색
let str = `1st : cool 2nd : code 3rd : states`; str.match(/c/gm) // 3개의 행을 검색하여 모든 c 를 반환합니다. // ['c', 'c'] str.match(/c/m) // m은 다중행을 검색하게 해 주지만, g 를 빼고 검색하면 검색 대상을 찾는 순간 검색을 멈추기 때문에 첫 행의 ['c'] 만 리턴
정규식 패턴
정규식 패턴 | 설명 |
---|---|
^ | 줄(Line)의 시작에서 일치 /^abc/ |
$ | 줄(Line)의 끝에서 일치 /xyz$/ |
. | (특수기호, 띄어쓰기를 포함한) 임의의 한 문자 |
a|b |
a or b 와 일치, 인덱스가 작은 것을 우선 반환 |
* | 0회 이상 연속으로 반복되는 문자와 가능한 많이 일치. {0,} 와 동일 |
*? | 0회 이상 연속으로 반복되는 문자와 가능한 적게 일치. {0} 와 동일 |
+ | 1회 이상 연속으로 반복되는 문자와 가능한 많이 일치. {1,} 와 동일 |
+? | 1회 이상 연속으로 반복되는 문자와 가능한 적게 일치. {1} 와 동일 |
{3} | 숫자 3개 연속 일치 |
{3,} | 3개 이상 연속 일치 |
{3, 5} | 3개 이상 5개 이하 연속 일치 |
() | 캡쳐(capture)할 그룹 |
[a-z] |
a부터 z 사이의 문자 구간에 일치(영어 소문자) |
[A-Z] |
A부터 Z 사이의 문자 구간에 일치(영어 대문자) |
[0-9] |
0부터 9 사이의 문자 구간에 일치(숫자) |
\(역슬래쉬) |
escape 문자. 특수 기호 앞에 \를 붙이면 정규식 패턴이 아닌, 기호 자체로 인식 |
\d | 숫자를 검색함. /[0-9]/와 동일 |
\D | 숫자가 아닌 문자를 검색함. /[^0-9]/와 동일 |
\w | 영어대소문자, 숫자, (underscore)를 검색함. /[A-Za-z0-9]/ 와 동일 |
\W | 영어대소문자, 숫자, (underscore)가 아닌 문자를 검색함. /[^A-Za-z0-9]/ 와 동일 |
[^] |
[]안의 문자열 앞에 ^이 쓰이면, []안에 없는 문자를 검색함 |
Anchors - ^ and $
-
^
문자열의 처음을 의미하며, 문자열에서 ^뒤에 붙은 단어로 시작하는 부분을 찾는다. 일치하는 부분이 있더라도, 그 부분이 문자열의 시작 부분이 아니면 null 리턴."coding is fun".match(/^co/); // ['co'] "coding is fun".match(/^fun/); // null
-
$
문자열의 끝을 의미하며, 문자열에서 $앞의 표현식으로 끝나는 부분을 찾는다. ^와 비슷하지만 ^는 문자열의 시작을 찾는 반면, $는 문자열의 마지막 부분을 찾는다. 일치하는 부분이 있더라도, 그 부분이 문자열의 끝부분이 아니면 null 리턴."coding is fun".match(/un$/); // ['un'] "coding is fun".match(/is$/); // null "coding is fun".match(/^coding is fun$/); // 문자열을 ^ 와 $ 로 감싸주면 그 사이에 들어간 문자열과 정확하게 일치하는 부분을 찾는다. // ["coding is fun"]
Quantifiers — *, +, ? and {}
-
*
*
바로 앞의 문자가 0번 이상 나타나는 경우를 검색. 아래와 같은 문자열이 있을 때에 /ode*/g 을 사용하게 되면 “od” 가 들어가면서 그 뒤에 “e”가 0번 이상 포함된 모든 문자열을 리턴."co cod code codee coding codeeeeee codingding".match(/ode*/g); // ["od", "ode", "odee", "od", "odeeeeee", "od"]
-
+
*
와 같은 방식으로 작동하며, 다만 + 바로 앞의 문자가 1번 이상 나타나는 경우를 검색한다는 점이*
과 다르다."co cod code codee coding codeeeeee codingding".match(/ode+/g); // ["ode", "odee", "odeeeeee"]
-
?
*
또는+
와 비슷하지만, ? 앞의 문자가 0번 혹은 1번 나타나는 경우만 검색. *? 또는 +? 와 같이 ?는 * 혹은 + 와 함께 쓰는 것도 가능.
"co cod code codee coding codeeeeee codingding".match(/ode?/g);
// ["od", "ode", "ode", "od", "ode", "od"]
"co cod code codee coding codeeeeee codingding".match(/ode*?/g);
// ["od", "od", "od", "od", "od", "od"]
"co cod code codee coding codeeeeee codingding".match(/ode+?/g);
// ["ode", "ode", "ode"]
-
{}
{}는 *, *?, +, +? 의 확장판으로 생각할 수 있다. *, *?, +, +? 가 ‘0개 이상’ 또는 ‘1개 이상’ 검색이 전부였던 반면, {}는 직접 숫자를 넣어서 연속되는 개수를 설정할 수 있다.
"co cod code codee coding codeeeeee codingding".match(/ode{2}/g);
// 2개의 "e"를 포함한 문자열을 검색.
// ["odee", "odee"]
"co cod code codee coding codeeeeee codingding".match(/ode{2,}/g);
// 2개 이상의 "e"를 포함한 문자열을 검색.
// ["odee", "odeeeeee"]
"co cod code codee coding codeeeeee codingding".match(/ode{2,5}/g);
// 2개 이상 5개 이하의 "e"를 포함한 문자열을 검색.
// ["odee", "odeeeee"]
Or
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"]
Character classes
-
\d
,\D
"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
"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); // ["@", "."]
Grouping and capturing
-
()
- 그룹화
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.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined] // 1. () 로 "co"를 캡처 // 2. 캡처한 "co" 는 일단 당장 사용하지 않고, + 가 "co"의 1회 이상 연속 반복을 검색 // 3. 이렇게 캡처 이외 표현식이 모두 작동하고 나면, 캡처해 두었던 "co"를 검색 // 따라서 2번 과정에 의해 "coco" 가 반환되고, 3번에 의해 "co"가 반환되는 것입니다.
"2021code".match(/(\d+)(\w)/); // ["2021c", "2021", "c", index: 0, input: "2021code", groups: undefined] // 1. () 안의 표현식을 순서대로 캡처 ⇒ \d+ 와 \w // 2. 캡처 후 남은 표현식으로 검색 ⇒ 이번 예시에는 남은 표현식은 없습니다. // 3. \d 로 숫자를 검색하되 + 로 1개 이상 연속되는 숫자를 검색 ⇒ 2021 // 4. \w 로 문자를 검색 ⇒ c // 5. 3번과 4번이 조합되어 "2020c" 가 반환 // 6. 첫 번째 캡처한 (\d+) 로 인해 2021 이 반환 // 7. 두 번째 캡처한 (\w) 로 인해 "c" 가 반환
"code.states".replace(/(\w+)\.(\w+)/, "$2.$1"); //states.code // 캡처된 값은 replace() 메소드를 사용하여 문자 치환 시 참조 패턴으로 사용될 수 있다. 우선 첫 번째 (\w+) 가 code 를 캡처하고, 두 번째 (\w+) 가 states 를 캡처한다. (/(\w+)\ 와 (\w+)/\사이의 . 은 . 앞에 역슬래시가 사용되었기 때문에 '임의의 한 문자'가 아닌 기호로서의 온점 . 을 의미한다.) 각 캡처된 값은 첫 번째는 $1 이 참조, 두 번째는 $2 이 참조하기 때문에 이 참조된 값을 "$2.$1" 이 대체하게 되어 code 와 states 가 뒤바뀐 "states.code" 가 반환된다.
non-capturing
(?:)로 사용하면 그룹은 만들지만 캡처는 하지 않는다.
let co = 'coco';
co.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined]
co.match(/(?:co)+/);
// ["coco", index: 0, input: "coco", groups: undefined]
lookahead
(?=) 는 검색하려는 문자열에 (?=여기) 에 일치하는 문자가 있어야 (?=여기) 앞의 문자열을 반환한다.
"abcde".match(/ab(?=c)/);
// ab 가 c 앞에 있기 때문에 ["ab"] 를 반환.
"abcde".match(/ab(?=d)/);
// d 의 앞은 "abc" 이기 때문에 null 을 반환.
negated lookahead
(?!) 는 (?=) 의 부정
"abcde".match(/ab(?!c)/); // null
"abcde".match(/ab(?!d)/); // ["ab"]