[JavaScript] 4-7. RegExp

RegExp

정규 표현식은 문자열에 나타는 특정 문자 조합과 대응시키기 위해 사용되는 패턴입니다.
자바스크립트에서, 정규 표현식 또한 객체입니다.
이 패턴들은 RegExp 의 exec 메소드와 test 메소드,
그리고 String 의 match 메소드, replace 메소드, search 메소드, split 메소드와 함께 쓰입니다.


정규 표현식 만들기

정규식을 만드는 방법에는 두 가지가 있습니다.
정규식 리터럴(슬래쉬”/”로 감싸는 패턴)을 사용하는 방법은 다음과 같습니다.

var re = /ab+c/;

정규식 리터럴은 스크립트가 불러와질 때 컴파일됩니다.
만약 정규식이 상수라면, 이렇게 사용하는 것이 성능을 향상시킬 수 있습니다.

다른 방법으로는, RegExp 객체의 생성자 함수를 호출하는 방법도 있습니다:

var re = new RegExp("ab+c");

생성자 함수를 사용하면 정규식이 실행 시점에 컴파일됩니다.
정규식의 패턴이 변경될 수 있는 경우, 혹은 사용자 입력과 같이 다른 출처로부터 패턴을 가져와야 하는 경우에는 생성자 함수를 사용하세요.


정규식 패턴 작성하기

정규식 패턴은 /abc/ 같이 단순 문자로 구성될 수도 있고,
/abc/ 또는 /Chapter (\d+).\d/와 같이 단순 문자와 특수 문자의 조합으로 구성될 수도 있습니다.
마지막 예제는 기억장치처럼 쓰이는 괄호를 포함하고 있습니다.
패턴화된 부분 문자열 일치 사용하기에서 설명하는것 처럼 패턴에서 괄호를 포함한 부분은 나중에 사용하기 위하여 저장됩니다.

단순 패턴은 문자열을 있는 그대로 대응시키고자 할 때 사용됩니다.
예를 들어, /abc/라는 패턴은 문자열에서 정확히 ‘abc’ 라는 문자들이 모두 함께 순서대로 나타나야 대응됩니다.
위의 패턴은 “Hi, do you know your abc’s?” 와
“The latest airplane designs evolved from slabcraft.” 두가지 예에서 부분 문자열 ‘abc’에 대응될 것입니다.
‘Grab crab’ 이라는 문자열에서 ‘ab c’ 라는 부분 문자열을 포함하고 있지만, ‘abc’를 정확하게 포함하고 있지 않기 때문에 대응되지 않습니다.

검색에서 하나 이상의 b 들을 찾거나, 혹은 공백을 찾는 것과 같이
‘있는 그대로의 대응’ 이상의 대응을 필요로 할 경우, 패턴에 특수한 문자를 포함시킵니다.
예를 들어, /abc/ 패턴은 ‘a’ 문자 뒤에 0 개 이상의 ‘b’ 문자( 문자는 바로 앞의 문자가 0 개 이상이라는 것을 의미합니다)가 나타나고
바로 뒤에 ‘c’ 문자가 나타나는 문자 조합에 대응됩니다.
문자열 “cbbabbbbcdebc,” 에서 위의 패턴은 부분 문자열 ‘abbbbc’ 와 대응됩니다.


정규식에서의 특수문자

특수문자 의미
\ 특수 문자가 아닌 문자(non-special character) 앞에서 사용된 백슬래시는 ‘해당 문자는 특별하고,
문자 그대로 해석되면 안된다’는 사실을 가리킵니다.
예를 들어, 앞에 \가 없는 ‘b’는 보통 소문자 b 가 나오는 패턴과 대응됩니다.
그러나 ‘\b’ 자체는 어떤 문자와도 대응되지 않습니다;
이 문자는 특별한 단어 경계 문자를 형성합니다. 특수 문자 앞에 위치한 백슬래시는 ‘다음에 나오는 문자는 특별하지않고,
문자 그대로 해석되어야 한다’는 사실을 가리킵니다.
예를 들어, 패턴 /a/ 에서의 특수문자 ‘‘는 0 개 이상의 ‘a’ 문자가 등장함을 나타냅니다.
이와는 다르게, 패턴 /a*/ 는 ‘‘이 특별하지 않다는 것을 나타내며, ‘a‘와 같은 문자열과 대응될 수 있습니다.
RegExp(“pattern”) 표기를 쓰면서 \ 자체를 이스케이프 하는 것을 잊지 마세요.
왜냐하면 \ 는 문자열에서도 이스케이프 문자이기 때문입니다.
(역주: /a*/ 와 같은 패턴을 생성자로 만들려면
new RegExp(‘a\*‘)와 같이 백슬래시 자체를 이스케이프 시켜주어야 합니다.)
\d 숫자 문자에 대응됩니다. [0-9]와 동일합니다.
예를 들어, /\d/ 또는 /[0-9]/는 “B2 is the suite number.”에서 ‘2’에 대응됩니다.
\D 숫자 문자가 아닌 문자에 대응됩니다. [^0-9]와 동일합니다.
예를 들어, /\D/ 또는 /[^0-9]/는 “B2 is the suite number.”의 ‘B’에 대응됩니다.
^ 입력의 시작 부분에 대응됩니다. 만약 다중행 플래그가 참으로 설정되어 있다면, 줄 바꿈 문자 바로 다음 부분과도 대응됩니다.
예를 들어, /^A/ 는 “an A” 의 ‘A’와는 대응되지 않습니다, 그러나 “An E” 의 ‘A’와는 대응됩니다.
’^’ 가 문자셋([abc]) 패턴의 첫 글자로 쓰인다면, 그 때는 전혀 다른 의미를 가집니다.
자세한 내용은 역 문자셋을 참고하세요.
$ 입력의 끝 부분과 대응됩니다. 만약 다중행 플래그가 참으로 설정되어 있다면, 줄 바꿈 문자의 바로 앞 부분과도 대응됩니다.
예를 들어, /t$/ 는 “eater” 의 ‘t’에는 대응되지 않습니다, 그러나 “eat” 과는 대응됩니다.
* 앞의 표현식이 0 회 이상 연속으로 반복되는 부분과 대응됩니다. {0,} 와 같은 의미입니다.
예를 들어, /bo*/ 는 “A ghost booooed” 의 ‘boooo’ 와 대응되고, “A bird warbled” 의 ‘b’에 대응되지만 “A goat grunted” 내의 어느 부분과도 대응되지 않습니다.
+ 앞의 표현식이 1 회 이상 연속으로 반복되는 부분과 대응됩니다. {1,} 와 같은 의미입니다.
예를 들어, /a+/ 는 “candy”의 ‘a’에 대응되고 “caaaaaaandy” 의 모든 ‘a’들에 대응되지만, “cndy” 내의 어느 부분과도 대응되지 않습니다.
? 앞의 표현식이 0 또는 1 회 등장하는 부분과 대응됩니다. {0,1} 와 같은 의미입니다.
예를 들어, /e?le?/ 는 “angel”의 ‘el’ 에 대응되고, “angle”의 ‘le’ 에 대응되고 또한 “oslo” 의 ‘l’에도 대응됩니다.
만약 수량자 *, +, ?, {} 바로 뒤에 사용하면,
기본적으로 탐욕스럽던(가능한 한 많이 대응시킴) 수량자를 탐욕스럽지 않게(가능한 가장 적은 문자들에 대응시킴) 만듭니다.
예를 들어, /\d+/를 “123abc”에 적용시키면 “123”과 대응됩니다.
그러나 /\d+?/를 같은 문자열에 적용시키면 오직 “1”과만 대응됩니다.
. 개행 문자를 제외한 모든 단일 문자와 대응됩니다.
예를 들어, /.n/는 “nay, an apple is on the tree”에서 ‘an’과 ‘on’에 대응되지만, ‘nay’ 에는 대응되지 않습니다.
(x) 다음의 예제가 보여주는것 처럼 ‘x’에 대응되고, 그것을 기억합니다.
괄호는 포획 괄호(capturing parentheses)라 불립니다.
패턴 /(foo) (bar) \1 \2/ 안의 ‘(foo)’ 와 ‘(bar)’는
문자열”foo bar foo bar”에서 처음의 두 단어에 대응되고 이를 기억합니다.
패턴 내부의 \1 와 \2 는 문자열의 마지막 두 단어에 대응됩니다.
(역주: \n 패턴은 앞의 n 번째 포획괄호에 대응된 문자열과 똑같은 문자열에 대응됩니다.)
\1, \2, \n 과 같은 문법은 정규식의 패턴 부분에서 사용됩니다.
정규식의 치환 부분에서는 $1, $2, $n 과 같은 문법이 사용되어야 합니다.
예를 들어, ‘bar foo’.replace( /(…) (…)/, ‘$2 $1’)와 같이 사용되어야 합니다.
$& 패턴은 앞에서 대응된 전체 문자열을 가리킵니다.
x|y ‘x’ 또는 ‘y’에 대응됩니다.
예를 들어, /green|red/는 “green apple”의 ‘green’에 대응되고, “red apple.”의 ‘red’에 대응됩니다.( || 과 같은 기능)
{n} 앞 표현식이 n 번 나타나는 부분에 대응됩니다. N 은 반드시 양의 정수여야 합니다.
예를 들어, /a{2}/는 “candy,”의 ‘a’에는 대응되지 않지만, “caandy,”의 모든 a 와, “caaandy.”의 첫 두 a 에는 대응됩니다.
{n,m} n 과 m 은 양의 정수이고, n <= m 를 만족해야 합니다. 앞 문자가 최소 n 개, 최대 m 개가 나타나는 부분에 대응됩니다.
m 이 생략된다면, m 은 ∞ 로 취급됩니다.
예를 들어, /a{1,3}/는 “cndy”에서 아무것에도 대응되지 않지만,
“caandy,”의 첫 두 a 와 “caaaaaaandy”의 첫 세 a 에 대응됩니다.
“caaaaaaandy”에서 더 많은 a 들이 있지만, “aaa”에만 대응된다는 점에 주목하세요.
또한 이 문자는 x(?=y) 와 x(?!y) 항목에서 설명하는 바와 같이 사전 검증(lookahead assertion)을 위해서도 쓰입니다.
[xyz] 문자셋(Character set) 입니다.
이 패턴 타입은 괄호 안의 어떤 문자(이스케이프 시퀀스까지 포함)와도 대응됩니다.
점(.) 이나 별표 (*) 같은 특수 문자는 문자셋 내부에서는 특수 문자가 아닙니다.
따라서 이스케이프시킬 필요가 없습니다. 하이픈을 이용하여 문자의 범위를 지정해줄 수 있습니다.
예를 들어, 패턴 [a-d] 는 패턴 [abcd] 와 똑같이 동작하며,
“brisket”의 ‘b’ 에 일치하고, “city”의 ‘c’ 에 일치합니다.
패턴 /[a-z.]+/ 와 /[\w.]+/ 는 test.i.ng 전체 문자열이 일치합니다.
[^xyz] 부정 문자셋(negated character set) 또는 보충 문자셋(complemented character set)입니다.
괄호 내부에 등장하지 않는 어떤 문자와도 대응됩니다.
하이픈을 이용하여 문자의 범위를 지정할 수 있습니다.
일반적인 문자셋에서 작동하는 모든 것은 여기에서도 작동합니다.
예를 들어, 패턴[^abc]는 패턴[^a-c]와 동일합니다.
두 패턴은 “brisket”의 ‘r’, “chop.”의 ‘h’ 에 대응됩니다.
\b 단어 경계에 대응됩니다.
단어 경계는 다른 ‘단어 문자’가 앞이나 뒤에 등장하지 않는 위치에 대응됩니다.
단어의 경계는 대응 결과에 포함되지 않는다는 사실에 주의하세요.
다른 말로는, 단어의 경계에 대응되는 문자열의 길이는 항상 0 입니다.
(패턴 [\b]와 혼동하지 마세요.)
예제: /\bm/는 “moon”의 ‘m’에 대응됩니다;
/oo\b/ 는 “moon”의 ‘oo’ 부분에 대응되지 않는데, 왜냐하면 ‘oo’를 뒤따라오는 ‘n’이 단어 문자이기 때문입니다;
/oon\b/는 “moon”의 ‘oon’에 대응됩니다.
왜냐하면, ‘oon’은 문자열의 끝이라서, 뒤따라오는 단어 문자가 없기 때문입니다 ;
/\w\b\w/는 어떤 것에도 일치하지 않습니다.
왜냐하면, 단어 문자는 절대로 비 단어 문자와 단어 문자 두개가 뒤따라올수 없기 때문입니다.
\B 단어 경계가 아닌 부분에 대응됩니다. 아래와 같은 경우들이 있습니다:
문자열의 첫 번째 문자가 단어 문자가 아닌 경우, 해당 문자의 앞 부분에 대응됩니다.
문자열의 마지막 문자가 단어 문자가 아닌 경우, 해당 문자의 뒷 부분에 대응됩니다.
두 단어 문자의 사이에 대응됩니다.
단어 문자가 아닌 두 문자 사이에 대응됩니다.
빈 문자열에 대응됩니다.
문자열의 시작 부분과 끝 부분은 단어가 아닌 것으로 간주됩니다.
예를 들어, /\B../ 는 “noonday”의 ‘oo’와 대응되며, /y\B./ 는 “possibly yesterday.”의 ‘ye’와 대응됩니다.
\s 스페이스, 탭, 폼피드, 줄 바꿈 문자등을 포함한 하나의 공백 문자에 대응됩니다.
[\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff].와 동일합니다.
예를 들어, /\s\w*/는 “foo bar.”의 ‘ bar’에 대응됩니다.
\S 공백 문자가 아닌 하나의 문자에 대응됩니다.
[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]. 와 동일합니다.
예를 들어, /\S\w*/는 “foo bar.”의 ‘foo’ 에 대응됩니다.
\w 밑줄 문자를 포함한 영숫자 문자에 대응됩니다.
[A-Za-z0-9_] 와 동일합니다. (역주: 여기에 대응되는 문자를 단어 문자라고 합니다.)
예를 들어, /\w/는 “apple,”의 ‘a’ 에 대응되고, “$5.28,”의 ‘5’에 대응되고,”3D.”의 ‘3’에 대응됩니다.
\W 단어 문자가 아닌 문자에 대응됩니다. [^a-za-z0-9_] 와 동일합니다.
예를 들어, /\W/ 또는 /[^a-za-z0-9_]/는 “50%.”의 ‘%’ 에 대응됩니다.
\n 정규식 내부의 n 번째 괄호에서 대응된 부분에 대한 역참조 입니다. 여기서, n 은 양의 정수입니다.
예를 들어, /apple(,)\sorange\1/는 “apple, orange, cherry, peach.”의 ‘apple, orange,’ 에 일치합니다.


정규식 사용 예제

// 문자열 래퍼 객체(String)의
// match, search, split, replace 메소드가 정규 표현식을 지원
"Hi, do you know your abc's?".match(/abc/);
"Hi, do you know your abc's?".search(/abc/);
"Hi, do you know your abc's?".split(/abc/);
"Hi, do you know your abc's?".replace(/abc/, "ABC");
"1234 abbbbbc 1234".match(/ab*c/);
"1234 abbbbbc 1234".search(/ab*c/);
"1234 abbbbbc 1234".split(/ab*c/);
"1234 abbbbbc 1234".replace(/ab*c/, "{$&}");

// 숫자 문자와 일치하는 패턴: \d
"Hello World".match(/\d/); // null
"Hello World 1234".match(/\d/); // [...]
"Hello*World".match(/\*/);

"an A".match(/^A/);
"An B".match(/^A/);

"eater".match(/t$/);
"eat".match(/t$/);

"ac".match(/ab*c/);
"ac".match(/ab+c/);
"abbbbbbbc".match(/ab+c/);
"abbbbbc".match(/ab?c/);

"123abc".match(/\d+?/);

// 여는 꺾쇠 괄호 뒤에 1개 이상의 문자가 오고,
// 그 다음에 닫는 꺾쇠 괄호가 나오는 패턴
// 괄호 사이에 들어오는 문자는 무엇이든 상관없지만,
// 가능한 적게 찾아라
"<hello>world <java>script".match(/\<.+?\>/);

"foo foo".match(/(foo) \1/);
"foooo foo".match(/(fo+) \1/); // null
"foooo foooo".match(/(fo+) \1/); // [...]

"foo bar foo bar".match(/(foo) (bar) \1 \2/);

"foo bar".match(/(foo) (bar)/);

"ksh@fastcampus.co.kr".match(/(.+?)@(.+)/);
"ksh@fastcampus.co.kr".replace(/(.+?)@(.+)/, "아이디: $1, 도메인: $2");

"1234 abbbbbc 1234".replace(/ab*c/, "{$&}");

"foofoofoo".match(/(foo)?/);
"foofoofoo".match(/(foo)+/);

// 포획 괄호: 부분 표현식을 하나의 단위로 취급하는 기능
//          + 대응된 문자열을 기억하는 기능
"foofoofoo".match(/(foo)*/);

// 비포획 괄호: 부분 표현식을 하나의 단위로 취급하는 기능
"foofoofoo".match(/(?:foo)*/);

"caaaandy".match(/a{1,3}/);
"caaaandy".match(/a{1,3}?/);

// 같은 의미
"caaaandy".match(/a+/);
"caaaandy".match(/a{1,}/);

"caaaandy".match(/a*/);
"caaaandy".match(/a{0,}/);

"caaaandy".match(/a?/);
"caaaandy".match(/a{0,1}/);

// 문자셋
// a 또는 b 또는 c와 일치하는 하나의 문자와 대응되는 패턴
"abcdef".match(/[abc]/);
"abcabcbacbacbcabcabcabdef".match(/[abc]+/);

"hello*****world".match(/\*+/);
"hello*****world".match(/[*]+/);

"hello*.!'***...!!!world".match(/[*.!']+/);

// 연속된 소문자 알파벳 찾아내기
"hello world".match(/[abcdefghijklmnopqrstuvwxyz]+/);
"hello world".match(/[a-z]+/);

"hello WORLD".match(/[A-Z]+/);
"hello 안녕하세요".match(/[가-힣]+/);
"hello 88881235132".match(/[0-9]+/);

"hello WORLD".match(/[^a-z]+/);

"moon ".match(/oon\b/);

// 공백 모두 제거하기
"hello    world   java    script".replace(/\s/g, "");

"hello    world    java   script".match(/\w+/g);

"HELLO WORLD".match(/[a-z]+/i);

// m 플래그를 붙이지 않았을 때
// ^ : 문자열의 첫 부분에 대응
`Hello
World
Java
Script`.match(/^\w+/g);

// m 플래그를 붙였을 때
// ^ : 줄의 첫 부분에 대응
`Hello
World
Java
Script`.match(/^\w+/gm);

const input = "010-1010-5678";

input.match(/^01[016789]-\d{3,4}-\d{4}$/);


정규식에서 쓰이는 메소드


플래그를 사용한 고급검색


꿀팁 - 정규식을 작성이 힘들 때 참고할만한 사이트

https://regexr.com/