如何在 MS Word 中处理多字节符号 VBA

How to process multibyte symbols in MS Word VBA

我正在尝试从大量旧 Word 文件中提取文本并将文本放入数据库中。为此,我将文本解析为多个部分,为每个部分创建一个文本文件,然后进行批量插入。

写这些文件的人最初使用了一些奇怪的字符。在 Word 中,外观类似于普通的扩展 ASCII 字符。但是当我开始查看十进制或十六进制代码时,它们不是任何已知字符。

左边是文档中的原始字符,右边是从键盘输入的适当 ASCII 字符。

当我将它们复制并粘贴到 vim 时,它看起来像这样:

使用vim查看十进制和十六进制代码,文件如下所示:

Original      True ASCII
Dec    Hex    Dec    Hex
61617  f0b1   177    00b1
61666  f0e2   174    00ae
 8220  201c    34    22
 8221  201d    34    22

我正在使用找到的代码中的一些行 here:

NextChar = ActiveDocument.Characters(idx)
Dim nBytes As Long
Dim abBuffer() As Byte

nBytes = WideCharToMultiByte(CP_UTF8, 0&, ByVal StrPtr(NextChar), -1, vbNull, 0&, 0&, 0&)
ReDim abBuffer(4)
nBytes = WideCharToMultiByte(CP_UTF8, 0&, ByVal StrPtr(NextChar), -1, ByVal VarPtr(abBuffer(0)), nBytes - 1, 0&, 0&)

使用此方法,VBA returns如下十进制代码(部分为多字节,用逗号表示):

Original        True ASCII
Dec             Dec
40              194, 177
40              194, 174
226, 128, 156   34
226, 128, 157   34

我也试过 AscAscW。这些在引号上工作得很好, return 只在最后一个字节。但是由于 ActiveDocument.Characters return 是其他两个的括号,它只是像括号一样处理它。

关于这些不同的输出以及如何正确处理这些字符,我有几个问题。


编辑:

刚刚了解了如何在 Word 中使用 AltX 将每个字符更改为其 unicode 编号。这在原始引号上工作正常,但当我在原始 plus/minus 和商标符号上尝试时它没有任何作用。不确定这些字符来自何处。


编辑 2:

我尝试保存到文本文件中。 plus/minus 和商标符号在西欧 (Windows) 编码中无法正确转换。 UTF-8 更好,但也有问题。 Unicode 将转换所有内容,但它将问题字符转换为前面的字符。


编辑 3:

Link to test file


编辑 4:

我使用了 Open XML 生产力工具并直接查看了 XML 和可能的代码来创建这些奇怪的符号。这是我发现的:

<w:r w:rsidRPr="00EE7521">
 <w:rPr>
   <w:sz w:val="16" />
 </w:rPr>
 <w:sym w:font="Symbol" w:char="F0B1" />

并且:

RunProperties runProperties1 = new RunProperties();
FontSize fontSize2 = new FontSize(){ Val = "16" };

runProperties1.Append(fontSize2);
SymbolChar symbolChar1 = new SymbolChar(){ Font = "Symbol", Char = "F0B1" };

有没有办法检测并正确解码 VBA 中输入的 SymbolChar 个字符?或此时的任何其他语言。

我刚刚测试了这个简单的宏,它在我的测试文档中成功地将前两个符号 (f0b1, f0e2) 的实例替换为它们的 ASCII 等效项。当检测到字符来自PUA(Private Use Area)时,它只是清除第一个字节。

Private Sub Strip_PUA()
    For idx = 1 To ActiveDocument.Characters.Count
        Dim bArr() As Byte
        bArr = ActiveDocument.Characters(idx)

        If bArr(1) >= &HE0 And bArr(1) <= &HF8 Then
            bArr(1) = 0
            ActiveDocument.Characters(idx) = bArr
        End If
    Next
End Sub

您可能需要根据系统的字节序和字符的字节数将 bArr 调整为 1 以外的值。这也依赖于您遇到的字符恰好已经具有正确的 ASCII 字节这一事实。情况可能并非总是如此 - 您必须进行调查才能确定。


编辑:转载自this Google Groups discussion

Sub SymbolsUnprotect()
  Dim SelFont, SelCharNum

  Selection.Collapse (wdCollapseStart)
  Selection.Find.ClearFormatting
  With Selection.Find
    .Text = "[" & ChrW(61472) & "-" & ChrW(61695) & "]"
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindContinue
    .Format = False
    .MatchCase = False
    .MatchWholeWord = False
    .MatchAllWordForms = False
    .MatchSoundsLike = False
    .MatchWildcards = True
  End With
  While Selection.Find.Execute
    With Dialogs(wdDialogInsertSymbol)
      SelFont = .Font
      SelCharNum = .CharNum
    End With

    Selection.Font.Name = SelFont
    Selection.TypeText Text:=ChrW(SelCharNum)

    ' replace the last 2 lines with the following to
    ' protect symbols from decorative fonts:
    ' Selection.InsertSymbol _
    '   Font:=SelFont, _
    '   CharacterNumber:=SelCharNum, _
    '   Unicode:=True

  Wend
End Sub

这将 "unprotect" 符号,在受保护时将显示为“(”(十进制 40)字符 - 这是通过 Word 的 Insert > Symbol 对话框插入的符号的默认设置。它将允许您可以将这些字符的字节正确读取为 f0**,但无法准确地告诉您这些字节对应于 Symbol 等字体的什么内容,这些字体在 PUA 中定义了自己的映射。查找这些 mappings for conversion into Unicode(链接到包含 ±(符号 177)到(Unicode 177)和 ®(符号 226)到(Unicode 174)的映射的特定字符块)。