고급 지식
이 섹션에서는 문자열 내부에 대해 더 자세히 설명합니다. 이 지식은 이모티콘, 희귀한 수학 또는 상형 문자 또는 기타 희귀한 기호를 다룰 계획이라면 유용할 것입니다.
이미 알고 있듯이 JavaScript 문자열은 유니코드를 기반으로 합니다. 각 문자는 1-4바이트의 바이트 시퀀스로 표시됩니다.
JavaScript를 사용하면 다음 세 가지 표기법 중 하나로 16진수 유니코드 코드를 지정하여 문자열에 문자를 삽입할 수 있습니다.
xXX
XX
00
에서 FF
사이의 값을 갖는 두 개의 16진수여야 하며, xXX
유니코드 코드가 XX
인 문자입니다.
xXX
표기법은 두 개의 16진수 숫자만 지원하므로 처음 256개의 유니코드 문자에만 사용할 수 있습니다.
처음 256자에는 라틴 알파벳, 대부분의 기본 구문 문자 및 기타 문자가 포함됩니다. 예를 들어, "x7A"
"z"
(유니코드 U+007A
)와 동일합니다.
경고( "x7A" ); // z 경고( "xA9" ); // ©, 저작권 기호
uXXXX
XXXX
0000
에서 FFFF
사이의 값을 갖는 정확히 4자리 16진수여야 하며, uXXXX
는 유니코드 코드가 XXXX
인 문자입니다.
U+FFFF
보다 큰 유니코드 값을 가진 문자도 이 표기법으로 표현할 수 있지만 이 경우 소위 대리 쌍을 사용해야 합니다(대리 쌍에 대해서는 이 장의 뒷부분에서 설명하겠습니다).
경고( "u00A9" ); // ©, xA9와 동일, 4자리 16진수 표기법 사용 경고( "u044F" ); // я, 키릴 문자 경고( "u2191" ); // ↑, 위쪽 화살표 기호
u{X…XXXXXX}
X…XXXXXX
0
~ 10FFFF
(유니코드로 정의된 가장 높은 코드 포인트) 사이의 1~6바이트의 16진수 값이어야 합니다. 이 표기법을 사용하면 기존의 모든 유니코드 문자를 쉽게 표현할 수 있습니다.
경고( "u{20331}" ); // 佫, 희귀한 한자(긴 유니코드) 경고( "u{1F60D}" ); // ?, 웃는 얼굴 기호(또 다른 긴 유니코드)
자주 사용되는 모든 문자에는 2바이트 코드(4자리 16진수)가 있습니다. 대부분의 유럽 언어, 숫자 및 기본 통합 CJK 표의 문자 집합(CJK – 중국어, 일본어 및 한국어 쓰기 시스템)의 문자는 2바이트로 표시됩니다.
처음에 JavaScript는 문자당 2바이트만 허용하는 UTF-16 인코딩을 기반으로 했습니다. 그러나 2바이트는 65536개의 조합만 허용하며 이는 가능한 모든 유니코드 기호에 충분하지 않습니다.
따라서 2바이트 이상이 필요한 드문 기호는 "대리 쌍"이라는 2바이트 문자 쌍으로 인코딩됩니다.
부작용으로 이러한 기호의 길이는 2
입니다.
경고( '?'.length ); // 2, 수학 스크립트 대문자 X 경고( '?'.length ); // 2, 기쁨의 눈물을 흘리는 얼굴 경고( '?'.length ); // 2, 희귀한 한자
그 이유는 JavaScript가 생성될 당시 대리 쌍이 존재하지 않았기 때문에 언어에서 올바르게 처리되지 않았기 때문입니다!
실제로 위의 각 문자열에는 단일 기호가 있지만 length
속성의 길이는 2
입니다.
대부분의 언어 기능은 서로게이트 쌍을 두 문자로 처리하기 때문에 기호를 얻는 것도 까다로울 수 있습니다.
예를 들어, 출력에서 두 개의 홀수 문자를 볼 수 있습니다.
경고( '?'[0] ); // 이상한 기호가 표시됩니다... 경고( '?'[1] ); // ...대리 쌍의 조각
서로게이트 쌍의 조각은 서로가 없으면 의미가 없습니다. 따라서 위 예의 경고는 실제로 가비지를 표시합니다.
기술적으로 서로게이트 쌍은 해당 코드로도 감지할 수 있습니다. 문자에 0xd800..0xdbff
간격의 코드가 있는 경우 이는 서로게이트 쌍의 첫 번째 부분입니다. 다음 문자(두 번째 부분)에는 0xdc00..0xdfff
간격의 코드가 있어야 합니다. 이러한 간격은 표준에 따라 서로게이트 쌍용으로만 예약되어 있습니다.
따라서 서로게이트 쌍을 처리하기 위해 String.fromCodePoint 및 str.codePointAt 메서드가 JavaScript에 추가되었습니다.
본질적으로 String.fromCharCode 및 str.charCodeAt와 동일하지만 서로게이트 쌍을 올바르게 처리합니다.
여기에서 차이점을 볼 수 있습니다.
// charCodeAt는 서로게이트 쌍을 인식하지 않으므로 ?의 첫 번째 부분에 대한 코드를 제공합니다. 경고( '?'.charCodeAt(0).toString(16) ); // d835 // codePointAt는 서로게이트 쌍을 인식합니다. 경고( '?'.codePointAt(0).toString(16) ); // 1d4b3, 서로게이트 쌍의 두 부분을 모두 읽습니다.
즉, 위치 1에서 가져오면(여기에서는 다소 부정확함) 둘 다 쌍의 두 번째 부분만 반환합니다.
경고( '?'.charCodeAt(1).toString(16) ); // dcb3 경고( '?'.codePointAt(1).toString(16) ); // dcb3 // 쌍의 의미 없는 후반부
나중에 Iterables 장에서 대리 쌍을 처리하는 더 많은 방법을 찾을 수 있습니다. 아마도 이를 위한 특별한 라이브러리도 있을 것입니다. 그러나 여기서 제안할 만큼 유명한 것은 없습니다.
요약: 문자열을 임의의 지점에서 분리하는 것은 위험합니다.
임의의 위치에서 문자열을 분할할 수는 없습니다. 예를 들어 str.slice(0, 4)
사용하여 유효한 문자열이기를 기대합니다. 예:
Alert( '안녕?'.slice(0, 4) ); // 안녕 [?]
여기서는 출력에서 가비지 문자(스마일 대리 쌍의 전반부)를 볼 수 있습니다.
대리 쌍을 사용하여 안정적으로 작업하려는 경우 이 점에 유의하십시오. 큰 문제는 아닐 수도 있지만 적어도 무슨 일이 일어나는지 이해해야 합니다.
많은 언어에는 위/아래에 표시가 있는 기본 문자로 구성된 기호가 있습니다.
예를 들어 문자 a
àáâäãåā
문자의 기본 문자가 될 수 있습니다.
가장 일반적인 "복합" 문자는 유니코드 테이블에 고유한 코드를 가지고 있습니다. 그러나 가능한 조합이 너무 많기 때문에 전부는 아닙니다.
임의의 구성을 지원하기 위해 유니코드 표준에서는 여러 유니코드 문자를 사용할 수 있습니다. 즉, 기본 문자 뒤에 이를 "장식"하는 하나 이상의 "표시" 문자가 옵니다.
예를 들어, S
뒤에 특수 "위의 점" 문자(코드 u0307
)가 오면 Ṡ로 표시됩니다.
경고( 'Su0307' ); // Ṡ
문자 위(또는 아래)에 추가 표시가 필요한 경우 문제 없습니다. 필요한 표시 문자를 추가하기만 하면 됩니다.
예를 들어, "아래 점" 문자(코드 u0323
)를 추가하면 "위와 아래에 점이 있는 S"가 됩니다: Ṩ
.
예를 들어:
경고( 'Su0307u0323' ); // Ṩ
이는 뛰어난 유연성을 제공하지만 흥미로운 문제도 있습니다. 두 문자가 시각적으로 동일해 보이지만 다른 유니코드 구성으로 표시될 수 있습니다.
예를 들어:
s1 = 'Su0307u0323'; // Ṩ, S + 위 점 + 아래 점 s2 = 'Su0323u0307'; // Ṩ, S + 아래 점 + 위 점 경고( `s1: ${s1}, s2: ${s2}` ); 경고( s1 == s2 ); // 문자가 동일해 보이지만 false(?!)
이 문제를 해결하기 위해 각 문자열을 단일 "일반" 형식으로 가져오는 "유니코드 정규화" 알고리즘이 있습니다.
이는 str.normalize()에 의해 구현됩니다.
Alert( "Su0307u0323".normalize() == "Su0323u0307".normalize() ); // 진실
우리 상황에서 normalize()
실제로 3개의 문자 시퀀스를 하나로 합친다는 것은 재밌습니다: u1e68
(S에는 두 개의 점이 있음).
경고( "Su0307u0323".normalize().length ); // 1 경고( "Su0307u0323".normalize() == "u1e68" ); // 진실
실제로는 항상 그런 것은 아닙니다. 그 이유는 기호 Ṩ
가 "충분히 일반적"이므로 유니코드 작성자가 이를 메인 테이블에 포함시켜 코드를 부여했기 때문입니다.
정규화 규칙 및 변형에 대해 자세히 알아보려면 유니코드 표준의 부록인 유니코드 정규화 형식에 설명되어 있지만 대부분의 실제 목적에서는 이 섹션의 정보로 충분합니다.