😈 I LOVE MATH
수학이다 수학 수 학 시 간 !!
🥺 알고리즘과
👉 특정 방법을 사용해서 풀어볼래 ? => 컴퓨팅 사고 가눙? = 수학적사고
🥺 정규표현식 ?
👉 문자열에서 특정한 문자를 찾아내는 도구
👉 이를 이용하면 수십 줄이 필요한 코딩작업을 간단하게 한두 줄로 끝낼 수 있다 ! WOW !
👉 특정한 규칙을 갖는 문자열로 이루어진 표현식
👉 정규표현식에서 특수 문자는 각각의 고유한 규칙을 갖고 있다.
🤔 ex) 이메일 유효성 검사
예시 코드들은 사용자가 입력한 이메일이나 휴대전화 번호가 유효한지 확인할 때 사용하는 정규표현식
// 이메일 유효성 검사
let regExp = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
// 휴대전화 번호 유효성 검사
let regExp = /^01([0|1|6|7|8|9]?)-?([0-9]{3,4})-?([0-9]{4})$/;
//문자열 str 이 주어질 때,
//str의 길이가 5 또는 7이면서 숫자로만 구성되어 있는지를 확인해 주는 함수를 작성하세요.
//결과는 Boolean으로 리턴됩니다.
//예를 들어 str가 c2021이면 false, 20212이면 true를 리턴합니다.
function solution(str) {
return /^\d{5}$|^\d{7}$/.test(str);
}
👉 정규표현식 규칙을 슬래시(/)로 감싸 사용합니다. 슬래시 안에 들어온 문자열이 찾고자 하는 문자열이며,
컴퓨터에게 '슬래시 사이에 있는 문자열을 찾고 싶어!'라고 명령을 내리는 것입니다.
let pattern = /c/;
// 'c 를 찾을 거야!' 라고 컴퓨터에게 명령을 내리는 것입니다.
// 찾고 싶은 c를 pattern 이라는 변수에 담아놨기 때문에 이 변수를 이용하여 c 를 찾을 수 있습니다.
👉 RegExp 객체의 생성자 함수를 호출하여 사용합니다.
let pattern = new RegExp('c');
// new 를 이용해서 정규 표현식 객체를 생성하고,
// 리터럴 패턴과 동일하게 'c 를 찾을 거야!' 라는 명령입니다.
Brendan Eich 그는 REA:L DO-DUK-CHEK
🥺 exec()
👉
execution 의 줄임말로, 원하는 정보를 뽑아내고자 할 때 사용
👉
검색의 대상이 찾고자 하는 문자열에 대한 정보를 가지고 있다면 이를 배열로 반환하며,
찾는 문자열이 없다면 null을 반환
let pattern = /c/; // 찾고자 하는 문자열
pattern.exec('cha-la-head-cha-la') // 검색하려는 대상을 exec 메소드의 첫 번째 인자로 전달합니다.
// 즉, 'cha-la-head-cha-la' 가 'c' 를 포함하고 있는지를 확인합니다.
// 이 경우 'c' 가 포함되어 있으므로, ['c'] 를 반환합니다.
🥺 test()
👉
찾고자 하는 문자열이 대상안에 있는지 여부를 boolean으로 리턴
let pattern = /c/;
pattern.test('cookie-run');
// 이 경우는 'cookie-run'가 'c'를 포함하고 있으므로 true 를 리턴합니다.
🥺 match()
👉
RegExp.exec() 와 비슷 !
정규 표현식을 인자로 받아 주어진 문자열과 일치된 결과를 배열로 반환
일치되는 결과가 없으면 null 을 리턴
let pattern = /c/;
let str = 'compunicate';
str.match(pattern);
// str 안에 pattern 이 포함되어 있으므로, ['c'] 를 반환합니다.
🥺 replace()
👉
'검색 후 바꾸기'를 수행
첫 번째 인자로는 정규표현식을 받고,
두 번째 인자로는 치환하려는 문자열을 받음
let pattern = /c/;
let str = 'choinophobia';
str.replace(pattern, 'C');
// str 안에서 pattern 을 검색한 후 'C' 로 변경하여 그 결과를 리턴합니다.
// 여기서는 'choinophobia'가 반환됩니다.
🥺 split()
👉
주어진 인자를 구분자로 삼아, 문자열을 부분 문자열로 나누어 그 결과를 배열로 반환
"123,456,789".split(",") // ["123", "456", "789"]
"12304560789".split("0") // ["123", "456", "789"]
🥺 search()
👉
정규표현식을 인자로 받아 가장 처음 매칭되는 부분 문자열의 위치를 반환
매칭되는 문자열이 없으면 -1을 반환
"JavaScript".search(/script/); // -1 대소문자를 구분합니다
"JavaScript".search(/Script/); // 4
"coding".search(/odi/); // 1
플래그(flags)는 정규표현식의 옵션으로 정규식으로 검색하려는 문자 패턴에 추가적인 옵션을 넣어 원하는 문자 검색 결과를 반환하도록 합니다.
🥺 정규표현식은 플래그를 설정해 줄 수
플래그는 추가적인 검색 옵션의 역할을 해
이 플래그들은 각자 혹은 함께 사용하는 것이 모두 가능*
순서에 구분이 없다
🥺 i
👉 i
를 붙이면 대소문자를 구분하지 않는다
let withi = /c/i;
let withouti = /c/;
"Chicken".match(withi); // ['C']
"Chicken".match(withouti); // null
🥺 g
👉 global의 약자로, g
를 붙이면 검색된 모든 결과를 리턴한다
let withg = /c/g;
let withoutg = /c/;
"check".match(withg); // ['c', 'c']
"check".match(withoutg); // ['c'] g 가 없으면 첫 번째 검색 결과만 반환합니다
🥺 m
👉 m
을 붙이면 다중행을 검색한다
let str = `1st : cool
2nd : cook
3rd : sing`;
str.match(/c/gm)
// 3개의 행을 검색하여 모든 c 를 반환합니다.
// ['c', 'c']
str.match(/c/m)
// m은 다중행을 검색하게 해 주지만, g 를 빼고 검색하면 검색 대상을 찾는 순간 검색을 멈추기 때문에
// 첫 행의 ['c'] 만 리턴합니다.
🥺 참고하여 열심히 써먹자
🥺 ^
👉
문자열의 처음을 의미, 문자열에서 ^
뒤에 붙은 단어로 시작하는 부분을 찾음
일치하는 부분이 있더라도, 그 부분이 문자열의 시작 부분이 아니면 null을 리턴함
"coding is fun".match(/^co/); // ['co']
"coding is fun".match(/^fun/); // null
🥺 $
👉
문자열의 끝을 의미, 문자열에서 $
앞의 표현식으로 끝나는 부분(마지막 부분)을 찾음
일치하는 부분이 있더라도, 그 부분이 문자열의 끝 부분이 아니면 null을 리턴함
"coding is fun".match(/un$/); // ['un']
"coding is fun".match(/is$/); // null
"coding is fun".match(/^coding is fun$/);
// 문자열을 ^ 와 $ 로 감싸주면 그 사이에 들어간 문자열과 정확하게 일치하는 부분을 찾습니다
// ["coding is fun"]
🥺 *
👉
*
는 *
바로 앞의 문자가 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"]
👉
|
는 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"] 를 반환합니다.
👉
대괄호 []
안에 명시된 값을 검색
[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"]
읽기 거북하고 피곤하고,,,, 대체 이게 뭔가 하겠지만 어쩔 수 없다 참고 읽자
🥺\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); // ["@", "."]
ㅎ ㅏ 끝도 없구나 근데 다 외워야함ㅋ
🥺()
👉 그룹으로 묶는다는 의미 이외에도 다른 몇 가지 의미가 더 있다고 한ㄷr...
()
로 묶어주면 그 안의 내용을 하나로 그룹화할 수 있다 !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"가 반환됨. BUT (co)+
는 "c" 와 "o" 를 그룹화하여 "co"를 단위로 1회 이상 반복을
검색하기 때문에 "coco"가 반환됨. 여기서 특이한 점은 일치하는 문자열로 반환된 결과가 2개입니다.
이제 이 이유에 대해 알아야 하지 않을카 ?...
()
로 그룹화한다고 하였고, 이를 캡처한다 라고 함 !co.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined]
따라서 2번 과정에 의해 "coco" 가 반환되고, 3번에 의해 "co"가 반환되는 것 ! 이라고함...
🤔 EX >
"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"
가 반환🥺 캡처된 값은 replace() 메소드를 사용하여 문자 치환 시 참조 패턴으로 사용될 수 있다 !
"code.studying".replace(/(\w+)\.(\w+)/, "$2.$1"); //studying.code
우선 첫 번째 (\w+)
가 code
를 캡처하고, 두 번째 (\w+) 가 studying 를 캡처합니다. (/(\w+)\
와 (\w+)/\
사이의 .
은 .
앞에 역슬래시가 사용되었기 때문에 '임의의 한 문자'가 아닌 기호로서의 온점 . 을 의미합니다.)
각 캡처된 값은 첫 번째는 $1
이 참조, 두 번째는 $2
이 참조하기 때문에
이 참조된 값을 "$2.$1"
이 대체하게 되어 code
와 studying
가 뒤바뀐 "studying.code"
가 반환
🥺 ()
를 사용하면 그룹화와 캡처를 한다고 하였쥬?
근데 (?:)
로 사용하면 그룹 만들기 ⭕️, 캡처 ❌ !
let co = 'coco';
co.match(/(co)+/); // ["coco", "co", index: 0, input: "coco", groups: undefined]
co.match(/(?:co)+/);
// ["coco", index: 0, input: "coco", groups: undefined]
// 위 "캡처" 예시의 결괏값과 비교해 보시기 바랍니다.
🥺 (?=)
는 검색하려는 문자열에 (?=여기)
에 일치하는 문자가 있어야 (?=여기)
앞의 문자열을 반환함 !
"abcde".match(/ab(?=c)/);
// ab 가 c 앞에 있기 때문에 ["ab"] 를 반환합니다.
"abcde".match(/ab(?=d)/);
// d 의 앞은 "abc" 이기 때문에 null 을 반환합니다.
🥺 (?!)
는 (?=)
의 부정
"abcde".match(/ab(?!c)/); // null
"abcde".match(/ab(?!d)/); // ["ab"]
아니 뭐이리 외워야하는게 많아 ?