정규표현식

YoungJoon Suh·2022년 4월 5일

문자열 관리를 간단하게 하는 방법으로 정규표현식이 있습니다. (회원 가입이 필요한 서비스에서 비밀번호 설정시 특정 조건에 맞추어 입력하라고 요구할 때)
정규표현식: 문자열에서 특정한 문자를 찾아내는 도구, 이를 이용하면 수십 중이 필요한 코딩작업을 간단하게 한 두줄로 끝낼 수 있습니다.
RegExp 객체의 메소드
exec(): execution의 줄임말로, 원하는 정보를 뽑아내고자 할 때 사용합니다. 검색의 대상이 찾고자 하는 문자열에 대한 정보를 가지고 있다면 이를 배열로 반환하며, 찾는 문자열이 없다면 null을 반환합니다.
let pattern = /c/; // 찾고자 하는 문자열
pattern.exec('codestates'); // 검색하려는 대상을 exec 메소드의 첫 번째 인자로 전달합니다. 'c'가 포함되어 있으므로 ['c']를 반환합니다.
test(): 찾고자 하는 문자열이 대상 안에 있는지의 여부를 boolean으로 리턴합니다.
pattern.test('codestates'); // true
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'); // 'Codestates'가 반환됩니다.
split(): 주어진 인자를 구분자로 삼아, 문자열을 부분 문자열로 나누어 그 결과를 배열로 반환합니다.
"123,456,789".split(",") // ["123", "456", "789"]
search(): 정규표현식을 인자로 받아 가장 처음 매칭되는 부분 문자열의 위치를 반환합니다. 매칭되는 문자열이 없으면 -1을 반환합니다.
"JavaScript".search(/script/); // -1
flag: 정규 표현식은 플래그를 설정해 줄 수 있으며, 플래그는 추가적인 검색옵션의 역할을 해 줍니다. 이 플래그들은 각자 혹은 함께 사용하는 것이 모두 가능하며, 순서에 구분이 없습니다. 아래는 자주 사용되는 3가지 플래그입니다.
i를 붙이면 대소문자를 구분하지 않습니다.
let withi = /c/i;
let withouti = /c/;
"Codestates".match(withi); // ['C']
"Codestates'.match(withouti); // null
g: global의 약자로, g를 붙이면 검색된 모든 결과를 리턴합니다.
let withg = /c/g;
let withoutg = /c/;
"coolcodestates".match(withg); // ['c', 'c']
"coolcodestates".match(withoutg); // ['c'] g가 없으면 첫 번째 검색 결과만 반환합니다.
m: 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: 줄(Line)의 끝에서 일치 /xyz/
.: (특수기호, 띄어쓰기를 포함한) 임의의 한 문자
a|b: a or b 와 일치, 인덱스가 작은 것을 우선 반환
: 0회 이상 연속으로 반복되는 문자와 가능한 많이 일치. {0,}와 동일
?: 0회 이상 연속으로 반복되는 문자와 가능한 적게 일치. {0}와 동일
+: 1회 이상 연속으로 반복되는 문자와 가능한 많이 일치. {1,}와 동일
+?: 1회 이상 연속으로 반복되는 문자와 가능한 적게 일치. {1}와 동일
{3}: 숫자 3개 연속 일치
{3,}: 3개 이상 연속 일치
{3, 5}: 3개 이상 5개 이하 연속 일치
(역슬래시): escape문자. 특수 기호 앞에 \를 붙이면 정규식 패턴이 아닌, 기호 자체로 인식.
\d: 숫자를 검색함. /[0-9]/와 동일
\D: 숫자가 아닌 문자를 검색함. /[^0-9]/와 동일
\w: 영어대소문자, 숫자, (underscore)를 검색함. /[A-Za-z0-9_]/ 와 동일
\W: 영어대소문자, 숫자, (underscore)가 아닌 문자를 검색함. /[^A-Za-z0-9_]/ 와 동일
[^]: []안의 문자열 앞에 ^이 쓰이면, []안에 없는 문자를 검색함.
문자열을 ^와 로감싸주면그사이에들어간문자열과정확하게일치하는부분을찾습니다.∗는∗바로앞의문자가0번이상나타나는경우를검색합니다./ode∗/g+는+바로앞의문자가1번이상나타나는경우를검색합니다./ode+/g?는?앞의문자가0번혹은1번나타나는경우만검색합니다./ode?/g∗?:바로앞의문자가0번나타나는경우만검색,/ode∗?/g+?:바로앞의문자가1번나타나는경우만검색/ode+?/g:직접숫자를넣어서연속되는개수를설정할수있습니다./ode2/g:2개의′e′를포함한문자열검색/ode2,/g:2개이상의′e′를포함한문자열검색/ode2,5/g:2개이상5개이하의′e′를포함한문자열검색∣:or조건으로검색하여∣의왼쪽또는오른쪽의검색결과를반환합니다."CcOoDdEe".match(/O∣D/g);//["O","D"]"CccOooDDdEEeee".match(/D+∣e+/g);//["DD","eee"][abc]//aorborc를검색합니다.a∣b∣c와동일하게작동함.[a−c]//[abc]와동일함,−로검색구간을설정할수있음."CccOooDDdEEeee".match(/[CD]+/g);//CorD가한번이상반복된문자열을반복검색함.["C","DD"]"AA12ZZAd로 감싸주면 그 사이에 들어간 문자열과 정확하게 일치하는 부분을 찾습니다. *는 *바로 앞의 문자가 0번 이상 나타나는 경우를 검색합니다. /ode*/g +는 +바로 앞의 문자가 1번 이상 나타나는 경우를 검색합니다. /ode+/g ?는 ?앞의 문자가 0번 혹은 1번 나타나는 경우만 검색합니다. /ode?/g *?: 바로 앞의 문자가 0번 나타나는 경우만 검색, /ode*?/g +?: 바로 앞의 문자가 1번 나타나는 경우만 검색 /ode+?/g {}: 직접 숫자를 넣어서 연속되는 개수를 설정할 수 있습니다. /ode{2}/g: 2개의 'e'를 포함한 문자열 검색 /ode{2,}/g: 2개 이상의 'e'를 포함한 문자열 검색 /ode{2,5}/g: 2개 이상 5개 이하의 'e'를 포함한 문자열 검색 |:or 조건으로 검색하여 |의 왼쪽 또는 오른쪽의 검색 결과를 반환합니다. "Cc Oo Dd Ee".match(/O|D/g); // ["O", "D"] "Ccc Ooo DDd EEeee".match(/D+|e+/g); // ["DD", "eee"] [abc] // a or b or c 를 검색합니다. a|b|c와 동일하게 작동함. [a-c] // [abc]와 동일함, -로 검색구간을 설정할 수 있음. "Ccc Ooo DDd EEeee".match(/[CD]+/g); // C or D 가 한 번 이상 반복된 문자열을 반복 검색함. ["C", "DD"] "AA 12 ZZ Ad %% Az !# dd 54 zz".match(/[A-Za-z]+/g); // ["AA", "ZZ", "Ad", "Az", "dd", "zz"] (/[A-Z]+/gi) : i는 대소문자를 구분하지 않기 때문에 위와 같은 결과가 나온다. "aAbB#67Xz@9".match(/[^a-zA-Z]+/g); // [] 안에 ^ 를 사용하면 anchor로서 문자열의 처음을 찾는 것이 아닌 부정을 나타내기 때문에 []안에 없는 값을 검색합니다. ["$#67", "@9"]
캡처: ()로 그룹화한다고 하였고, 이를 캡처한다 라고 합니다.
let co = 'coco';
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번이 조합되어 "2021c"가 반환
6. 첫 번째 캡처한 (\d+) 로 인해 2021이 반환
7. 두 번째 캡처한 (\w)로 인해 "c"가 반환
문자열 대체 시 캡처된 값 참조: 캡처된 값은 replace() 메소드를 사용하여 문자 치환 시 참조 패턴으로 사용할 수 있습니다.
"code.states".replace(/(\w+).(\w+)/, "$2.$1"); //states.code
non-capturing: ()를 사용하면 그룹화와 캡처를 한다고 하였습니다. 하지만 (?:)로 사용하면 그룹은 만들지만 캡처는 하지 않습니다.
let co = 'coco';
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"]
정규표현식은 응용을 통해 유효성 검사뿐만 아니라 데이터 스크래핑, 문자열 파싱 등과 같은 다양한 상황에서 사용할 수 있고, 알고리즘 문제에서 아이디어를 얻을 수도 있습니다.

profile
저는 서영준 입니다.

0개의 댓글