문자열

김하은·2023년 5월 13일
0

자바스크립트에는 글자 하나만 저장할 수 있는 별도의 자료형은 없다.
텍스트 형식의 데이터는 길이와 상관없이 문자열형태로 저장된다.

자바스크립트에서 문자열은 헤이지 인코딩 방식과 상관없이 항상 UTF-16형식을 따른다.

따옴표

문자열은 작은따옴표나 큰따옴표, 벡틱으로 감쌀 수 있다.

let single = '문자열입니다';
let double = "문자열입니다";
let backtick = `문자열입니다`;

작은따옴표와 큰따옴표는 기능상의 차이가 없으나, 벡틱에는 특별한 기능이 있다.
표현식을 ${}으로 감싸고 벡특으로 감싼 문자열 중간에 넣어주면 해당 표현식을 문자열 중간에 쉽게 삽입할 수 있다.
=> 이러한 방식을 템플릿 리터럴 이라고한다.

벡틱을 사용하면 문자열을 여러줄에 걸쳐 작성할 수도 있다.

let guestList = `손님:
 * John
 * Pete
 * Mary
`;

alert(guestList);

작은 따옴표나 큰 따옴표를 사용 시에는 이것이 불가능하다. => 에러가 발생한다.

벡틱안에는 표현식 뿐만아니라 변수를 넣을 수도 있다.

특수 기호

줄바꿈 문자라고 불리는 특수기호 \n 을 사용하면 작은 따옴표나 큰 따옴표로도 여러줄의 문자열을 만둘 수 있따.

따옴표와 \n을 이용해 만든 여러줄의 문자열과 백틱을 이용해 만든 여러줄의 문자열은 표현방식만 다를 뿐 차이가 없다.

유니코드를 사용=>

alert( "\u00A9" ); // ©
alert( "\u{20331}" ); // 佫, 중국어(긴 유니코드)
alert( "\u{1F60D}" ); // 😍, 웃는 얼굴 기호(긴 유니코드)

모든 특수문자는 이스케이프 문자라고도 불리는 역슬래시로 시작한다.

역슬래시는 문자열 내에 따옴표를 넣을 때에도 사용할 수 있다.

alert( 'I\'m the Walrus!' ); // I'm the Walrus!

이렇게 같은 문자열 내에 따옴표에는 반드시 역슬래시를 붙여주어야하는데 자바스크립트에서 해당 따옴표가 문자열을 닫는 용도로 사용된 것이라 잘못 해석할 수도 있기 때문이다.

이렇게 이스케이프 문자를 붙이는 것보다 문자열 전체를 벡틱으로 감싸는 것이 더 우아해보인다.

역슬래시는 문자열을 정확하게 읽기 위해 만들어졌다. 따라서 제 역할을 다하면 사라진다. 이로인해 메모리에 저장되는 문자열에는 존재하지 않는다!!

문자열의 길이

length 프로퍼티에는 문자열의 길이가 저장된다.

alert('My\n'.length)

이것을 실행해보면 3이나온다. \n이 특수문자 하나로 취급되기 때문이다.

length는 프로퍼티이다.
함수가 아니고 숫자가 저장되는 프로퍼티이기에 뒤에 소괄호를 붙일 필요는 없다.

특정 글자에 접근하기

문자열 내 특정 위치에 있는 글자에 접근하려면 대괄호를 이용하거나 특정 메서드를 활용하면 된다.

let str = `Hello`;

// 첫 번째 글자
alert( str[0] ); // H
alert( str.charAt(0) ); // H

// 마지막 글자
alert( str[str.length - 1] ); // o

주로 대괄호를 활용하는 방식을 많이 사용하게 된다.
(charAt은 하위 호환성을 위해 남아있는 메서드이다.)

두 접근 방식은 차이가 있다. 반환할 글자가 없을 때 드러난다.

접근하려는 위치에 글자가 없는경우 대괄호를 사용했었을 때에는 undefined를 , charAt은 빈문자열을 반환한다.

for...of를 사용하면 문자열을 구성하는 글자를 대상으로 반복작업을 할 수 있다.

for (let char of "Hello") {
  alert(char); // H,e,l,l,o (char는 순차적으로 H, e, l, l, o가 됩니다.)
}

문자열의 불변성

문자열은 수정이 불가능하다.(문자열 중간의 글자하나를 바꾸는것..)

Hi라는 문자열에 H를 소문자 h로 바꾸고 싶다고 해서 인덱스로 접근해 재할당해 변경하는 것은 동작하지 않는다.
이러한 문제를 해결하기 위해선 완전히 새로운 문자열을 하나 더 만든다음 이 문자열을 할당하는 방식을 사용할 수 있다.

let str = 'Hi';

str = 'h' + str[1]; // 문자열 전체를 교체함

alert( str ); // hi

대*소문자 변경하기

메서드 toLowerCase()를 사용하면 대문자를 소문자로 변경이 가능하고,
toUpperCase()를 사용하면 소문자를 대문자로 변경이 가능하다.

글자 하나의 케이스만 변경하는 것도 가능하다.

부분 문자열 찾기

문자열에서 부분문자열을 찾는 것도 가능하다.

str.indexOf("원하는 문자열")

찾고자하는 문자열이 있는 인덱스를 반환하는 메서드이다.
해당 문자열이 없다면 -1을 반환한다.

첫번째 매개변수외 두번째 매개변수도 있는데 두번째 매개변수를 명시하게되면 검색이 해당 위치부터 시작된다.

str.lastIndexOf(substr, position)
str.lastIndexOf(substr, position)는 indexOf와 유사한 기능을 하는 메서드입니다. 문자열 끝에서부터 부분 문자열을 찾는다는 점만 다릅니다.

반환되는 부분 문자열 위치는 문자열 끝이 기준입니다.

if 문의 조건식에서 indexOf를 쓸때 주의해야하는 점이 있다.

let str = "Widget with id";

if (str.indexOf("Widget")) {
    alert("찾았다!"); // 의도한 대로 동작하지 않습니다.
}

str.indexOf("Widget") 이것은 0을 반환하는데 0은 거짓같은 값에 들어가게되기때문에 if문이 동작하지 않는다.따라서 부분문자열을 검사하려면 -1과 비교해야한다.

let str = "Widget with id";

if (str.indexOf("Widget") != -1) {
    alert("찾았다!"); // 의도한 대로 동작합니다.
}

이렇게 말이다.

비트 NOT연산자를 사용한 기법

비트 NOT연산자 ~ 를 사용한 기법이 있다.
비트 NOT연산자는 피연산자를 32비트 정수로 바꾼 뒤 (소수부분은 다 버림) 모든 비트를 반전하낟.

따라서 n이 32비트 정수라면 ~n은 -(n + 1)이다.

..

같은 논리로 ~2 는 -(2 + 1)이기에 -3이라고 할 수 있겠다.
~-1은 -(-1+1) => 0

부호가 있는 32비트 정수 n중 ~n을 0으로 만드는 경우는 n == -1일때가 유일하다.

이것을 사용해서 -1을 반환하지 않는 경우를 만들 수 있다.

if (~str.indexOf("Widget")) {
  alert( '찾았다!' ); // 의도한 대로 동작합니다.
}

이렇게되면 index가 0이 나오게 되면 비트 연산자에 의해 -(0 +1)로 -1 이되니 if문이 우리가 원하는 대로 작동한다.

이렇게 언어 특유의 기능을 사용해 직관적이지 않은 코드를 작성하는 것은 권장하지 않는다.
그러나 이러한 기법은 오래된 스크립트에서 쉽게 발견될 수 있기때문에 알아두는것이 좋다.

if (~str.indexOf(...))

같은 코드를 만나면 일단 부분문자열인지 확인하는 코드라고 알아두자.
다만 주의해야할것은 문자열이 아주 길지 않은 경우에만 ~ 연산자가 의도한대로 작동한다는것을 명심하자

includes.startsWith,endsWith

비교적 최근에 나온 메서드인 str.includes(substr,pos)는 str에 부분문자열 substr이 있는지에 따라 true나 false를 반환한다.

부분문자열의 위치 정보는 필요하지 않고 해당 문자열이 포함되어있는지 여부만 알고 싶을 때 적합하다.

str.inclues도 str.indexOf처럼 두번째 인수를 넘기면 해당 위치부터 부분문자열을 검색한다.

메서드 str.startsWith와 str.endsWith는 메서드 이름 그대로 문자열 str이 특정 문자열로 시작하는지(start with) 여부와 특정 문자열로 끝나는지(end with)여부를 알기위헤 사용할 수 있다.

부분 문자열 추출하기

자바스크립트에는 부분 문자열 추출과 관련된 메서드가 세가지 있다.


  • str.slice(start,[end])

start부터 end까지 반환하는데 end는 포함되지 않는다.

두번째 인수가 생략된 경우, 명시한 위치부터 문자열 끝까지 반환한다.

start와 end가 음수가 될 수도 있다.
음수를 넘기면 문자열 끝에서 부터 카운팅을 시작한다.

let str = "stringify";

// 끝에서 4번째부터 시작해 끝에서 1번째 위치까지
alert( str.slice(-4, -1) ); // gif

  • str.substring(start,[end])
    start와 end사이에 있는 문자열을 반환한다.

substring은 slice와 유사하지만 start가 end보다 커도 괜찮다.
다만, substring은 음수 인수를 허용하지 않아 음수는 0으로 치환된다.


  • str.substr(start, [length])

start부터 시작해 length개의 글자를 반환한다.

길이를 기준으로 문자열을 추출한다는 점에서 위의 두가지와 차이가 있다.

첫번째 인수가 음수라면 뒤에서 부터 개수를 센다.


substr의 경우 구식 스크립트에 대응하기 위해 남겨둔 브라우저 전용 기능들을 명시해 놓은 부록에 정의 되어있다. 거의 모든 곳에서 작동하기는 하지만 브라우저 외의 호스트 환경에서는 제대로 작동하지 않을 수 있다는 단점이 있다.

남은 두 메서드 중 slice가 음수 인수를 허용하기 때문에 substring보다 조금 더 유연하다.

문자열 비교하기

문자열을 비교할 때에는 알파벳 순서를 기준으로 글자끼리 비교가 이루어진다.

  • 소문자는 대문자보다 항상크다.
  • 발음 구별기호가 붙은 문자는 알파벳 순서 기준을 따르지 않는다.

이러한 예외들로 이름순으로 국가를 나열할 때 예상치 못한 결과가 나올 수 있다.

모든 문자열은 UTF-16을 이용해 인코딩되는데 이것에서는 모든 글자가 숫자형식의 코드와 매칭이된다.
연관 코드를 알아낼 수 있는 메서드는 str.codePointAt(pos)이다.
pos인덱스에 위치한 코드를 반환한다.

숫자형식의 code에 대항하는 글자를 만들어 주는 String.fromCodePoint(code)

// 글자는 같지만 케이스는 다르므로 반환되는 코드가 다릅니다.
alert( "z".codePointAt(0) ); // 122
alert( "Z".codePointAt(0) ); // 90

alert( String.fromCodePoint(90) ); // Z

\u뒤에 특정 글자에 대항하는 16진수 코드를 붙이는 방식으로도 원하는 글자를 만들 수 있다.

크기비교.
대문자가 먼저 나오고 그후 소문자가 나온다. 발음기호가 붙은 문자는 거의 마지막에 나온다.
이 말은 소문자의 코드가 대문자보다 크다는 말이고 발음기호의 코드는 소문자의 코드보다 더 크다는 말이다

문자열 제대로 비교하기

언어마다 문자 체계가 다르다. 따라서 문자열을 제대로 비교하는 알고리즘을 만드는 것은 생각보다 쉽지 않다.

다행히 모던 브라우저 대부분이 국제화 관련 표준인 ECMA-402를 지원한다.

이것에는 언어가 다를 때 적용할 수 있든 문자열 비교 규칙과 이를 준수하는 메서드가 정의되어있다.

str.localeCompare(str2)를 호출하면 ECMA-402에서 정의한 규칙에 따라 str이 str2보다 큰지 , 같은지, 작은지를 나타내주는 정수가 반환된다.
str이 str2보다 크면 양수를, 작으면 음수같으면 0을 반환한다.

서로게이트 쌍

자주사용되는 글자들은 모두 2바이트 코드를 가진다.
유럽권 언어에서 사용되는 글자, 숫자, 상형문자 대다수는 2바이트 표현 체계를 사용하낟.

다만 2바이트는 2의 16승개의 조합(65,536개)밖에 만들어내지 못하기에 현존하는 기호를 모두 표현하기에는 충분하지 않다.
이를 극복하기 위해 사용빈도가 낮은 기호는 서로게이트 쌍 이라고 불리는 2바이트 글자들의 쌍을 사용해 인코딩한다.

서로게이트 쌍을 사용해 인코딩한 기호의 길이는 2이다

alert( '𝒳'.length ); // 2, 수학에서 쓰이는 대문자 X(그리스 문자 카이 - 옮긴이)
alert( '😂'.length ); // 2, 웃으면서 눈물 흘리는 얼굴을 나타내는 이모티콘

자바스크립트가 만들어졌을 때에는 서로게이트 쌍이 없었다.
따라서 자바스크립트는 서로게이트 쌍으로 표현한 기호를 제대로 처리하지 못했다.

위에서 기호는 하나이나 길이가 2인 경우가 바로 이러한 이유때문이다.

String.fromCodePoint와 str.codePointAt은 명세서에 추가된 지 얼마 안 된 메서드로, 서로게이트 쌍을 제대로 처리할 수 있는 몇 안 되는 메서드 이다.
두 메서드가 등장전에는 String.fromCharCode와 str.charCodeAt을 사용했는데 이 두개는 fromCodePoint,codePointAt과 동일하게 작동하나 서로게이트 쌍은 처리하지 못한다.

서로게이트 쌍은 두글자로 취급되기에 기호를 가져오기가 까다롭다.
서로게이트 쌍을 구성하는 글자들은 붙어있을때만 의미가 있다. 인덱스0 이나 1 이런식으로 하나만 뽑아 오려고 하면 이상한 기호가 출력된다고 한다.

기술적으로 서로게이트 쌍은 서로게이트 쌍에 대응하는 코드를 사용해 감지할 수 있다. 글자의 코드가 0xd800..0xdbff 사이에 있으면 이 코드는 서로게이트 쌍을 구성하는 첫 번째 글자를 나타낸다는 것을 알 수 있다.
두 번째 글자의 코드는 반드시 0xdc00..0xdfff 사이에 있어야 한다.

발음 구별 기호, 유니코드 정규화

여러 언어에서 베이스가 되는 글자 위쪽이나 아래에 발음 구별 기호라는 기호를 붙여 글자를 만든다.

a를 베이스 글자로, àáâäãåā를 만드는 것 같이 ..

이런 합성 글자 대부분은 UTF-16테이블에서 독자적인 코드를 갖는다. 그러나 모든 합성 글자에 코드가 부여되지는 않는다.
(조합가능한 글자 수가 많기때문)

임의이ㅡ 조합을 지원하기 위해 UTF-16에서는 몇개의 유니코드 문자를 남겨두었다.
베이스 글자 뒤에 하나 또는 여러개의 유니코드 문자를 붙여 베이스 글자를 꾸밀 수 있다.

이러한 방식은 유연성을 제공하지만 눈으로 봤을때는 같은 글자이나 유니코드 조합이 다를 수 있다는 단점도 있다.
문제를 해결하기위해 유니코드 정규화라고 불리는 알고리즘을 사용해 각 문자열을 동일한 형태로 정규화 해야한다.

유니코드 정규화 알고리즘은 str.normalize()에 구현되어 있다.

S 위, 아래에 점을 붙이는 사례에선 normalize()를 사용하면 세 개의 글자가 하나로 합쳐집니다. Ṩ를 나타내는 유니코드 \u1e68로.

항상 이렇지는 않다 . 충분히 나타날 수 있는 위와 같은 사레는 UTF-16테이블에 포함하고 코드를 부여해놓았기 때문이다.

출처: 모던 자바스크립트 튜토리얼

0개의 댓글