Unicode:修饰符“u”和class\p{…}
JavaScript使用Unicode编码(Unicodeencoding)对字符串进行编码。大多数字符使用2个字节编码,但这种方式只能编码最多65536个字符。
这个范围不足以对所有可能的字符进行编码,这就是为什么一些罕见的字符使用4个字节进行编码,比如?
(数学符号X)或者?
(笑脸),一些象形文字等等。
以下是一些字符对应的unicode编码:
所以像a
和≈
这样的字符占用2个字节,而?
,?
和?
的对应编码则更长,它们具有4个字节的长度。
很久以前,当JavaScript被发明出来的时候,Unicode的编码要更加简单:当时并没有4个字节长的字符。所以,一部分语言特性在现在仍旧无法对unicode进行正确的处理。
比如length
认为这里的输入有2个字符:
alert('?'.length);//2alert('?'.length);//2
…但我们可以清楚地认识到输入的字符只有一个,对吧?关键在于length
把4个字节当成了2个2字节长的字符。这是不对的,因为它们必须被当作一个整体来考虑。(即所谓的“代理伪字符”(surrogatepair))。
默认情况下,正则表达式同样把一个4个字节的“长字符”当成一对2个字节长的字符。正如在字符串中遇到的情况,这将导致一些奇怪的结果。。
与字符串有所不同的是,正则表达式有一个修饰符u
被用以解决此类问题。当一个正则表达式使用这个修饰符后,4个字节长的字符将被正确地处理。同时也能够用上Unicode属性(Unicodeproperty)来进行查找了。我们接下来就来了解这方面的内容。
Unicode属性(Unicodeproperties)\p{…}
在Firefox和Edge中缺乏支持
尽管unicodeproperty从2018年以来便作为标准的一部分,但unicode属性在Firefox(bug)和Edge(bug)中并没有相应的支持。
目前XRegExp这个库提供“扩展”的正则表达式,其中包括对unicodeproperty的跨平台支持。
Unicode中的每一个字符都具有很多的属性。它们描述了一个字符属于哪个“类别”,包含了各种关于字符的信息。
例如,如果一个字符具有Letter
属性,这意味着这个字符归属于(任意语言的)一个字母表。而Number
属性则表示这是一个数字:也许是阿拉伯语,亦或者是中文,等等。
我们可以查找具有某种属性的字符,写作\p{…}
。为了顺利使用\p{…}
,一个正则表达式必须使用修饰符u
。
举个例子,\p{Letter}
表示任何语言中的一个字母。我们也可以使用\p{L}
,因为L
是Letter
的一个别名(alias)。对于每种属性而言,几乎都存在对应的缩写别名。
在下面的例子中3种字母将会被查找出:英语、格鲁吉亚语和韩语。
letstr="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
:- 十进制数字(decimaldigit)
Nd
, - 字母数字(letternumber)
Nl
, - 其它(other)
No
。
- 十进制数字(decimaldigit)
- 标点符号(Punctuation)
P
:- 链接符(connector)
Pc
, - 横杠(dash)
Pd
, - 起始引用号(initialquote)
Pi
, - 结束引用号(finalquote)
Pf
, - 开(open)
Ps
, - 闭(close)
Pe
, - 其它(other)
Po
。
- 链接符(connector)
- 标记(Mark)
M
(accentsetc):- 间隔合并(spacingcombining)
Mc
, - 封闭(enclosing)
Me
, - 非间隔(non-spacing)
Mn
。
- 间隔合并(spacingcombining)
- 符号(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
, - 未分配(notassigned)
Cn
, - 私有(privateuse)
Co
, - 代理伪字符(surrogate)
Cs
。
- 控制符(control)
因此,比如说我们需要小写的字母,就可以写成\p{Ll}
,标点符号写作\p{P}
等等。
也有其它派生的类别,例如:
Alphabetic
(Alpha
),包含了字母L
,加上字母数字Nl
(例如Ⅻ–罗马数字12),加上一些其它符号Other_Alphabetic
(OAlpha
)。Hex_Digit
包括16进制数字0-9
,a-f
。- …等等
Unicode支持相当数量的属性,列出整个清单需要占用大量的空间,因此在这里列出相关的链接:
- 列出一个字符的所有属性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.
- 以文本格式整理的所有Unicode字符,包含了所有的属性:https://www.unicode.org/Public/UCD/latest/ucd/.
实例:16进制数字
举个例子,让我们来查找16进制数字,写作xFF
其中F
是一个16进制的数字(0…9或者A…F)。
一个16进制数字可以表示为\p{Hex_Digit}
:
letregexp=/x\p{Hex_Digit}\p{Hex_Digit}/u;alert("number:xAF".match(regexp));//xAF
实例:中文字符
让我们再来考虑中文字符。
有一个unicode属性Script
(一个书写系统),这个属性可以有一个值:Cyrillic
,Greek
,Arabic
,Han
(中文)等等,这里是一个完整的列表。
为了实现查找一个给定的书写系统中的字符,我们需要使用Script=<value>
,例如对于西里尔字符:\p{sc=Cyrillic}
,中文字符:\p{sc=Han}
,等等。
letregexp=/\p{sc=Han}/gu;//returnsChinesehieroglyphsletstr=`HelloПривет你好123_456`;alert(str.match(regexp));//你,好
实例:货币
表示货币的字符,例如$
,€
,¥
,具有unicode属性\p{Currency_Symbol}
,缩写为\p{Sc}
。
让我们使用这一属性来查找符合“货币,接着是一个数字”的价格文本:
letregexp=/\p{Sc}\d/gu;letstr=`Prices:$2,€1,¥9`;alert(str.match(regexp));//$2,€1,¥9
总结
修饰符u
在正则表达式中提供对Unicode的支持。
这意味着两件事:
- 4个字节长的字符被以正确的方式处理:被看成单个的字符,而不是2个2字节长的字符。
- Unicode属性可以被用于查找中
\p{…}
。
有了unicode属性我们可以查找给定语言中的词,特殊字符(引用,货币)等等。
引用文章:https://zh.javascript.info/regexp-unicode