자바스크립트는 문자열에 유니코드 인코딩을 사용합니다. 대부분의 문자는 2바이트로 인코딩되어있는데 2바이트로는 최대 65,536개의 글자밖에 표현할 수 없습니다.
65,536자는 모든 글자를 인코딩하기에는 부족한 숫자입니다. 그래서 일부 문자는 4바이트로 인코딩되어있습니다. 예를 들면 𝒳(수학에서 사용하는 X)나 😄(웃는 표정), 일부 상형 문자 등이 있죠.
다음은 일부 문자의 유니코드 값입니다.
| 문자 | 유니코드 | 유니코드의 바이트 수 |
|---|---|---|
| a | 0x0061 |
2 |
| ≈ | 0x2248 |
2 |
| 𝒳 | 0x1d4b3 |
4 |
| 𝒴 | 0x1d4b4 |
4 |
| 😄 | 0x1f604 |
4 |
보다시피 a나 ≈같은 문자는 2바이트를 차지하고 𝒳, 𝒴, 😄같은 문자는 코드값이 더 길고 4바이트를 차지합니다.
오래전에 자바스크립트 언어가 탄생했을 때는 유니코드 인코딩은 지금보다 단순했습니다. 4바이트 문자가 없었죠. 그래서 일부 언어 기능은 아직도 이런 문자들을 정확하게 다루지 못합니다.
그중 하나로 length는 다음 경우에서 문자가 두 개 있다고 봅니다.
alert('😄'.length); // 2
alert('𝒳'.length); // 2
하지만 분명 문자는 하나잖아요? 여기서 중요한 것은 length는 4바이트 문자를 2바이트 문자 2개로 취급한다는 것입니다. 4바이트를 하나로 묶어서 취급해야 하므로 올바르지 않은 결과입니다.(이런 문자를 '서로게이트 쌍’이라고 합니다. 문자열에서 서로게이트 쌍에 대해 읽어볼 수 있습니다.)
기본적으로는 정규 표현식도 4바이트의 '긴 문자’를 2바이트 문자 2개로 취급합니다. 문자열의 경우처럼 이런 방식은 잘못된 결과로 이어질 수 있습니다. 나중에 Sets and ranges [...]에서 다시 알아볼 것입니다.
문자열과 다르게 정규 표현식에는 이런 문제를 해결할 수 있는 u 플래그가 있습니다. u 플래그를 사용하면 정규식은 4바이트 문자를 올바르게 처리합니다. 그리고 유니코드 프로퍼티(Unicode property)를 사용한 검색이 가능해집니다. 바로 알아보죠.
유니코드 프로퍼티 \p{…}
유니코드의 모든 문자는 다양한 프로퍼티를 가집니다. 프로퍼티는 문자가 어떤 '범주’에 속하는지 설명하기도 하고 그 외에도 문자의 여러 가지 정보를 담고 있습니다.
예를 들어 문자에 Letter 프로퍼티가 있다면 그 문자는 어떠한 언어의 글자라는 뜻입니다. Number 프로퍼티가 있다면 아라비아 숫자든 한자 숫자든 숫자라는 뜻이죠.
\p{…}를 사용하면 프로퍼티를 통해 문자를 찾을 수 있습니다. \p{…}를 사용하기 위해서는 정규 표현식에 u 플래그가 반드시 있어야 합니다.
예시로 p{Letter}는 언어의 글자를 표기하는 방법입니다. p{L}을 대신 사용할 수도 있습니다. 여기서 L은 Letter의 약자입니다. 거의 모든 프로퍼티에 이렇게 짧게 쓸 수 있는 약자가 있습니다.
아래 예시에서는 영문자, 조지아 문자, 한글 3종류의 글자를 검색합니다.
let str = "A ბ ㄱ";
alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ
alert( str.match(/\p{L}/g) ); // null ('u' 플래그가 없어서 일치 결과 없음)
다음은 주요 문자 범주와 각각의 하위 범주 목록입니다.
- 문자(Letter)
L:- 소문자(lowercase)
Ll - 조정(modifier)
Lm - 단어의 첫 글자를 대문자로(titlecase)
Lt - 대문자(uppercase)
Lu - 기타(other)
Lo
- 소문자(lowercase)
- 숫자(Number)
N:- 10진수(decimal digit)
Nd - 문자(letter number)
Nl - 기타(other)
No
- 10진수(decimal digit)
- 문장 부호(Punctuation)
P:- 연결선(connector)
Pc - 대시(dash)
Pd - 처음 따옴표(initial quote)
Pi - 마지막 따옴표(final quote)
Pf - 열기(open)
Ps - 닫기(close)
Pe - 기타(other)
Po
- 연결선(connector)
- 표시(Mark)
M(강세 등):- 간격 결합(spacing combining)
Mc - 묶음(enclosing)
Me - 비공백(non-spacing)
Mn
- 간격 결합(spacing combining)
- 기호(Symbol)
S:- 통화(currency)
Sc - 수정(modifier)
Sk - 수학(math)
Sm - 기타(other)
So
- 통화(currency)
- 구분 기호(Separator)
Z:- 줄(line)
Zl - 단락(paragraph)
Zp - 공백(space)
Zs
- 줄(line)
- 기타(Other)
C:- 제어(control)
Cc - 형식(format)
Cf - 할당되지 않음(not assigned)
Cn - 사용자 지정(private use)
Co - 서로게이트(surrogate)
Cs
- 제어(control)
예를 들어 소문자를 찾아야 한다면 \p{Ll}을, 문장 부호를 찾아야 한다면 \p{P}를 사용하는 식으로 검색할 수 있습니다.
또한 다음과 같이 파생된 범주도 있습니다.
Alphabetic(Alpha)는 LettersL와 로마 숫자 Ⅻ같이 문자로 된 숫자Nl에 더해서Other_Alphabetic(OAlpha)에 속한 문자들을 모두 포함합니다.Hex_digit은 16진수 숫자인0-9,a-f를 포함합니다.- 이것 말고도 더 있죠.
유니코드는 정말 다양한 프로퍼티를 지원합니다. 모든 걸 나열하려면 공간이 너무 많이 필요하니 참고할 수 있는 문서를 알려드립니다.
- 문자별 프로퍼티 목록: https://unicode.org/cldr/utility/character.jsp
- 프로퍼티별 문자 목록: https://unicode.org/cldr/utility/list-unicodeset.jsp
- 프로퍼티별 줄임말: https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt
- 텍스트 형식으로 정리된 유니코드 문자와 각 문자의 모든 프로퍼티: https://www.unicode.org/Public/UCD/latest/ucd/
예시: 16진수
실제 사례로 16진수를 찾아봅시다. xFF 형식으로 쓰고 F 자리에는 16진수의 숫자(0…1이나 A…F)가 들어갑니다.
16진수 숫자는 \p{Hex_Digit}로 표기합니다.
let regexp = /x\p{Hex_Digit}\p{Hex_Digit}/u;
alert("number: xAF".match(regexp)); // xAF
예시: 한자
한자를 검색해봅시다.
Script(표기 체계)라는 유니코드 프로퍼티가 있습니다. Script는 Cyrillic(키릴 문자), Greek(그리스 문자), Arabic(아라비아 문자), Han(한자) 등의 값을 가질 수 있습니다. Script 값의 전체 목록은 여기서 볼 수 있습니다.
특정 표기 체계의 문자를 찾으려면 Script=<value>를 사용해야 합니다. 키릴 문자는 \p{sc=Cyrillic}, 한자는 \p{sc=Han}로 검색하는 식으로 쓸 수 있습니다.
let regexp = /\p{sc=Han}/gu; // 한자를 반환
let str = `Hello Привет 你好 123_456`;
alert( str.match(regexp) ); // 你,好
예시: 통화
$, €, ¥ 등 통화 단위를 나타내는 문자는 유니코드 프로퍼티 \p{Currency_Symbol}를 가지고 있습니다. 짧게는 \p{Sc}로 사용합니다.
\p{Sc}를 사용해서 ‘통화 단위 바로 뒤 숫자’ 형태의 가격 표시를 찾아봅시다.
let regexp = /\p{Sc}\d/gu;
let str = `Prices: $2, €1, ¥9`;
alert( str.match(regexp) ); // $2,€1,¥9
나중에 Quantifiers +, *, ? and {n}에서 자릿수가 여러 개인 수를 찾는 방법을 알아볼 것입니다.
요약
u 플래그로 정규 표현식에서 유니코드 관련 기능을 활성화할 수 있습니다.
즉, 다음 두 기능이 사용 가능해집니다.
- 4바이트 문자를 2바이트 문자 두 개로 처리하지 않고 문자 한 개로 올바르게 처리합니다.
\p{…}를 이용해 유니코드 프로퍼티를 검색에 사용할 수 있습니다.
유니코드 프로퍼티를 사용하면 특정 언어의 단어, 따옴표나 통화 단위같은 특수 문자 등을 모두 검색할 수 있습니다.
댓글
<code>태그를, 여러 줄로 구성된 코드를 삽입하고 싶다면<pre>태그를 이용하세요. 10줄 이상의 코드는 plnkr, JSBin, codepen 등의 샌드박스를 사용하세요.