정규표현식(정규식:正規式)
문자열에서 특정한 규칙에 따른 문자열 집합을 표현하기 위해 사용되는 형식 언어
- 특정한 규칙을 갖는 문자열로 이루어진 표현식
- 정규표현식에서의 특수 문자는 각각의 고유한 규칙을 가진다.
- 이러한 규칙을 조합하여 원하는 패턴을 만들고, 특정 문자열에서 해당 패턴과 대응하는 문자를 찾을 수 있다.
const email = 'kimcoding@codestates.com';
let result = '올바릅니다.';
// 1. 정규표현식 사용
let regExp = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
if(regExp.test(email) === false) result = '올바르지 않습니다.';
result; // '올바르지 않습니다.'
-----------------------------------------------------------------------------
// 2. 정규표현식이 아닌 경우, 이메일 아이디가 영문 소문자인지 확인하는 코드
let idx = email.indexOf('@');
if(idx === -1) result = '영문 소문자가 아닙니다.';
let ID = email.slice(0,idx);
ID.split('').forEach(e => {
e = e.charCodeAt(0);
if(e < 97 || e > 122){
result = '영문 소문자가 아닙니다.';
}
});
result; // '올바릅니다.'
let regExp = /^01([0|1|6|7|8|9]?)-?([0-9]{3,4})-?([0-9]{4})$/;
let pattern = /c/;
// 'c 를 찾을 거야!' 라고 컴퓨터에게 명령을 내리는 것
// 찾고 싶은 c를 pattern 이라는 변수에 담아놨기 때문에 이 변수를 이용하여 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]/ 와 동일 |
[^] | []안의 문자열 앞에 ^이 쓰이면, []안에 없는 문자를 검색함 |
문자열
str
이 주어질 때,str
의 길이가 5 또는 7이면서 숫자(0~9)로만 구성되어 있는지를 확인해 주는 함수를 작성하세요. 결과는 Boolean으로 리턴됩니다. 예를 들어str
가c2021
이면false
,20212
이면true
를 리턴합니다.
// 정규표현식 사용
function solution(str) {
return /^\d{5}$|^\d{7}$/.test(str);
}
// 정규표현식 미사용
function solution(str) {
if(str.length === 5 || str.length === 7) {
for(let i = 0; i < str.length; i++) {
if(typeof Number(str[i]) !== 'number') return false;
}
return true;
}
return false;
}
정규표현식을 알아두면 문자열을 다룰 때 코드를 간결하게 줄일 수 있다. 👏
exec()
: execution 원하는 정보를 뽑아내고자 할 때 사용.let pattern = /y/; // 찾고자 하는 문자열
pattern.exec('yeonkr') // 검색하려는 대상을 exec 메소드의 첫 번째 인자로 전달
// 'yeonkr' 가 'y' 를 포함하고 있는지를 확인한다.
// 'y' 가 포함되어 있으므로, ['y'] 를 반환한다.
test()
: 찾고자 하는 문자열이 대상 안에 있는지의 여부를 boolean으로 리턴.let pattern = /y/;
pattern.test('yeonkr');
//'yeonkr'가 'y'를 포함하고 있으므로 true 를 리턴
match()
: RegExp.exec()
와 비슷한 기능을 하며, 정규 표현식을 인자로 받아 주어진 문자열과 일치된 결과를 배열로 반환한다.
let pattern = /y/;
let str = 'yeonkr';
str.match(pattern);
// str 안에 pattern 이 포함되어 있으므로, ['y'] 를 반환
replace()
: '검색 후 바꾸기' 수행. 문자열에서 찾고자 하는 대상을 검색해 이를 치환하려는 문자열로 변경 후 변경된 값을 리턴한다.
let pattern = /y/;
let str = 'yeonkr';
str.replace(pattern, 'Y');
// str 안에서 pattern 을 검색한 후 'Y' 로 변경하여 그 결과를 리턴
// 'Yeonkr' 반환
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
- 정규표현식은 플래그를 설정할 수 있다.
- 플래그 : 추가적인 검색 옵션의 역할을 한다.
- 따로 혹은 함께 사용하는 것이 모두 가능하며, 순서구분X
i
: i
를 붙이면 대소문자를 구분하지 않는다.let withi = /y/i;
let withouti = /y/;
"Yeonkr".match(withi); // ['Y']
"Yeonkr".match(withouti); // null
g
: global의 약자. g
를 붙이면 검색된 모든 결과를 리턴한다.let withg = /y/g;
let withoutg = /y/;
"yjyeonkr".match(withg); // ['y', 'y']
"yjyeonkr".match(withoutg); // ['y'] g 가 없으면 첫 번째 검색 결과만 반환
m
: m
을 붙이면 다중행을 검색한다.let str = `1st : yj
2nd : yeon
3rd : kr`;
str.match(/y/gm)
// 3개의 행을 검색하여 모든 y 를 반환
// ['y', 'y']
str.match(/y/m)
// m은 다중행을 검색하게 해 주지만, g 를 빼고 검색하면 검색 대상을 찾는 순간 검색을 멈추기 때문에
// 첫 행의 ['y'] 만 리턴한다.
1. Anchors : ^
and $
^
: 문자열의 처음을 뜻함 ^
뒤에 붙은 단어로 시작하는 부분을 찾는다."coding is fun".match(/^co/); // ['co']
"coding is fun".match(/^fun/); // null
$
: 문자열의 끝을 뜻함$
앞의 표현식으로 끝나는 부분을 찾는다."coding is fun".match(/un$/); // ['un']
"coding is fun".match(/is$/); // null
"coding is fun".match(/^coding is fun$/);
// 문자열을 ^ 와 $ 로 감싸주면 그 사이에 들어간 문자열과 정확하게 일치하는 부분을 찾는다.
// ["coding is fun"]
2. 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"]
{}
: 위의 확장판. 직접 숫자를 넣어서 연속되는 개수를 설정할 수 있다. "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"]
3. OR operator
|
: 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"] 를 반환
4. Bracket Operator - []
[]
안에 명시된 값을 검색한다.[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"]
아 ...
씨를 뿌리는 과정이다 이건 ^^
5. Character classes
\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); // ["@", "."]
6. 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+
는 "c"를 검색하고 +
가 "o"를 1회 이상 연속으로 반복되는 문자를 검색해주므로 "cooo"가 반환되었다.(co)+
는 "c", "o"를 그룹화하여 "co"를 단위로 1회 이상 반복을 검색하므로 "coco"가 반환되었다.캡처 : ()
로 그룹화한다 === 캡처한다.
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"
반환7. 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]
// 위 "캡처" 예시의 결괏값과 비교해보기.
8. lookahead
(?=)
: 검색하려는 문자열에 (?=여기)
에 일치하는 문자가 있어야 (?=여기)
앞의 문자열을 반환한다."abcde".match(/ab(?=c)/);
// ab 가 c 앞에 있기 때문에 ["ab"] 를 반환
"abcde".match(/ab(?=d)/);
// d 의 앞은 "abc" 이기 때문에 null을 반환
9. negated lookahead
(?!)
: (?=)
의 부정"abcde".match(/ab(?!c)/); // null
"abcde".match(/ab(?!d)/); // ["ab"]
정규표현식은 유효성 검사 뿐만 아니라 데이터 스크래핑, 문자열 파싱 등과 같은 다양한 상황에서 사용 가능. 알고리즘 문제에서 다양한 해결 아이디어도 얻을 수 있다.