dolog
문자열 2 본문
부분 문자열(substring) 찾기
- str.indexOf(substr, pos) : substr의 pos에서 부터 시작해, 부분 문자열 substr이 어디에 위치하는지 찾아주고 찾은 위치 반환, 못찾으면 -1 반환
let str = ‘Widget with id’;
console.log(str.indexOf(‘Widget’)); // 0
console.log(str.indexOf(‘widget’)); // -1, 대/소문자를 따지기 때문에
console.log(str.indexOf(‘id’)); // 1, Widget에서 id로 1번째 위치로 파악함
str.indexOf(substr, pos)의 두번째 매개변수 pos를 사용하면 검색이 해당 위치부터 시작된다
let str = ‘Widget with id’;
console.log(str.indexOf(‘id’, 2)); // 12
// 이 때 순서는 0에서 부터 시작해 세기 시작하며, 띄어쓰기도 포함하여 센다
let str = "As sly as a fox, as strong as an ox";
let target = “as”;
let pos = -1;
while ((pos = str.indexOf(target, pos +1)) != -1) {
console.log(`위치: ${pos}`); // 7, 17, 27
}
- str.lastIndexOf(subsets, pos) : str.indexOf와 비슷한 기능을 하는 매서드이지만 문자열 끝에서 부터 부분 문자열을 찾는 다는 점이 다르다
- 반환되는 부분 문자열 위치는 문자열 끝이 기준이다
✓ if 문의 조건식에 str.indexOf을 쓸 때 주의해야 할 점
let str = ‘Widget with id’;
if (str.indexOf(“Widget”)) {
console.log(“Get!”); // 의도한 대로 동작 X
}
// 0을 반환하기 때문에 false로 인식해 출력이 되지 않는 것
제대로 출력하려면 ⬇️
let str = ‘Widget with id’;
if (str.indexOf(“Widget” != -1)) {
console.log(“Get!”); // 의도한 대로 동작한다
}
비트 NOT 연산자(~)를 사용한 기법
- 비트 NOT 연산자(~) : 피연산자를 32비트 정수로 바꾼 후(소수부는 모두 버려짐) 모든 비트를 반전한다
- n이 32비트 정수일 때 ~n은 -(n+1)이 된다
- 아주 큰 숫자에 사용할 경우 0이 출력된다
console.log( ~2 ); // -3, -(2+1)과 같음
console.log( ~1 ); // -2, -(1+1)과 같음
console.log( ~0 ); // -1, -(0+1)과 같음
console.log( ~-1 ); // 0, -(-1+1)과 같음, 0으로 만드는 유일한 경우
str.indexOf가 -1을 반환하지 않을 경우 검사할 수 있다 ⬇️
let str = ‘Widget’;
if (~str.indexOf(‘Widget’)) {
console.log(“Get!”);
}
// 추천하는 코드는 아니지만 이러한 패턴 코드를 만나면(if(~str.indexOf)) 부분 문자열인지 확인 하는 코드라고 기억해 두자
includes, startsWith, endsWith
- str.includes(substr, pos) : 부분 문자열이 있는지에 따라 true/false 반환
// 위치보단 포함 여부를 알고 싶을 때 사용한다
console.log(‘Widget with id’.includes(‘Widget’)); // true
console.log(‘Hello’.includes(‘Bye’)); // false
// 두번째 인수 pos 값을 주면 해당 위부터 부분 문자열을 검색한다
console.log(‘Widget with id’.includes(‘id’)); // true
console.log(‘Widget with id’.includes(‘id’, 3)); // false
- str.startsWith, str.endsWith : 문자열이 특정 문자열로 시작하는 지 여부와 특정 문자열로 끝나는 지 여부를 확인할 때 사용
console.log(‘Widget’.startsWith(“Wid”)); // true
console.log(‘Widget’.endsWith(“get”)); // true
부분 문자열 추출하기 - 3가지 매서드 : substring, substr, slice
- str.slice(start [, end]) : 문자열의 start ~ end(미 포함)까지 반환
let str = “stringify”;
console.log(str.slice(0, 5)); // strin ➡️ 0,1,2,3,4
console.log(str.slice(0, 1)); // s ➡️ 0
// end가 생략되면 명시한 위치부터 끝까지 반환
console.log(str.slice(2)); // ringify
// 음수로 인수를 주면 문자열 끝에서부터 카운팅 시작
console.log(str.slice(-4, -1)); // gif
- str.substring(start [, end]) : start와 end 사이에 있는 문자열 반환
*str.slice와 비슷하지만 다른 점으론 start 인수가 end 보다 커도 된다는 점이다
let str = “stringify”;
// 동일한 부분 문자열을 반환
console.log(str.substring(2, 6)); // ring
console.log(str.substring(6, 2));// ring
// 음수는 허용하지 않으므로 0으로 처리된다
- str.substr(start [, length]) : start에서 시작해 length개의 글자를 반환
*브라우저 이외의 호스트 환경에서는 제대로 동작하지 않을 수 있다(…)
let str = “stringify”;
console.log(str.substr(2, 4)); // ring
// start가 음수면 뒤에서 부터 개수 계산
console.log(str.substr(-4, 2)); // gi
문자열 비교하기
- 알파벳 순서를 기준으로 글자끼리 비교한다
그러나 주의할 점)
- 항상 ( 대문자 < 소문자 )
console.log( a > Z ); // true
- 발음 구별 기호(diacritical mark)가 붙은 문자는 알파벳 순서를 따르지 않는다
console.log( 'Österreich' > 'Zealand' ); // true
왜 그런걸까?
모든 문자열은 UTF-16을 사용해 인코딩된다
UTF-16에선 모든 글자가 숫자 형식의 코드와 매칭되는데 코드로 글자를 얻거나 글자에서 연관 코드를 알 수 있는 매서드가 있다
- str.codePointAt(pos) : pos에 위치한 글자의 코드(숫자)를 반환
// 글자는 같지만 케이스가 달라 반환되는 코드가 다름
console.log(“z”.codePointAt(0)); // 122
console.log(“Z”.codePointAt(0)); // 90
- String.fromCodePoint(code) : 숫자 형식의 코드에 대응하는 글자를 만들어준다
console.log(String.fromCodePoint(90)); // Z
- \uXXXX(16진수)로 글자를 만들 수 있다
console.log(‘\u005a’); // Z
65와 220 사이에 대응하는 글자 출력 예시 ⬇️
let str = ‘’;
for (let i =65 ; i <= 220 ; i++) {
str += String.fromCodePoint(i);
}
console.log(str);
문자열 제대로 비교하기
- 언어마다 문자 체계가 다르다
- 페이지에서 어떤 언어를 사용하고 있는지 브라우저가 알아야 한다
- 대부분의 브라우저는 국제 표준 ECMA-402를 지원한다
- ECMA-402엔 언어가 다를 때 적용 가능한 문자열 비교 규칙, 매서드가 정의되어 있다
- str.localeCompare(str2) ➡️ ECMA-402에서 정의한 규칙에 따라 str이 str2 보다 작은지, 같은지, 큰지 나타내주는 정수 반환
- str < str2 : 음수 반환
- str = str2 : 0을 반환
- str > str2 : 양수 반환
console.log( 'Österreich'.localeCompare('Zealand') ); // -1, (Ö < Z)
*더 자세한 내용은 필요할 때 MDN을 참고하라
문자열 심화
- 서로 게이트 쌍(surrogate pair)
*JS에선 존재하지 않았기에 제대로 처리하지 못한다
- 사용하게 된 배경)
대부분의 글자는 모두 2byte 코드를 가지고 있다 : 유럽권에서 사용하는 글자, 숫자, 상형 문자 대다수가 2byte 표현 체계 사용
그러나 2byte = 65,536(2^16)개의 조합밖에 만들어내지 못하기 때문에 모든 기호를 표현하기엔 부족하다
극복하기 위해 사용 빈도가 낮은 서로게이트 쌍(surrogate pair)이라 불리는 2byte 글자들의 쌍을 사용해 인코딩한다(길이는 2)
console.log( '𝒳'.length ); // 2, 수학에서 쓰이는 대문자 X(그리스 문자 카이 - 옮긴이)
console.log( '😂'.length ); // 2, 웃으면서 눈물 흘리는 얼굴을 나타내는 이모티콘
console.log( '𩷶'.length ); // 2, 사용 빈도가 낮은 중국어(상형문자)
- String.fromCodePoint와 str.codePointAt가 서로게이트 쌍을 처리할 수 있는 몇 안되는 매서드
- 0xd800..0xdbff 사이에 있는 코드는 서로게이트 쌍을 구성하는 첫번째 글자를 나타내고 0xdc00..0xdfff 두번째 글자는 이 사이에 있어야 한다(서로게이트 쌍을 위해 일부러 비워놓은 코드)
발음 구별 기호와 유니코드 정규화
a를 베이스 글자로 àáâäãåā 같은 합성 글자들은 UTF-16에서 독자적인 코드를 가진다 하지만 너무 많기 때문에 모든 합성 글자에 부여되진 않는다
그래서 베이스 글자에 붙일 수 있도록 여러 개의 유니코드 문자를 넣으면 된다
console.log( ’S\u0307’ ); // Ṡ
// 위, 아래 둘다 추가 가능
console.log( 'S\u0307\u0323' ); // Ṩ
let s1 = 'S\u0307\u0323'; // Ṩ, S + 윗 점 + 아랫 점
let s2 = 'S\u0323\u0307'; // Ṩ, S + 아랫 점 + 윗 점
console.log( `s1: ${s1}, s2: ${s2}` );
console.log( s1 == s2 ); // 눈으로 보기엔 같은 글자이지만 동등 비교 시 false가 반환된다
이러한 문제 해결을 위해 유니코드 정규화(unicode normalization) 알고리즘 사용 - str.normalize() ⬇️
console.log( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
// 위, 아래 점을 다 붙인 사례에서 str.normalize을 사용하면 글자가 하나로 합쳐진다
console.log( "S\u0307\u0323".normalize().length ); // 1
console.log( "S\u0307\u0323".normalize() == "\u1e68" ); // true
추가로 문자열에 사용하는 매서드)
- str.trim() : 문자열 앞과 끝의 공백을 제거해준다
- str.repeat(n) : 문자열을 n번 반복한다
- etc.