몇몇 코딩테스트에서 비트연산자, 문자열조작 등 기본적인 언어 사용법이 나와서 정리할 필요가 있어 정리한다.
비트 연산자
@nulbo [TIL / JavaScript] 비트 연산자에 잘 정리되어 있다.
비트의 출력
num.toString(2)
을 이용하면 2진법의 비트로 이루어진 문자열을 반환한다. (원본을 변환하지 않는다.)
let num = 10;
console.log(num.toString(2)); //1010
이러한 비트를 직접 조작하는 연산이 비트연산자이다.
비트의 입력
한편 숫자형에 '0b'라는 접두사를 붙이면 숫자를 이진수로 입력할 수 있다.
let num = 0b1010;
console.log(num); //10
2진수(binary): 0b (숫자 0과 알파벳 b)
8진수(octal): 0o (숫자 0과 알파벳 o)
16진수(hexadeciaml): 0x (숫자 0과 알파벳 x)
출처
즉 0b 접두사와 toString(2)를 이용하면 비트로 입력 및 출력할 수 있다. (0b접두사를 붙여서 출력하는 법? 그런 건 없다. ㅇㅇ)
let num = 0b1010;
console.log(num.toString(2)); //1010
논리 연산자 (AND, OR, XOR, NOT)
& (AND)
두 비트 모두 1이면 결과는 1, 그 외에는 0이 됩니다.
0101
0011
-> 0001
let a = 0b0101; //5
let b = 0b0011; //3
let res = a & b;
console.log(res.toString(2).padStart(4, 0)); // 0001
console.log(res); // 1
| (OR)
두 비트 모두 1이면 결과는 1, 그 외에는 0이 됩니다.
0101
0011
-> 0111
let a = 0b0101; //5
let b = 0b0011; //3
let res = a | b;
console.log(res.toString(2).padStart(4, 0)); // 0111
console.log(res); // 7
^ (XOR)
두 비트가 다르면 1, 같으면 0
0101
0011
-> 0110
let a = 0b0101; //5
let b = 0b0011; //3
let res = a ^ b;
console.log(res.toString(2).padStart(4, 0)); // 0110
console.log(res); // 6
~ (NOT)
부정 연산자이다. 0과 1을 뒤집는다.
비트 연산자가 32비트로 이루어진다는 점을 이용하여 문자나 소수를 정수로 표현하는 데에 쓰기도 하며, 'Number(str)'보다 연산 속도가 빠르다.
let a = 1b0101; //5
let res = ~a;
console.log(res.toString(2)); // -110
console.log(res); // -6
let b = 0b0011; //3
res = ~b;
console.log(res.toString(2)); // -100
console.log(res); // -4
let str = "30";
console.log(typeof ~~str); //number
시프트 연산자와 바이너리 서치
과거에 적어놓았던 내용을 복-붙
부분집합. {O|X, O|X, O|X, O|X} 의 모든 부분집합을 구하려면?
- 비트 연산자
a = 60, b = 13 이라 가정한다.
a = 0011 1100
b = 0000 1101
&
AND 연산. 둘다 참일때만 만족 (a & b) = 12 → 0000 1100
|
OR 연산. 둘 중 하나만 참이여도 만족 (a | b) = 61 → 0011 1101
^
XOR 연산. 둘 중 하나만 참일 때 만족 (a ^ b) = 49 → 0011 0001
~
보수 연산. (~a) = -61 → 1100 0011
<<
왼쪽 시프트 연산자. 변수의 값을 왼쪽으로 지정된 비트 수 만큼 이동(끝에 0이 하나 더 붙는 거임) a << 2 = 240 → 1111 0000
>>
오른쪽 시프트 연산자. 변수의 값을 오른쪽으로 지정된 비트 수 만큼 이동 a >> 2 = 15 → 0000 1111
이번에는 시프트(shift) 연산을 알아보겠습니다.
먼저 왼쪽 시프트(<<)입니다.
>>> bin(0b1 << 1)
'0b10'
>>> bin(0b1 << 2)
'0b100'
>>> bin(0b1 << 3)
'0b1000'
위에서 1이 점점 왼쪽으로 이동하는 것을 볼 수 있습니다.
다음은 오른쪽 시프트(>>)입니다.
>>> bin(0b1010 >> 1)
'0b101'
1이 오른쪽으로 한 자리씩 밀린 것을 볼 수 있습니다. 더 많이 시프트하면 어떻게 될까요?
>>> bin(0b1010 >> 2)
'0b10'
>>> bin(0b1010 >> 3)
'0b1'
>>> bin(0b1010 >> 4)
'0b0'
점점 밀려서 결국 0이 되었네요.
시프트 연산자로 왼쪽으로 한 칸 이동할 때 마다 2배가 되는 효과가 생긴다.
반대로 오른쪽으로 한 칸씩 이동하면 숫자가 줄어든다.
그럼 2를 곱하고 나누면 되지 않나?라고 생각할 수 있는데, 큰 수에서 비트단위로 코드를 짜야하는 경우가 생기는데, 이 때 시프트연산자를 사용하면 큰 생각없이 비트들을 정리할 수 있어서 편하다.
1 << n
: 비트 원소가 n개일 때 부분집합의 갯수. 즉 2의 n승
i&(1<<j)
: i가 10110일때 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5
는 1, 10, 100, 1000, 10000, 100000 (0이 늘어난다. 즉 2배씩 늘어난다.) 이고, 이를 &으로 대입하면 0, 10, 100, 0, 1000을 반환한다. 이를 불리언 값으로 대입하면 f, t, t, f, t가 나온다. 이를 활용해 '1이 특정 자릿수에 있는지'를 판단할 수 있게 된다.
print(bin(5)) # 0101
print(bin(14)) # 1110
print(bin(5 & 14)) # 0100 -> 4
정규 표현식
정규표현식을 사용하는 String의 인스턴스 메서드
match(정규표현식)
: 문자열에서 정규표현식을 충족하는 문자들을 배열로 반환한다. 없는 경우 빈 배열을 반환한다.let test = 'ABC is UpperCase of "Abc" or "abc"';
,console.log(test.match(/[A-C]/g)); //['A', 'B', 'C', 'C', 'A']
search(정규표현식)
: 문자열에서 정규표현식을 충족하는 첫번째 문자를 반환한다. 없는 경우 -1을 반환한다.replace(regexp|substr, newSubstr|function)
: 특정 패턴을 가장 앞 하나 새로운 패턴으로 바꾸어서 새로운 문자열을 반환합니다.replaceAll(regexp|substr, newSubstr|function)
: 특정 패턴을 모두 새로운 패턴으로 바꾸어서 새로운 문자열을 반환합니다.
RegExp
RegExp()
로 새로운 정규표현식 객체를 만들 수 있다.
/패턴/
과 플래그
의 조합, /^abc/gi
으로 구성된다.
const regexp1 = new RegExp(/^abc/i); // ES6
const regexp2 = new RegExp(/^abc/, 'i');
문자 클래스
https://ko.javascript.info/regexp-character-classes
\d
: 0~9사이의 문자
\s
: 공백. 스페이스, \t
, \n
, \v
, \f
, \r
\w
: \a-z\
, \A-Z\
, _
포함
\D
: 숫자가 아닌 문자
\S
: 공백이 아닌 문자
\W
: 비 라틴 문자나 공백 문자 등 특수문자
패턴
위치
^패턴
: 해당 패턴으로 시작할때 매치
패턴$
: 해당 패턴으로 끝날 때 매치
\특수문자
: 이스케이프. \ 뒤의 특수 문자를 정규표현식이 아닌 일반 문자로 인식
.
: 모든 문자
[문자들]
: 대괄호가 문자 하나를 의미. 예를 들어, [A-C][0-3][g-i]
는 A0g, A0h, B1i, C3g 등의 문자열과 일치합니다.
시작문자-끝문자
: 문자의 범위. 가령 [A-C]
는 A부터 C
^
: 부정을 의미. [^A-C]
는 A부터 Z를 제외한 모든 문자.
(서브1|서브2)
: 소괄호 안에는 파이프로 분리한 문자들을 넣을 수 있음.
수량자
*
: 별표 앞 문자가 패턴에 없을 수도 있고 여러개 있을 수도 있음. 가령 a*b
는 aaab
, ab
와 b
와도 일치함.
+
: 플러스 앞 문자가 패턴에 하나 이상 있음. 가령 a+b
는 aaab
, ab
와는 일치하지만 b
와는 일치하지 않음.
?
: 물음표 앞 문자가 패턴에 하나 이하 있음. 가령 a?b
는 b
, ab
와는 일치하지만 aab
와는 일치하지 않음.
{}
: 중괄호 안에 있는 숫자의 범위만큼 일치. [a-z]{3,}
는 소문자가 3개 이상, [abc]{1,2}
는 a 혹은 b 혹은 c가 1개 혹은 2개 연결됨.
*
, +
, {n, }
는 탐욕적 수량자이다. 최대한 많은 문자를 모두 찾아낸다.
탐욕적 수량자?
는 게으른 수량자이다. 최소한 적게 일치하는 패턴. 가령 r.*
은 r로 시작하는 최대한 긴 문장이지만, r.*?
은 r로 시작하는 가장 짧은 문자. 즉 가장 처음만나는 r을 의미한다.
플래그
i
: 대소문자 구분 없음
g
: 패턴과 일치하는 모든 값을 검색
m
: 다중행 모드. 템플릿 리터럴에서 유용함.
s
: \n
도 .
문자에 포함될 수 있도록.
u
: 유니코드
y
: sticky