专题 字符串 Unicode

发布于:2025-07-12 ⋅ 阅读:(16) ⋅ 点赞:(0)

你将知道:

  • 什么是 Unicode 和 UTF-16
  • charCodeAt()codePointAt() 字符串方法
  • length 属性的工作原理
  • Unicode 转义序列
  • 词典比较

Unicode 和 UTF-16

我们已经知道字符串是字符序列。

计算机处理器内部发生的一切都是数字计算。字符串处理、图像处理、音频处理、视频处理——实际上一切都围绕着数字展开。

对于字符串中的字符,每个字符也都与一个特定的数字相关联。这种关联由所使用的字符集决定。

字符集可以被认为是一个包含字符名称及其相关数字的表。

如今,计算机中已存在众多已知甚至仍在使用的字符集,每种字符集都有其自身的传承。ASCII 无疑是迄今为止最流行的字符集之一,其历史可以追溯到计算机诞生之初。

在 ASCII 中,英文字母中的小写字母 'a' 与数字 97 相关联。大写字母 'A' 与数字 65 相关联。同样,小写字母 'b' 是 98,而大写字母 'B' 是 66。空格字符是 32。

Character name

Glyph

Code

Code (hex)

Character name

Glyph

Code

Code (hex)

Space

32

0x20

Exclamation Mark

33

!

0x21

Quotation Mark

"

34

0x22

Number Sign

35

#

0x23

Dollar Sign

$

36

0x24

Percent Sign

37

%

0x25

Ampersand

&

38

0x26

Apostrophe

39

'

0x27

Left Parenthesis

(

40

0x28

Right Parenthesis

41

)

0x29

Asterisk

*

42

0x2A

Plus Sign

43

+

0x2B

Comma

,

44

0x2C

Hyphen-Minus

45

-

0x2D

Full Stop

.

46

0x2E

Solidus

47

/

0x2F

Digit Zero

0

48

0x30

Digit One

49

1

0x31

Digit Two

2

50

0x32

Digit Three

51

3

0x33

Digit Four

4

52

0x34

Digit Five

53

5

0x35

Digit Six

6

54

0x36

Digit Seven

55

7

0x37

Digit Eight

8

56

0x38

Digit Nine

57

9

0x39

Colon

:

58

0x3A

Semicolon

59

;

0x3B

Less-Than Sign

<

60

0x3C

Equals Sign

61

=

0x3D

Greater-Than Sign

>

62

0x3E

Question Mark

63

?

0x3F

Commercial At

@

64

0x40

Latin Capital Letter A

65

A

0x41

Latin Capital Letter B

B

66

0x42

Latin Capital Letter C

67

C

0x43

Latin Capital Letter D

D

68

0x44

Latin Capital Letter E

69

E

0x45

Latin Capital Letter F

F

70

0x46

Latin Capital Letter G

71

G

0x47

Latin Capital Letter H

H

72

0x48

Latin Capital Letter I

73

I

0x49

Latin Capital Letter J

J

74

0x4A

Latin Capital Letter K

75

K

0x4B

Latin Capital Letter L

L

76

0x4C

Latin Capital Letter M

77

M

0x4D

Latin Capital Letter N

N

78

0x4E

Latin Capital Letter O

79

O

0x4F

Latin Capital Letter P

P

80

0x50

Latin Capital Letter Q

81

Q

0x51

Latin Capital Letter R

R

82

0x52

Latin Capital Letter S

83

S

0x53

Latin Capital Letter T

T

84

0x54

Latin Capital Letter U

85

U

0x55

Latin Capital Letter V

V

86

0x56

Latin Capital Letter W

87

W

0x57

Latin Capital Letter X

X

88

0x58

Latin Capital Letter Y

89

Y

0x59

Latin Capital Letter Z

Z

90

0x5A

Left Square Bracket

91

[

0x5B

Reverse Solidus

\

92

0x5C

Right Square Bracket

93

]

0x5D

Circumflex Accent

^

94

0x5E

Low Line

95

_

0x5F

Grave Accent

`

96

0x60

Latin Small Letter A

97

a

0x61

Latin Small Letter B

b

98

0x62

Latin Small Letter C

99

c

0x63

Latin Small Letter D

d

100

0x64

Latin Small Letter E

101

e

0x65

Latin Small Letter F

f

102

0x66

Latin Small Letter G

103

g

0x67

Latin Small Letter H

h

104

0x68

Latin Small Letter I

105

i

0x69

Latin Small Letter J

j

106

0x6A

Latin Small Letter K

107

k

0x6B

Latin Small Letter L

l

108

0x6C

Latin Small Letter M

109

m

0x6D

Latin Small Letter N

n

110

0x6E

Latin Small Letter O

111

o

0x6F

Latin Small Letter P

p

112

0x70

Latin Small Letter Q

113

q

0x71

Latin Small Letter R

r

114

0x72

Latin Small Letter S

115

s

0x73

Latin Small Letter T

t

116

0x74

Latin Small Letter U

117

u

0x75

Latin Small Letter V

v

118

0x76

Latin Small Letter W

119

w

0x77

Latin Small Letter X

x

120

0x78

Latin Small Letter Y

121

y

0x79

Latin Small Letter Z

z

122

0x7A

Left Curly Bracket

123

{

0x7B

Vertical Line

|

124

0x7C

Right Curly Bracket

125

}

0x7D

Tilde

~

126

0x7E

Delete

127



0x7F

为了在字符和数字之间(即对字符进行编码 )以及在数字和字符之间(即对字符进行解码 )进行相互转换,我们应该使用某种编码-解码机制,也称为字符编码方案字符编码格式

字符编码方案是在计算机上对字符进行编码和解码的系统。

一个更好、更流行的替代方案是 UTF-16 。在 UTF-16 编码方案中,每个字符至少占用 16 位。注意,16 位块代表一个代码单元 。毫无疑问,并非所有 Unicode 字符都能用 16 位来存储;我们必须增加存储空间来容纳它们。这就需要用到一些数学知识。UTF-16 是可变长度的,也就是说,有些字符占用一个码元(即 16 位),有些字符占用两个码元(即 32 位)。

在 Unicode 术语中,跨越两个代码单元的字符被表示为代理对 - 一个高代理 值(第一个代码单元)后跟一个低代理 值(第二个代码单元)。

问题在于,UTF-16 中的某些代码单元(即 16 位块)被保留用于特殊含义——有些是高位代理,有些是低位代理。当 UTF-16 解码器遇到高位代理时,它会立即知道也应该读取下一个代码单元,以确定当前字符。

回到 JavaScript,它默认对其字符串类型使用 UTF-16 编码方案 。

因此,Unicode 是使用的字符集。这样,JavaScript 中给定字符串中的每个字符占用 16 位或 32 位,具体取决于字符本身。

所以从 JavaScript 的角度来看,字符串是无符号的 16 位整数序列 。

换句话说,JavaScript 字符串是 Unicode 代码单元(无符号 16 位整数)的序列。在使用字符串时,务必牢记这一定义。

UTF-16 解码器(将字符转换为数字)将给定的字符串读取为 16 位整数数组,并处理每个代码单元以确定应该输出的相应字符。

所以最后,我们现在了解了 JavaScript 中 “字符” 的含义,即它是一个基于 UTF-16 格式的跨越 16 位或 32 位的无符号整数。

现在,让我们研究一下字符串上可用的 charCodeAt() 方法,它可以帮助我们看到这些无符号整数。 不然太抽象

charCodeAt() 方法

借助 charCodeAt() 字符串方法,我们可以检查与字符串中特定字符相关联的代码单元(即数字)。

string.charCodeAt([index])

只需将您想要检查其代码的字符的索引作为第一个 index 参数传入,该方法将根据 UTF-16 格式返回与该字符相对应的整数。

var str = 'abc';

console.log(str.charCodeAt(0)); 97
console.log(str.charCodeAt(1)); 98
console.log(str.charCodeAt(2)); 99

如果我们向 charCodeAt() 传递一个在给定字符串中不存在的索引,则该方法返回 NaN

显然, 'abc' 中没有第四个字符,同样 str.charCodeAt(3) 计算结果为 NaN

现在,我们尝试一个稍微复杂一点的例子。考虑以下代码:

var str = '🙂';

console.log(str.charCodeAt(0));
console.log(str.charCodeAt(1));

let we see see

55357
56898

这真是令人惊讶。上面的字符串中只有一个字符,因此 str.charCodeAt(1) 应该返回 NaN 。然而,事实并非如此。

what's going on !

嗯,这只是一个问题,或者正确定义 charCodeAt() 作用。在开头,我们说过 charCodeAt() 返回给定字符串中特定字符的代码。这个定义并不精确。更好的说法是 charCodeAt( i ) 返回给定字符串中的 i 个代码单元。

JavaScript 将字符串视为代码单元(16 位块)的序列。现在,无论字符串包含两个代码单元来表示一个字符,还是包含两个代码单元来表示两个独立的字符,它都是由两个单元组成的,因此我们可以分别访问它们。

回到上面的代码,🙂字符由两个代码单元组成,它们共同构成一个代理对。第一个代码单元是高位代理,值为 55,357,而第二个代码单元是低位代理,值为 56,898。这两个代码单元共同构成了 UTF-16 编码的 🙂 字符。

例如,如果我们使用括号表示法访问字符串 '🙂' 的第一个或第二个字符,我们会得到相同的行为:

var str = '🙂'
str[0] >>> '\uD83D'
str[1] >>> '\uDE42'
// 十六进制数 D83D 代表十进制数 55,357,而 DE42 代表 56,898,
// 分别类似于上面的 charCodeAt(0) 和 charCodeAt(1) 调用发出的值。

如果我们知道给定的字符串中有一个字符的代码点超出了 16 位范围(即 65,535),使用 2 个代码单元表示,那么我们必须使用 codePointAt() 方法来检索整个代码点。

codePointAt() 方法

codePointAt() 方法与 charCodeAt() 非常相似,因为它也允许我们检查字符串中给定字符的代码。

string.codePointAt([index])

index 是给定字符串中要检查的代码单元的索引。如果省略,则默认为 0

然而, codePointAt()charCodeAt() 之间存在细微但非常重要的差异。

也就是说,如果检查的代码单元代表高代理值, codePointAt() 会将以下低代理代码单元与高代理代码单元(检查的代码单元)结合起来,生成完整的代码点。

var str = '🙂';

console.log(str.codePointAt(0)); // 128578	
console.log(str.codePointAt(1)); // 56898

注意这里的第一条日志。由于字符 '🙂' 由两个代码单元组成,并且在 str.codePointAt(0) 中我们检查了第一个代码单元,它是一个高位代理,该方法将下一个代码单元(其值为 56,898)与高位代理组合起来,得到 128,578('🙂' 在 Unicode 中的代码点)。

如果检查的代码单元不代表高位代理, codePointAt() 会按原样返回该单元。同样,对于字符串 '🙂'str.codePointAt(1)str.charCodeAt(1) 都会返回相同的值。

除此之外,如果检查的代码单元不存在于给定的字符串中,则 charCodeAt() 返回 NaNcodePointAt() 返回 undefined


length 属性

length 字符串属性返回给定字符串中的代码单元总数。

因此,字符串 '🙂' 的长度不是 1 ;而是 2 ,因为 '🙂' 由两个代码单元组成。

console.log('abc'.length) // 3
console.log('🙂'.length) // 2

请注意,上面的 “字符” 数量并不总是与 length 的返回值(即代码单元的数量)相同。

如果我们想要获得给定字符串中的字符总数(而不是代码单元),我们必须简单地遍历整个字符串,检查每个单独的代码单元以确定它是否代表高代理值(因此应该与下一个代码单元组合以生成单个字符)。

Unicode 转义序列

当我们拥有特定字符的十六进制代码并想要展示它时,Unicode 转义序列很有用。

\u{X}
\u{XX}
...
\u{XXXXXX}

词典比较

我们了解了关系运算符 <<=>>====!== 在字符串上的使用。现在,是时候深入了解它们的工作原理了。上面列出的每个二元运算符都对给定的字符串执行所谓的字典顺序比较。这或多或少类似于字典按字母顺序对单词进行排序的方式。

词典顺序比较是如何发生的?

对给定的两个字符串同时进行迭代,并将相应的代码单元相互比较。

如果在任何时候发现不匹配,则代码单元值大于另一个字符串中相应代码单元的字符串在字典顺序上被称为大于另一个字符串;相应地,另一个字符串在字典顺序上被称为较小

但是,如果直到迭代耗尽其中一个字符串(即到达其末尾)时才发现不匹配,那么此时就有两种可能性:

  • 两个字符串的长度相同。在这种情况下,字符串完全相同。
  • 两个字符串的长度相同。在这种情况下,长度较大的字符串按字典顺序排列比另一个字符串大;相应地,另一个字符串按字典顺序排列则较小。

回到运算符,给定两个字符串变量 ab

  • a < b 表示 ' a 按字典顺序小于 b ' 。 事实上 比如 'a' 97 'b' 98 97 < 98
  • a > b 表示 ' a 按字典顺序大于 b '
  • a === b 表示 ' ab 相同 '

其他关系运算符 <=>=!== 可以从这三个简单运算符派生而来。

'a' < 'b' // true
'a' < 'a' // false
'a' < 'A' // false
'ab' < 'abc' // true

在第三个语句中,由于 'a' 的代码单元(十进制为 97)大于 'A' 的代码单元(十进制为 65), 'a' < 'A' 得出 false

在最后一条语句中,由于 'ab''abc' 的前两个代码单元没有不匹配的情况,因此比较的重点在于字符串的长度。由于 'ab' 长度较小,因此按字典顺序排列它确实比 'abc' 短。因此, 'ab' < 'abc' 返回 true


网站公告

今日签到

点亮在社区的每一天
去签到