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

时间:2016-07-26 16:04:12

标签: vba ms-word ascii extended-ascii

我尝试从大量旧Word文件中提取文本并将文本放入数据库中。我通过将文本解析为部分,为每个部分创建文本文件,然后进行批量插入来实现此目的。

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

strange characters and their ASCII equivalents

左侧是文档中最初的字符,右侧是从键盘输入的相应ASCII字符。

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

vim's interpretation of the characters

使用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返回以下十进制代码(一些是多个字节,用逗号表示):

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

我还尝试了AscAscW。这些在引号上以某种方式正常工作,只返回最后一个字节。但是,由于ActiveDocument.Characters返回另外两个的括号,它只是像括号一样处理它。

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

  • 为什么ActiveDocument.Characters在阅读加号/减号和注册商标符号时会返回括号?
  • 为什么194在使用这些字符时会被置于前面?
  • 我见过的大多数ASCII表遵循here编码。但是那些表明177174应该分别是点和双箭头。哪个与Word或vim不匹配。但this table似乎同意Word和vim。有多种ASCII编码吗?我认为这是一个标准。
  • 读取这些特殊的多字节字符的正确方法是什么,以便我可以识别它们并用它们的ASCII等价物替换它们?

编辑:

刚学会在Word中使用 Alt X 将每个字符更改为其unicode编号。这在原始引号上工作正常,但是当我在原始正/负和商标符号上尝试它时它什么也没做。不确定这些角色的来源。

编辑2:

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

Western European (Windows)

Unicode

编辑3:

Link to test file

编辑4:

我使用Open XML Productivity Tool并直接查看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个字符?或者此时的任何其他语言。

1 个答案:

答案 0 :(得分:3)

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

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

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