개념/javaScript

[javaScript] 정규 표현식

칠뎁 2022. 7. 24. 21:30

정규식(정규 표현식, Regular Expression)이란

정규 표현식, 또는 정규식은 문자열에서 특정 문자 조합을 찾기 위한 패턴을 말한다 <mdn>

대표적인 예로, 아이디나 비밀번호가 일정조건에 들어오는지 체크할때 쓰일 수 있다. 

아이디엔 영어 몇자리 이상이어야 한다던가, 비밀번호는 특수문자와 영어 대소문자를 포함해야한다던가...

이런것들을 체크하는 것을 "유효성 검사(validation check)"라고 하는데 이 정규식은 유효성 검사에서 요긴하게 쓸 수 있다.


정규표현식 생성하기

정규 표현식을 생성하는 두가지 방법은 아래와 같다.

//1️⃣리터럴 방식, 슬래시로 패턴을 감싸서 작성 => /패턴/
//바뀔일 없을 때, 고정적일때
const re = /abc/;
//2️⃣객체의 생성자 호출
//바뀔일 있을때, 가변적일때
const re = new RegExp("abc")

둘다 결과물은 같지만, 1️⃣리터럴 방식은 스크립트를 불러올 때 컴파일 되기 때문에, 바뀔 일이 없는 패턴의 경우에 좋으며,

2️⃣생성자 함수는 런타임에 컴파일 되기 때문에, 바뀔 수 있거나, 사용자 입력 등 외부 출처에서 가져오는 패턴의 경우에 좋다.

let tag = prompt("어떤 태그를 찾고 싶나요?", "h2");

let regexp = new RegExp(`<${tag}>`); // 프롬프트에서 "h2"라고 대답한 경우, /<h2>/와 동일한 역할을 합니다.

위와 같은 경우처럼 유저의 답변에 따라 다른 문자열을 찾고 싶을 때에 이런 식으로 작성할 수 있다.


플래그(flag)로 검색 조건 달기

플래그는 /패턴/ 끝에 붙어,

정규표현식을 생성할때 고급 검색을 위한 옵션을 설정할 수 있도록 하는 것이다.

하나만 찾을지, 모두 찾을지 등, 대소문자를 생각할지 말지 등등의 조건을 입력한다.

생성할때 리터럴 방식으로 작성했는가 생성자 호출 방식으로 작성했는가에 따라 적는 방법이 약간 다르다.

//flags에 플래그 문자열 들어감
const re1 = /abc/flags;
const re2 = new RegExp(/abc/,flags)

-플래그의 종류-

플래그 의미 설명
i Ignore Case 대소문자 구별하지 않고 검색
g Global 몇번 등장하던, 패턴에 일치하는 모든 문자 찾아냄
(안쓰면 매칭되는 첫 문자만 검색된다. 마치 querySelector처럼)
m Multi Line 줄바뀜 있는 문자열에 대해서 검색

아래는 몇가지 사례이다.

const str1 = "bcabccabcabc";

str1.match(/a/);
//['a', index: 0, input: 'abcabccabcabc', groups: undefined]
//g옵션이 없어서 가장 처음 a하나만 찾음

str1.match(/a/g)
// ['a', 'a', 'a', 'a']
//g옵션이 있어서 등장하는 갯수대로 찾음

//------------------------------

const str2 = "AbcabccAbcabc";
str2.match(/a/);
//['a', index: 3, input: 'AbcabccAbcabc', groups: undefined]
//i옵션 없어서 대문자는 무시하고 소문자a를 찾음

str.match(/a/i)
//['A', index: 0, input: 'AbcabccAbcabc', groups: undefined]
//i옵션 있어서 대문자A 부터 찾음

str.match(/a/ig)
//['A', 'a', 'A', 'a']
//i옵션이 있어서 대소문자 구분없이 g옵션으로 있는대로 다 찾음

//------------------------------
//m플래그는 특별히 아래서 설명할 앵커 '^' '$'의 작동방식에만 영행을 준다.
const str3 = `1st place: Winnie
2nd place: Piglet
3rd place: Eeyore`;

str3.match(/^\d/gm)
//['1', '2', '3']

str3.match(/^\d/g)
//['1']
//\d는 숫자를 의미한다. 숫자만 찾는다.
//^기호로 텍스트 시작문자를 검색할 수 있는데,
//여기에 m을 써주면 줄바꿈이 있다고 알려줘서 ^앵커가 줄바꿈 직후의 문자를 모두 찾는다.
//m플래그가 있으면,각 줄의 첫문자
//m이 없으면, 전체의 첫문자

const str4 = `Winnie: 1
Piglet: 2
Eeyore: 3`;

str4.match(/\d$/gm)
//['1', '2', '3']

str4.match(/\d$/g)
//['3']
//$기호로 텍스트 끝문자를 검색할 수 있는데,
//여기에 m을 써주면 줄바꿈이 있다고 알려줘서 $앵커가 한줄의 마지막 문자를 모두 찾는다.
//m플래그가 있으면,각 줄의 끝문자
//m이 없으면, 전체의 끝문자

str4.match(/\d\n/gm)
//['1\n', '2\n']
//'행의 끝'을 의미하는 $와 '줄바꿈'을 의미하는 \n의 차이
//마지막 줄 Eeyore: 3`은 줄바꿈은 일어나지 않으나, 행의 끝은 맞다.

m옵션과 앵커^,$ 그리고 \n 에대해 더 자세히 알고 싶다면

 


정규표현식 매칭 패턴(문자,숫자,기호 등)

매칭패턴 === 이런 형식에 일치하는 텍스트를 찾아줘!

패턴 의미
a-zA-Z 영어 알파벳(-으로 범위 지정)
ㄱ-ㅎ가-힣 한글 문자(-으로 범위 지정)
0-9 숫자(-으로 범위 지정)
. 모든 문자열(숫자,한글,영어,특수기호,공백 모두. 단 줄바꿈❌)
\d 숫자
\D 숫자가 아닌 것 (↔️\d)
\w 영어 알파벳, 숫자, 언더스코어(_) 
\W \w가 아닌 것(영어 알파벳, 숫자, 언더스코어가 아닌 것) (↔️\w)
\s space 공백
\S space 공백이 아닌것 (↔️\s)
\특수기호 특수기호
\*\^\&\!\?\등...

문자 클래스 더 자세히 보기


정규표현식 검색 패턴(AND,OR,StartWith,EndWith 등)

검색 패턴 === 이런 조건에 걸리는 텍스트를 찾아줘

기호 의미
| OR
[] 괄호안의 문자들 중 하나
[^문자] 괄호안의 문자를 제외한 것
^문자열 특정 문자열로 시작 (주의! 괄호 없음)
문자열$ 특정 문자열로 끝남
() 그룹 검색 및 분류(match메서드에서 그룹별로 묶어줌)
(?:패턴) 그룹 검색(분류 ❌) 
\b 단어의 처음/끝
\B 단어의 처음/끝이 아님(↔️\b)

^와 $ 더 자세히 보기


정규표현식 갯수(수량)패턴

갯수 패턴 === n개 반복되는지 찾아줘

기호 의미
? 최대 한번(없음 || 한개)
* 없거나 있거나 (없음 || 있음): 여러개 포함
+ 최소 한개(한개 || 여러개)
{n} n개
{Min,} 최소 Min개 이상 (주의! 끝에 쉼표(,)적어야 함)
{Min,Max} 최소 Min개 이상, 최대 Max개 이하

더 자세히 보기


정규표현식 메서드

아래 메서드들 안에 정규표현식을 인자로 넣으면 패턴을 이용해 검사하고, 결과 값을 리턴한다.

기호 의미
"문자열".match(/정규표현식/플래그) "문자열"에서 "정규표현식"에 매칭되는 항목들을 배열로 변환
"문자열".replace(/정규표현식/플래그,"대체문자열") /정규표현식/에 매칭되는 항목을 "대체문자열"로 변환
"문자열".split(/정규표현식/플래그) "문자열"을 /정규표현식/에 매칭되는 항목으로 쪼개어 배열로 반환
/정규표현식/플래그.test("문자열") "문자열"이 "정규표현식"과 매칭되는 부분이 있으면 true, 아니면 false 반환
/정규표현식/플래그.exec("문자열") match메서드와 유사(단, 무조건 첫번째 매칭 결과만 반환)

메서드 더 자세히 보기

아래의 여러가지 코드들을 보면 이해가 더 쉽다.

let str = "I love JavaScript";

let result = str.match(/Java(Script)/g);

alert( result[0] ); // JavaScript
alert( result.length ); // 1
let str = "I love JavaScript";

let result = str.match(/HTML/);

alert(result); // null
alert(result.length); // Error: Cannot read property 'length' of null

let result = str.match(/HTML/) || []; //매칭되는게 없으면 빈배열로 반환
alert(result); // []
'12, 34, 56'.split(/,\s*/) // ['12', '34', '56'] 공백(\s)가 있거나 없거나 무시
'12, 34, 56'.split(/,/) //['12', ' 34', ' 56']
'12, 34, 56'.split(/,/) //['12', ' 34', ' 56'] 공백space도 같이 잘림
// replace a dash by a colon
alert('12-34-56'.replace("-", ":")) // 12:34-56

// replace all dashes by a colon
alert( '12-34-56'.replace( /-/g, ":" ) )  // 12:34:56
let str = "html and css";

let result = str.replace(/html|css/gi, str => str.toUpperCase());

alert(result); // HTML and CSS
let str = "John Smith";

let result = str.replace(/(?<name>\w+) (?<surname>\w+)/, (...match) => {
  let groups = match.pop();

  return `${groups.surname}, ${groups.name}`;
});

alert(result); // Smith, John
let str = "I love JavaScript";

// these two tests do the same
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true
let str = 'Hello, world!';

let regexp = /\w+/g; // without flag "g", lastIndex property is ignored
regexp.lastIndex = 5; // search from 5th position (from the comma)

alert( regexp.exec(str) ); // world

사용예시

 

1. 비밀번호 유효성 검사

function onlyNumberAndEnglish(str) {
  return /^[A-Za-z][A-Za-z0-9]*$/.test(str);
}
//첫글자는 영문자 + 영어나 숫자 가능

function strongPassword(str) {
 return  ^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(str);
}
//최소 8자 이상 + 최소 한개의 영문자 + 최소 한개의 숫자

function strongerPassword(str) {
  return /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/.test(str);
}
//최소 8자 이상 + 최소 한개의 영문자 + 최소 한개의 숫자 + 최소 한개 특수문자(@$!%*#?&)

function strongestPassword(str) {
  return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,10}$/.test(str);
}
//최소 8자 이상 + 최대 10자 이하 + 최소 한개의 소문자 + 최소 한개의 대문자 + 최소 한개의 숫자 + 최소 한개의 특수 문자(@$!%*#?&)

 

2. 전화번호 형식 체크

function phoneNumber(str) {
  return /^\d{2,3}-\d{3,4}-\d{4}$/.test(str);
}
//숫자 2or3개 - 숫자 3or4개 - 숫자 4개

문제에서 응용하기 01

 

*문제

문자열을 입력받아 문자열 내에 아래 중 하나가 존재하는지 여부를 리턴해야 합니다.

  1. 'a'로 시작해서 'b'로 끝나는 길이 5의 문자열
  2. 'b'로 시작해서 'a'로 끝나는 길이 5의 문자열

*입력

  • string 타입의 알파벳 문자열

*출력

  • boolean 타입을 리턴해야 합니다.

*주의사항

  • 대소문자를 구분하지 않습니다.
  • 공백도 한 글자로 취급합니다.
  • 'a'와 'b'는 중복해서 등장할 수 있습니다.

*입출력예시

let output = ABCheck('lane Borrowed');
console.log(output); // --> true

*풀이

function ABCheck(str) {
  let regexp1 = /a...b/gi
  let regexp2 = /b...a/gi
  if(regexp1.test(str)||regexp2.test(str)){
    return true
  }else{return false}
}

 

++ 다른 풀이(정규표현식 사용 안한 ver)

function ABCheck(str) {
  if (str === undefined) {
    return false;
  }

  str = str.toLowerCase();

  for (let i = 4; i < str.length; i++) {
    if (
      (str[i] === 'a' && str[i - 4] === 'b') ||
      (str[i] === 'b' && str[i - 4] === 'a')
    ) {
      return true;
    }
  }

  return false;
}

문제에서 응용하기 02

 

*문제

문자열을 입력받아 아이소그램인지 여부를 리턴해야 합니다.

아이소그램(isogram)은 각 알파벳을 한번씩만 이용해서 만든 단어나 문구를 말합니다.

*입력

  • string 타입의 공백이 없는 알파벳 문자열

*출력

  • boolean 타입을 리턴해야 합니다.

*주의사항

  • 빈 문자열을 입력받은 경우, true를 리턴해야 합니다.
  • 대소문자는 구별하지 않습니다.

*입출력예시

let output = isIsogram('aba');
console.log(output); // false

output = isIsogram('Dermatoglyphics');
console.log(output); // true

output = isIsogram('moOse');
console.log(output); // false

*풀이

function isIsogram(str) {
  return !/(\w).*\1/i.test(str)
}

정규식 뜯어보기

 (\w) 문자 뒤에   . 줄바꿈 제외한 모든 문자가  * 있든 없든 많든  \1  그룹1과 같은게 또나오는 형식이 .test  있다면 true리턴하는데

그럼 아이소그램이 아니므로 이를  반전 시켜준다.

여기서 \w를 그룹화()시킨것은 \1로 첫번째 그룹을 group1로 기억하고 참조할 수 있기 때문! 

 

이 정규식이 이해가 가지 않아서 추가적으로 여러 자료들을 찾아봤는데, 

그중 가장 도움이 되었던 엘리님의 강의 + 정리를 첨부한다. (https://github.com/dream-ellie/regex)

 

++ 다른 풀이(정규표현식 사용 안한 ver)

function isIsogram(str) {
  if (str.length === 0) {
    return true;
  }

  let cache = {};
  let strLowered = str.toLowerCase();

  for (let i = 0; i < strLowered.length; i++) {
    if (cache[strLowered[i]]) {
      return false;
    }
    cache[strLowered[i]] = true;
  }

  return true;
}

쨌든... 이걸 어차피 다 외울수는 없고 대충 이정도로 돌아간다는 걸 알아두고 그때그때 찾아서 써야 할듯 싶다

끝으로 정규식 검사하고 뜯어보기에 유명한 사이트 하나 첨부한다.

 

RegExr: Learn, Build, & Test RegEx

RegExr is an online tool to learn, build, & test Regular Expressions (RegEx / RegExp).

regexr.com

 

근데 개발에 있어서는 대충넘어갈건 넘어가야 한다.