[REAL Deep Dive into JS] 31. RegExp

young_palleteΒ·2022λ…„ 10μ›” 7일
0

REAL JavaScript Deep Dive

λͺ©λ‘ 보기
32/46

🚦본둠

λ“œλ””μ–΄! μ œκ°€ κΈ°λŒ€ν•˜λ˜ λŒ€λ§μ˜ νŒŒνŠΈκ°€ μ™”λ„€μš”! πŸ₯°
μ΄μ „μ—λŠ” 이 파트λ₯Ό κ·Έμ € λ„˜κ²ΌλŠ”λ°, μ •κ·œν‘œν˜„μ‹μ„ μ œλŒ€λ‘œ ν†Ίμ•„λ³Ό 수 있게 λ˜μ–΄ ν–‰λ³΅ν•΄μš”!

μ •κ·œν‘œν˜„μ‹μ€ μΌμ •ν•œ νŒ¨ν„΄μ„ 가진 λ¬Έμžμ—΄μ˜ 집합을 ν‘œν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” ν˜•μ‹ 언어라 ν•΄μš”.

일반적으둜, μ •κ·œν‘œν˜„μ‹μ€ λ‹€λ₯Έ μ–Έμ–΄λ“€κ³Ό 에디터듀에 λŒ€μ²΄λ‘œ λ‚΄μž₯λ˜μ–΄ μžˆμ–΄μš”.
λŒ€μ²΄μ μœΌλ‘œ λ‹€μŒ 2κ°€μ§€μ˜ μ •κ·œν‘œν˜„μ‹ 문법이 μ‘΄μž¬ν•˜λŠ”λ°μš”.

  • POSIX (Standard)
  • PCRE (Extends POSIX by perl)

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” PCRE 문법을 ES3λ•ŒλΆ€ν„° λ„μž…ν•˜μ˜€μŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ λ§Œμ•½ POSIX에 μ΅μˆ™ν•˜μ‹  뢄듀은 PCRE 문법을 μ΅νžˆμ‹œκΈ°λ₯Ό ꢌμž₯ν•΄μš”.

핡심

일단 μ €λŠ” μ–΄λ–€ 것을 이해할 λ•Œλ§ˆλ‹€ 항상 이 주제의 핡심과 μ‚¬μš© λͺ©μ μ΄ 무엇인지λ₯Ό λͺ…ν™•νžˆ ν•˜λŠ”λ°μš”!κ²°κ΅­ 제 생각에 μ •κ·œν‘œν˜„μ‹μ„ 이해할 λ•Œ μ£Όλͺ©ν•  뢀뢄은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ •κ·œν‘œν˜„μ‹μ˜ νŒ¨ν„΄μ΄λž€ 무엇인지? (κ²°κ΅­ 본질이 λ¬Έμžμ—΄μ˜ νŒ¨ν„΄μ—μ„œ κΈ°μΈν•œ ν‘œν˜„μ‹μ΄λ―€λ‘œ)
  • μ–΄λ–»κ²Œ μœ νš¨ν•œμ§€λ₯Ό νŒλ‹¨ν•˜μ§€? (μ£Ό μ‚¬μš© λͺ©μ μΈ 검색과 검사λ₯Ό μœ„ν•œ 방법)
  • μ£Όμš” 문법은 무엇인지? (μ‹€μ œ μ‚¬μš©ν•˜κΈ° μœ„ν•œ 문법)

우리, 그러면 ν•œ 번 이 3가지λ₯Ό ν† λŒ€λ‘œ μ‚΄νŽ΄λ³ΌκΉŒμš”? πŸ˜‰

νŒ¨ν„΄μ΄λž€

νŒ¨ν„΄μ΄λΌ ν•  λ•Œ μ–΄λ–€ 것이 λ– μ˜€λ₯΄μ…¨λ‚˜μš”? μ•„λ§ˆ λŒ€λΆ€λΆ„ μ–΄λ–€ ν˜„μƒμ— λŒ€ν•œ μΌμ •ν•œ κ·œμΉ™μ„ λ– μ˜¬λ Έμ„ 것 κ°™μ•„μš”.

이λ₯Ό μ •κ·œν‘œν˜„μ‹μ˜ νŒ¨ν„΄μ— λŒ€μž…ν•˜λ©΄ μ–΄λ–¨κΉŒμš”? λ°”λ‘œ, μ–΄λ–€ λ¬Έμžμ—΄ ν‘œν˜„μ‹μ— λŒ€ν•œ μΌμ •ν•œ κ·œμΉ™μ„ νŠΉμ •ν•œ λ¬Έλ²•μœΌλ‘œ μ„œμˆ ν•œ 것이라 ν•  수 있겠죠? πŸ™‡πŸ»β€β™‚οΈ

μ΄λŸ¬ν•œ νŒ¨ν„΄μ€ /으둜 μ—΄κ³  λ‹«μ•„μš”. 마치 μ—˜λ¦¬λ¨ΌνŠΈμ—μ„œ μš°λ¦¬κ°€ <div></div>μ‹μœΌλ‘œ μ—˜λ¦¬λ¨ΌνŠΈμ˜ μ‹œμž‘κ³Ό 끝을 μ•Œλ¦¬λ“―μ΄ 말이죠.

μ˜ˆμ»¨λŒ€ λ‹€μŒκ³Ό 같이 ν‘œκΈ°ν•˜λŠ” κ²λ‹ˆλ‹€.

const regExp = /Hello/;

맀치

μœ„μ˜ νŒ¨ν„΄μ„ 예둜 λ“€μžλ©΄, ν•΄λ‹Ή νŒ¨ν„΄κ³Ό μΌμΉ˜ν•˜λŠ” λ¬Έμžκ°€ μžˆλ‹€λ©΄ 이λ₯Ό λ§€μΉ˜ν•œλ‹€λΌκ³  λ§ν•©λ‹ˆλ‹€.

μ‹€μ œλ‘œλ„ RegExp.prototype.test λ©”μ„œλ“œμ™€ String.prototype.matchλΌλŠ” λ©”μ„œλ“œλ₯Ό 톡해 이 일치 μ—¬λΆ€λ₯Ό 확인할 수 μžˆλ‹΅λ‹ˆλ‹€.

const regExp = /Hello/;

const test = 'Hello, world!';

console.log(regExp.test(test)); // true
console.log(test.match(regExp)); 
// ['Hello', index: 0, input: 'Hello, world!', groups: undefined]

μ—¬κΈ°μ„œ λ°˜ν™˜λ˜λŠ” κ°μ²΄λŠ” λ°°μ—΄ 객체라고 ν•˜μ§€λ§Œ, ν”„λ‘œνΌν‹° ν‚€κ°€ μ •μˆ˜κ°€ μ•„λ‹ˆλΌλŠ” μ μ—μ„œ μœ μ‚¬λ°°μ—΄μ˜ νŠΉμ§•μ„ κ°•ν•˜κ²Œ μ§€λ‹™λ‹ˆλ‹€.

// μ‹€μ œλ‘œ `index`, `input`κ³Ό `groups`λΌλŠ” ν”„λ‘œνΌν‹°κ°€ μžˆμŠ΅λ‹ˆλ‹€.
console.log(test.match(regExp).index); // 0
console.log(test.match(regExp).input); // 'Hello, world!'
console.log(test.match(regExp).hasOwnProperty('groups')); // true;
// '[object Array]'
Object.prototype.toString.call(test.match(regExp));

// μƒμ„±μžλ₯Ό 확인해보아도 λ°°μ—΄μž…λ‹ˆλ‹€.
test.match(regExp).constructor.name; // 'Array'

// lengthλ₯Ό κ°€μ§‘λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 값이 μ •ν™•ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
console.log(test.match(regExp).length); // 1

// test.match(regExp)의 λ°˜ν™˜κ°’μ€ μΌμΉ˜ν•˜λŠ” λ¬Έμžμ—΄μž…λ‹ˆλ‹€.
test.match(regExp)[0]; // 'Hello'
test.match(regExp)[1]; // 'undefined'

ν”Œλž˜κ·Έ

μ΄λ•Œ, 같이 λ”°λΌμ„œ μ΅ν˜€μ•Ό ν•  μΉœκ΅¬κ°€ ν•˜λ‚˜ μžˆμŠ΅λ‹ˆλ‹€. ν”Œλž˜κ·ΈλΌλŠ” μΉœκ΅¬μΈλ°μš”.
ν”Œλž˜κ·Έλ₯Ό 톡해 μΌμ’…μ˜ 검색 방식을 μ •μ˜ν•  수 μžˆμ–΄μš”!

λ‹€μŒ 3가지가 μžˆμŠ΅λ‹ˆλ‹€.

ν”Œλž˜κ·Έμ˜λ―Έμ„€λͺ…
iignore caseλŒ€μ†Œλ¬Έμž ꡬ별을 λ¬΄μ‹œν•©λ‹ˆλ‹€.
gglobalμ „μ—­μ μœΌλ‘œ λͺ¨λ“  μΌμΉ˜ν•˜λŠ” λ¬Έμžλ“€μ„ κ²€μƒ‰ν•©λ‹ˆλ‹€.
mmulti lineμ—¬λŸ¬ ν–‰μ˜ λ¬Έμžμ—΄ 행을 μ—¬λŸ¬ 쀄에 걸쳐 κ²Œμ†ν•΄μ„œ κ²€μƒ‰ν•©λ‹ˆλ‹€.

쀑간 정리

자, μš°λ¦¬λŠ” 이제 2가지λ₯Ό λ°°μ› μ–΄μš”.

  • μ •κ·œν‘œν˜„μ‹μ˜ νŒ¨ν„΄μ΄λž€ 무엇인지? (κ²°κ΅­ 본질이 λ¬Έμžμ—΄μ˜ νŒ¨ν„΄μ—μ„œ κΈ°μΈν•œ ν‘œν˜„μ‹μ΄λ―€λ‘œ)
  • μ–΄λ–»κ²Œ μœ νš¨ν•œμ§€λ₯Ό νŒλ‹¨ν•˜μ§€? (μ£Ό μ‚¬μš© λͺ©μ μΈ 검색과 검사λ₯Ό μœ„ν•œ 방법)

λ‹€λ§Œ ν•΄λ‹Ή μ§ˆλ¬Έμ— λŒ€ν•΄μ„œλŠ” 아직 μ„€λͺ…이 λ˜μ§€λ₯Ό μ•Šμ•˜κ΅°μš”.

  • μ£Όμš” 문법은 무엇인지? (μ‹€μ œ μ‚¬μš©ν•˜κΈ° μœ„ν•œ 문법)

πŸ’‘ κ·Έλ ‡λ‹€λ©΄, 이제 μ •κ·œν‘œν˜„μ‹μ— λŒ€ν•œ λ‹€μ–‘ν•œ μ‚¬μš©λ°©λ²•κ³Ό 문법듀을 ν•œ 번 μ‚΄νŽ΄λ³ΌκΉŒμš”?

μ£Όμš” 문법

반볡 검색

{m,n}을 톡해 효과적으둜 반볡 검색을 μΆ”λ €λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ 두 인자인 m, n은

  • m: μ΅œμ†Œ m번
  • n: μ΅œλŒ€ n번

λ₯Ό μ˜λ―Έν•΄μš”. μ΄λ•Œ n은 μƒλž΅ κ°€λŠ₯ν•©λ‹ˆλ‹€.

μ—¬κΈ°μ„œ νŒ¨ν„΄μ€, κ³΅λ°±κΉŒμ§€ 체크할 수 μžˆκΈ°μ— μƒλ‹Ήνžˆ λ―Όκ°ν•΄μš”. μž…λ ₯에 곡백이 없도둝 μœ μ˜ν•©μ‹œλ‹€.

예제λ₯Ό ν•œ 번 μ‚΄νŽ΄λ³Όκ²Œμš”.

const str = '0000000000000000000000000O0000000000'
console.log(str.match(/\O{1,1}/).index) // 25

주의! g ν”Œλž˜κ·Έλ₯Ό μ‚¬μš©ν•˜λ©΄ index ν‚€λ₯Ό ν™œμš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€. (μ „μ—­ κ²€μƒ‰μ΄λ‹ˆκΉŒμš”!)

문자 λ‹€μŒμ΄λ‚˜, []내에 μ—¬λŸ¬ 문자λ₯Ό μž…λ ₯ ν›„ +λ₯Ό ν•  경우 1번 μ΄μƒμ˜ λ°˜λ³΅λ˜λŠ” λ¬Έμžλ“€μ„ λͺ¨λ‘ κ²€μƒ‰ν•©λ‹ˆλ‹€.

'000-0000-0000'.match(/0+/g) // ['000', '0000', '0000']
'000-0000-0000'.match(/[0-]+/g) // ['010-1234-1234']

OR 검색

보톡 μ—¬λŠ 문법과 λ§ˆμ°¬κ°€μ§€λ‘œ |을 ν† λŒ€λ‘œ 의미λ₯Ό κ°€μ§ˆ 수 μžˆλ„€μš”!

const isCheckAnswerPattern = /Y|N/;
const answers = {
	requiredAgreement1: 'Y',
    requiredAgreement2: 'Y',
    optionalAgreement1: 'N',
    optionalAgreement2: 'N',
};

// true
console.log(
  Object.values(answers)
  		.every(v => isCheckAnswerPattern.test(v))
);

ν˜Ήμ€ []μ•ˆμ— μ—¬λŸ¬ 문자λ₯Ό λ„£λŠ” 경우, 이λ₯Ό OR둜 μ·¨κΈ‰ν•©λ‹ˆλ‹€.

const isCheckAnswerPattern = /[YN]/;
const answers = {
	requiredAgreement1: 'Y',
    requiredAgreement2: 'Y',
    optionalAgreement1: 'N',
    optionalAgreement2: 'N',
};

// true
console.log(
  Object.values(answers)
  		.every(v => isCheckAnswerPattern.test(v))
);

λ²”μœ„ 검색

[]μ•ˆμ— μ‹œμž‘λ²”μœ„-λλ²”μœ„ ν˜•νƒœλ‘œ μž…λ ₯ν•©λ‹ˆλ‹€.
μ˜ˆμ»¨λŒ€ λ‹€μŒκ³Ό 같이 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const onlyNumberPattern = /[0-9]/g
// ['0', '1', '0', '1', '2', '3', '4', '1', '2', '3', '4']
'010-1234-1234'.match(onlyNumberPattern);

\d와 \D

였직 숫자인 λ¬Έμžμ—΄λ§Œ κ²€μƒ‰ν•˜λŠ” κ²½μš°λŠ” κ½€λ‚˜ λ§ŽμŠ΅λ‹ˆλ‹€.
이럴 λ•ŒλŠ” ν•΄λ‹Ή νŒ¨ν„΄μ„ 톡해 κ°„νŽΈν•˜κ²Œ 검색할 수 μžˆμŠ΅λ‹ˆλ‹€.

const onlyNumberPattern = /[\d]+/g;
'010-1234-1234'.match(onlyNumberPattern); //  ['010', '1234', '1234']

\w와 \W

μ•ŒνŒŒλ²³, 숫자, μ–Έλ”μŠ€μ½”μ–΄λ₯Ό 포함할 λ•Œ ν•΄λ‹Ή νŒ¨ν„΄μ€ μœ μš©ν•©λ‹ˆλ‹€.
μ˜ˆμ»¨λŒ€ λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•  수 μžˆκ² κ΅°μš”!

const email = 'young_pallete@test.com'

// [숫자, μ•ŒνŒŒλ²³, μ–Έλ”μŠ€μ½”μ–΄]@[숫자, μ•ŒνŒŒλ²³, μ–Έλ”μŠ€μ½”μ–΄, ν•˜μ΄ν”ˆ](1~2번의 .[μ•ŒνŒŒλ²³])으둜 이메일 λ¬Έμžμ—΄ μœ νš¨μ„± 검사
const emailPattern = /^([\w]+)@([\w-]+)((.([a-z]{2,3})){1,2})$/

emailPattern.test(email) // true

μ‹œμž‘ μœ„μΉ˜ 검색

이미 μœ„μ— λ‚˜μ™€ μžˆκ΅°μš”!
^을 μ΄μš©ν•˜λ©΄ 맨 처음 μ‹œμž‘ μœ„μΉ˜λΆ€ν„° μΌμΉ˜ν•΄μ•Ό ν•œλ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€.
μ΄λ•Œ, [^...]의 λ°©μ‹μœΌλ‘œ ν‘œν˜„ν•˜λŠ” νŒ¨ν„΄μ—μ„  ...와 μΌμΉ˜ν•˜μ§€ μ•ŠλŠ” νŒ¨ν„΄μΈμ§€ NOT의 의미둜 ν•΄μ„ν•˜λ‹ˆ μ£Όμ˜ν•˜μ„Έμš”!

λ§ˆμΉ˜λ§‰ μœ„μΉ˜ 검색

이 μ—­μ‹œ μœ„μ˜ μ˜ˆμ‹œμ— λ‚˜μ™€ μžˆμ–΄μš”!
$을 μ΄μš©ν•˜λ©΄, νŠΉμ • λ¬Έμžμ—΄ νŒ¨ν„΄μ˜ λ§ˆμ§€λ§‰μ΄ μΌμΉ˜ν•˜λŠ”μ§€λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

ν•Έλ“œν° 번호 검사(κ΅­λ‚΄)

const phoneNumber = '010-1234-1234';

const phoneNumberPattern = /^\d{3}-\d{3,4}-\d{4}$/;

phoneNumberPattern.test(phoneNumber) // true

특수문자 검색

λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•˜λ©΄ 특수문자 포함 μ—¬λΆ€λ₯Ό 검사 κ°€λŠ₯ν•΄μš”!

const specialCharPattern = /[^a-zA-Z0-9γ„±-γ…Žγ…-γ…£κ°€-힣]/
specialCharPattern.test('μ•ˆλ…•!') // true
specialCharPattern.test('μ•ˆλ…•γ…Žγ…Ž') // false

기타

κ·Έλ‚˜λ§ˆ μ±…μ—μ„œ μœ μš©ν•˜λ‹€ 싢은 것듀은 κ°€μ Έμ˜€κ±°λ‚˜ 제 μ˜λ„μ— 맞게 λ³€ν˜•ν–ˆλŠ”λ°μš”!
사싀 이거 말고도 νŒ¨ν„΄μ„ μœ„ν•œ νŠΉμˆ˜λ¬ΈμžλŠ” μ—„~μ²­ λ§Žμ•„μš” 😭

λ”°λΌμ„œ, ν•„μš”ν•œ 쑰건듀이 μžˆλ‹€λ©΄, 이λ₯Ό κ°„νŽΈν•˜κ²Œ μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ 방법이 μžˆλŠ”μ§€ ν•΄λ‹Ή MDN - μ •κ·œ ν‘œν˜„μ‹ μ°Έκ³ λ₯Ό μΆ”μ²œλ“œλ €μš” πŸ₯°

μ‘μš©λ¬Έμ œ - λ‹€νŠΈκ²Œμž„

μ΄μ „μ˜ λ‹€νŠΈκ²Œμž„μ€ λ‹€μŒκ³Ό 같이 ν’€μ—ˆμ—ˆμ–΄μš”!

const setValue = {
  S: (data) => data,
  D: (data) => Math.pow(data, 2),
  T: (data) => Math.pow(data, 3),
  "*": (data) => (2 * data),
  "#": (data) => (-1 * data),
  "+": (strData, nextStrData) => strData + nextStrData,
};


const getScoresArr = (dartResult) => {
  const scoresArr = [];
  let isLastDataStr = false;

  const length = dartResult.length;
  for (let i = 0; i < length; i += 1) {
    const nowData = dartResult[i];
    const isNowDataStr = Number.isNaN(parseInt(nowData));
      
          if (isNowDataStr) {
      const lastScore = scoresArr.pop();
      scoresArr.push(
        ...(nowData === "*" && scoresArr.length
          ? [setValue[nowData](scoresArr.pop()), setValue[nowData](lastScore)]
          : [setValue[nowData](lastScore)])
      );
    } else {
      scoresArr.push(
        !i || isLastDataStr ? nowData : setValue["+"](scoresArr.pop(), nowData)
      );
    }

    isLastDataStr = isNowDataStr;
  }
  return scoresArr;
};

const solution = (dartResult) => {
  return getScoresArr(dartResult).reduce(
    (acc, cur) => acc + parseInt(cur),
    0
  );
};

μƒλ‹Ήνžˆ 절차적으둜 ν’€μ–΄μ„œ, μ–Έλœ» 보기엔 ν•œλˆˆμ— μ½”λ“œκ°€ 잘 보이지 μ•ŠλŠ”κ΅°μš”.
ν•˜μ§€λ§Œ μ •κ·œν‘œν˜„μ‹ λ¬Έλ²•μœΌλ‘œ 이λ₯Ό ν’€μ–΄λ‚΄λ©΄, μ„ μ–Έμ μœΌλ‘œ μ–΄λ–€ λ‚΄μš©μœΌλ‘œ 문제λ₯Ό μ ‘κ·Όν–ˆλŠ”μ§€λ₯Ό νŒŒμ•…ν•˜κΈ° μ‰¬μ›Œμ§‘λ‹ˆλ‹€!

const getInteger = (now, p1, p2, p3) => (p3 ?  -1 : 1) * p1 ** ['', 'S', 'D', 'T'].indexOf(p2) + 'S'

const passStarForward = (now, p1, p2, p3) => p3 + (p1 * 2) + p2

const solution = (dartResult) =>  dartResult
        .replace(/([0-9]+)(S|D|T)(\#?)/g, getInteger) // μ •μˆ˜λ‘œ λ°”κΏ”μ£Όκ³ !
        .replace(/([-|0-9]+)(S|D|T)(\*)/g, passStarForward) // μŠ€νƒ€μƒμ˜ 경우 2배둜 ν•΄μ€€ λ‹€μŒ μ•žμœΌλ‘œ λ„˜κ²¨μ£Όκ³ !
        .replace(/([-|0-9]+)(S|D|T)(\*)/g, passStarForward) // μ•žμ˜ μ μˆ˜λ„ μ—…λ°μ΄νŠΈ ν•΄μ£Όκ³ !
        .match(/[-|0-9]+/g) // μ •μˆ˜λ§Œ λ½‘μ•„λ‚΄μ„œ
		.reduce((acc, cur) => acc + Number(cur), 0) // λ‹€ 더해쀀닀!

정말 재미있죠? πŸ₯°

πŸŽ‰ 마치며

사싀 이 파트 κΈ°λŒ€λ„ 많이 ν•˜λ©΄μ„œ 걱정도 많이 ν–ˆμ–΄μš”.
κ½€λ‚˜ μ œκ°€ λΆ€μ‘±ν•œ 뢀뢄이라 μƒκ°ν–ˆλŠ”λ°, μ΄λ²ˆμ— μ‚΄νŽ΄λ³΄λ©΄μ„œ 많이 곡포증을 κ·Ήλ³΅ν–ˆλ„€μš”!
μ‘°λ§Œκ°„ ν•œ 번 이λ₯Ό μ΄μš©ν•œ λ¬Έμ œλ„ ν’€μ–΄λ΄μ•Όκ² μ–΄μš”. πŸ™†πŸ» 이상!

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. πŸ˜‰

0개의 λŒ“κΈ€