使用VBA在Office中输入特殊字符(3/3)

在博文《使用VBA在Office中输入特殊字符(1/3)》中我们知道月亮符号的字符代码为0x1F319【0x为16进制标识,在VBA中使用&H前缀】。
使用VBA在Office中输入特殊字符(3/3)_第1张图片
在Word中录制得到的代码如下。

Sub WordInsertMoon()
    Selection.InsertSymbol Font:="Segoe UI Symbol", CharacterNumber:=-10180, _
        Unicode:=True
    Selection.InsertSymbol Font:="Segoe UI Symbol", CharacterNumber:=-8423, _
        Unicode:=True
End Sub

使用Windows中的计算器可以轻松进行进制转换,如下图所示。其中的-10180-8423和字符代码0x1F319的不同进制转换如下,似乎这几个数字之间也看不出什么关系。
使用VBA在Office中输入特殊字符(3/3)_第2张图片
如果看一下-10180-8423的低4位,可以拆分为:D8,3C,DF,19,对应的十进制为:216,60,223,25,这就是Byte数组中的4个元素,只是顺序不同而已。

字符代码(0x1F319)和这两个数字的关系是什么呢?先来看一下UTF-16(Unicde编码的一种实现形式)的标准文档RFC2781。

https://tools.ietf.org/html/rfc2781
2.1 Encoding UTF-16

Encoding of a single character from an ISO 10646 character value to
UTF-16 proceeds as follows. Let U be the character number, no greater
than 0x10FFFF.

  1. If U < 0x10000, encode U as a 16-bit unsigned integer and
    terminate.

  2. Let U’ = U - 0x10000. Because U is less than or equal to
    0x10FFFF,
    U’ must be less than or equal to 0xFFFFF. That is, U’ can be
    represented in 20 bits.

  3. Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800
    and
    0xDC00, respectively. These integers each have 10 bits free to
    encode the character value, for a total of 20 bits.

  4. Assign the 10 high-order bits of the 20-bit U’ to the 10
    low-order
    bits of W1 and the 10 low-order bits of U’ to the 10 low-order
    bits of W2. Terminate.

Graphically, steps 2 through 4 look like: U’ = yyyyyyyyyyxxxxxxxxxx
W1 = 110110yyyyyyyyyy W2 = 110111xxxxxxxxxx

对于月亮字符,U=0x1F319,符合大于0x10000,并且小于0x10FFFF

  • 按step 2操作, U’ = U - 0x10000 = 0xF319
  • 初始化一个16 bit的无符号整数0xD800 DC00,W1高位6比特为110110,W2高位6比特为110111
  • 将U’按照从低位到高位依次填充到无符号整数中,不足的位补零
  • 对于月亮符号,内存中存储的数据实际上为 0xD83C DF19
    使用VBA在Office中输入特殊字符(3/3)_第3张图片
    对于0xD83C和0xDF19,如果转换为有符号的整数,那么其十进制值分别为:-10180-8423,和本文第一个截图中的两个数字是相同的,这就是Word录制宏中两个负数的由来(UTF-16中使用无符号整数,如果最高Bit位为1,当作符号整数进行解析时,则代表负整数)。
    Byte数组中的元素顺序与上图中不同,是由于计算机中存储顺序(小端模式)为低位字节在前、高位在后。

这里提供一段代码实现这个编码格式的计算。

Function UTF16(sHex As String, Optional sMode As String = "HEX") As String
    Dim lByte, hByte, arrRes
    ' &HDC00 = 56320, &HD800 = 55296
    lByte = Hex(56320 Or (&H3FF And Application.Hex2Dec(sHex)))
    hByte = Hex(55296 Or ((&HFFC00 And (Application.Hex2Dec(sHex) - &H10000)) / &H400))
    arrRes = Array(Right(hByte, 2), Left(hByte, 2), Right(lByte, 2), Left(lByte, 2))
    If sMode = "DEC" Then
        For i = 0 To 3
            arrRes(i) = Application.Hex2Dec(arrRes(i))
        Next
    End If
    UTF16 = Join(arrRes)
End Function

【代码解析】
函数UTF16有两个参数:

  • sHex为16进制的字符串,不包含前缀
  • sMode为转换结果的格式,默认为“HEX”,即16进制,如果设置为“DEC”,即十进制。
    第4行和第5行代码获取低位和高位的两个Byte,用16进制形式表示。
    第6行代码根据小端模式,将4个Byte的16进制形式字符保存在数组中。
    如果输出为十进制形式,那么第7~10行代码借助工作表函数将16进制转换为十进制。
    第12行代码将数组结果组合为一个字符串。

使用如下过程,可以测试代码效果。

Sub InsertSymbol()
    Dim moon As String, iNum
    Dim arrByte(0 To 3) As Byte
    iNum = Split(UTF16("1F337", "DEC"))
    For i = 0 To 3
        arrByte(i) = iNum(i)
    Next
    moon = arrByte
    ActiveCell.FormulaR1C1 = moon
End Sub

【代码解析】
第4行代码指定插入字符代码为0x1F337,输出格式为十进制。
第5~7行代码将结果赋值给Byte数组。
第8行代码将Byte数组转换为字符。
第9行代码将特殊字符插入活动单元格中,效果如下图所示。
使用VBA在Office中输入特殊字符(3/3)_第4张图片


至此,字符代码和Byte数组的对应转换关系已经全部讲完了,如果对于进制转换和内存Bit位不太理解的话,可能理解起来有些困难,但是主要学会使用上面两段代码将可以了,插入特殊字符从此不用再求人!


相关链接:

  1. 使用VBA在Office中输入特殊字符(1/3)
  2. 使用VBA在Office中输入特殊字符(2/3)
  3. 使用VBA在Office中输入特殊字符(3/3)

你可能感兴趣的:(Office,数组,VBA,特殊字符,Byte数组,位运算,16进制)