问题
ChrW
字符代码参数是Long
,用于标识字符,但不允许大于65535的值(十六进制值&HFFFF
)-
参见MS Help。
例如,其他符号和象形文字可以在 Unicode十六进制块 1F300-1F5FF
中找到。因此,对于打开或关闭的挂锁符号,我找不到任何方式来表示►1F512
和1F513
的建议十六进制值
正好在课程ChrW(&H1F512)
的此字符块中,将导致无效的过程/参数调用。
最近的answer发现了一个可能的错误选择,它引用了较低的字符代码
(通过ChrW(&HE1F7)
和ChrW(&HE1F6)
),但我正在寻找一种获得更高字符代码表示的方法。
问题
是否存在一种系统的方法来表达通过VBA或变通方法在大于FFFF
的十六进制代码块中找到的 Unicode字符?
答案 0 :(得分:6)
类似的事情应该起作用。大多数代码我都没有写,但是我知道要寻找什么。基本上将十六进制映射到等效的字节数组,然后返回字符串。
Option Explicit
'Pulled from https://www.di-mgt.com.au/howto-convert-vba-unicode-to-utf8.html
''' Maps a character string to a UTF-16 (wide character) string
Private Declare PtrSafe Function MultiByteToWideChar Lib "kernel32" ( _
ByVal CodePage As Long, _
ByVal dwFlags As Long, _
ByVal lpMultiByteStr As LongPtr, _
ByVal cchMultiByte As Long, _
ByVal lpWideCharStr As LongPtr, _
ByVal cchWideChar As Long _
) As Long
' CodePage constant for UTF-8
Private Const CP_UTF8 = 65001
''' Return length of byte array or zero if uninitialized
Private Function BytesLength(abBytes() As Byte) As Long
' Trap error if array is uninitialized
On Error Resume Next
BytesLength = UBound(abBytes) - LBound(abBytes) + 1
End Function
''' Return VBA "Unicode" string from byte array encoded in UTF-8
Public Function Utf8BytesToString(abUtf8Array() As Byte) As String
Dim nBytes As Long
Dim nChars As Long
Dim strOut As String
Utf8BytesToString = ""
' Catch uninitialized input array
nBytes = BytesLength(abUtf8Array)
If nBytes <= 0 Then Exit Function
' Get number of characters in output string
nChars = MultiByteToWideChar(CP_UTF8, 0&, VarPtr(abUtf8Array(0)), nBytes, 0&, 0&)
' Dimension output buffer to receive string
strOut = String(nChars, 0)
nChars = MultiByteToWideChar(CP_UTF8, 0&, VarPtr(abUtf8Array(0)), nBytes, StrPtr(strOut), nChars)
Utf8BytesToString = Left$(strOut, nChars)
End Function
'Grabbed from https://stackoverflow.com/questions/28798759/how-convert-hex-string-into-byte-array-in-vb6
Private Function HexToBytes(ByVal HexString As String) As Byte()
'Quick and dirty hex String to Byte array. Accepts:
'
' "HH HH HH"
' "HHHHHH"
' "H HH H"
' "HH,HH, HH" and so on.
Dim Bytes() As Byte
Dim HexPos As Integer
Dim HexDigit As Integer
Dim BytePos As Integer
Dim Digits As Integer
ReDim Bytes(Len(HexString) \ 2) 'Initial estimate.
For HexPos = 1 To Len(HexString)
HexDigit = InStr("0123456789ABCDEF", _
UCase$(Mid$(HexString, HexPos, 1))) - 1
If HexDigit >= 0 Then
If BytePos > UBound(Bytes) Then
'Add some room, we'll add room for 4 more to decrease
'how often we end up doing this expensive step:
ReDim Preserve Bytes(UBound(Bytes) + 4)
End If
Bytes(BytePos) = Bytes(BytePos) * &H10 + HexDigit
Digits = Digits + 1
End If
If Digits = 2 Or HexDigit < 0 Then
If Digits > 0 Then BytePos = BytePos + 1
Digits = 0
End If
Next
If Digits = 0 Then BytePos = BytePos - 1
If BytePos < 0 Then
Bytes = "" 'Empty.
Else
ReDim Preserve Bytes(BytePos)
End If
HexToBytes = Bytes
End Function
示例呼叫
Public Sub ExampleLock()
Dim LockBytes() As Byte
LockBytes = HexToBytes("F0 9F 94 92") ' Lock Hex representation, found by -->http://www.ltg.ed.ac.uk/~richard/utf-8.cgi
Sheets(1).Range("A1").Value = Utf8BytesToString(LockBytes) ' Output
End Sub
这是输出到A1的内容。
答案 1 :(得分:5)
对基本多语言平面(BMP)之外的Unicode字符有效的功能是WorksheetFunction.Unichar()
。此示例将包含十六进制的单元格转换为其等效的Unicode:
Sub Convert()
For i = 1 To Selection.Cells.Count
n = WorksheetFunction.Hex2Dec(Selection.Cells(i).Text)
Selection.Cells(i) = WorksheetFunction.Unichar(n)
Next
End Sub
运行宏之前的原始选择:
运行宏后:
如果您的Excel较旧并且WorksheetFunction
不可用,则手动构建UTF-16替代品也可以:
Sub Convert()
For i = 1 To Selection.Cells.Count
n = CLng("&H" + Selection.Cells(i).Text) 'Convert hexadecimal text to integer
If n < &H10000 Then 'BMP characters
Selection.Cells(i) = ChrW(n)
Else
'UTF-16 hi/lo surrogate conversion
'Algorithm:
'1. Code point - 10000h (max U+10FFFF give 9FFFF...20 bits)
'2. In binary, but 10 bits in first surrogate (x) and 10 in 2nd surrogate (y)
' 110110xxxxxxxxxx 110111yyyyyyyyyy
tmp = n - &H10000
h = &HD800 + Int(tmp / (2 ^ 10)) 'bitwise right shift by 10
l = &HDC00 + (tmp And &H3FF) 'bitwise AND of last 10 bits
Selection.Cells(i) = ChrW(h) + ChrW(l)
End If
Next
End Sub
答案 2 :(得分:2)
替代T.M。
别忘了添加对“ Microsoft HTML对象库”的引用
Function GetUnicode(CharCodeString As String) As String
Dim Doc As New HTMLDocument
Doc.body.innerHTML = "&#x" & CharCodeString & ";"
GetUnicode = Doc.body.innerText
End Function
答案 3 :(得分:1)
通过HTML解决
除了上述有效的解决方案之外:我发现使用 IE HTML 内容很容易,因为HTML不能区分高低代码块集。下面的函数仅返回解释后的内部html :
示例调用中写的挂锁符号到单元格A1
[A1] = GetUnicode("1F512")
[1]函数GetUnicode()-通过InternetExplorer
Function GetUnicode$(ByVal CharCodeString$)
' Purpose: get Unicode character via any valid unprefixed hex code string
' Note: late bound InternetExplorer reference
Dim Ie As Object
Set Ie = CreateObject("InternetExplorer.Application")
With Ie
.Visible = False
.Navigate "about:blank"
.document.body.innerhtml = "&#x" & CharCodeString & ";" ' prefixing HTML code
GetUnicode = .document.body.innerhtml
.Quit
End With
End Function
[2]替代函数GetUnicode()-通过XMLDom(编辑5/12 2019)
这表示使用XMLDom的主机不可知方法。引用Wikipedia
“文档对象模型(DOM)是一个跨平台且与语言无关的应用程序编程接口,该文档编程接口将XML文档视为树结构,其中每个节点都是代表文档一部分的对象。”
类似于IE方法,Unicode实体由数字(十六进制)前缀&#x
+ num + ;
组成。通常,我喜欢XML,因为它通常允许通过其各个节点和子节点引用进行更灵活的编码。该示例仅演示了给出想法的最简单方法。
Function getUnicode$(ByVal CharCodeString$)
' Purpose: get Unicode character via any valid unprefixed hex code string
' Note: late bound MSXML2 reference using XMLDom
Dim XmlString$
XmlString = "<?xml version=""1.0"" encoding=""UTF-8""?><root><symbol>&#x" _
& CharCodeString & ";</symbol></root>"
With CreateObject("MSXML2.DOMDocument.6.0")
.ValidateOnParse = True
.Async = False
If .LoadXML(XmlString) Then
getUnicode = .DocumentElement.SelectSingleNode("symbol").Text
End If
End With
End Function
答案 4 :(得分:1)
以下是我在ASP Classic中使用的VBScript代码。
您会注意到,没有类型声明,所有内容都是变量。
我打算使用它的ChrU
和AscU
函数支持UCS-2(基本多语言平面)以外的字符。
由于这是为VBScript首先编写的,所以我认为它与主机无关。没有,但是它也应该在MAC上工作。希望对您有所帮助。
Private Function RightShift(ByVal pVal, shift)
Dim i, nVal
For i = 1 To shift
nVal = (pVal And &H7FFFFFFF) \ 2
If nVal And &H80000000 Then nVal = nVal Or &HC0000000
pVal = nVal
Next
RightShift = pVal
End Function
Private Function LeftShift(ByVal pVal, shift)
Dim i, nVal
For i = 1 To shift
nVal = (pVal And &H3FFFFFFF) * 2
If pVal And &H40000000 Then
nVal = nVal Or &H80000000
End If
pVal = nVal
Next
LeftShift = nVal
End Function
Public Function ChrU(ByVal code)
Dim lo, hi ' to hold 16-bit surrogate pairs
code = Int(code)
If code <= 65535 Then
' code is in the UCS-2 range (a.k.a. Basic Multilingual Plane) which ChrW (and AscW) relies on.
' falling back to ChrW
ChrU = ChrW(code)
ElseIf code <= 1114111 Then ' code is in the Unicode range beyond UCS-2
code = code - &H10000
lo = ChrW(&HD800& Or RightShift(code, 10))
hi = ChrW(&HDC00& Or (code And &H3FF))
ChrU = Join(Array(lo, hi), "")
Else
Err.Raise 9, "ChrU", "Code point was out of range."
End If
End Function
Public Function AscU(str)
Dim lo, hi ' to hold 16-bit surrogate pairs
If Len(str) = 1 Then
AscU = AscW(str) And &HFFFF&
Else
Dim txt
txt = Left(str, 2)
lo = AscW(Mid(txt, 1, 1)) And &HFFFF&
hi = AscW(Mid(txt, 2, 1)) And &HFFFF&
If &HDC00& > hi Or hi > &HDFFF& Then
' hi surrogate is not valid
' assuming "str" is a Unicode (UCS-2) string of at least 2 characters
' returning first character's codepoint
' as Asc and AscW do
AscU = lo
Exit Function
End If
AscU = &H10000 + LeftShift(lo And &H3FF, 10) + (hi And &H3FF)
End If
End Function