如何在 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
我也试过 Asc
和 AscW
。这些在引号上工作得很好, return 只在最后一个字节。但是由于 ActiveDocument.Characters
return 是其他两个的括号,它只是像括号一样处理它。
关于这些不同的输出以及如何正确处理这些字符,我有几个问题。
- 为什么
ActiveDocument.Characters
return读plus/minus和注册商标符号时有括号?
- 为什么使用这些字符时
194
放在前面?
- 我见过的大多数 ASCII 表都遵循所见的编码 here. But those indicate that
177
and 174
should be dots and double arrows, respectively. Which does not match up to Word or vim. But this table 似乎与 Word 和 vim 一致。是否有多种 ASCII 编码?我以为这是一个标准。
- 读取这些特殊的多字节字符的正确方法是什么,以便我可以识别它们并用它们的 ASCII 等价物替换它们?
编辑:
刚刚了解了如何在 Word 中使用 AltX 将每个字符更改为其 unicode 编号。这在原始引号上工作正常,但当我在原始 plus/minus 和商标符号上尝试时它没有任何作用。不确定这些字符来自何处。
编辑 2:
我尝试保存到文本文件中。 plus/minus 和商标符号在西欧 (Windows) 编码中无法正确转换。 UTF-8 更好,但也有问题。 Unicode 将转换所有内容,但它将问题字符转换为前面的字符。
编辑 3:
编辑 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)的映射的特定字符块)。
我正在尝试从大量旧 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
我也试过 Asc
和 AscW
。这些在引号上工作得很好, return 只在最后一个字节。但是由于 ActiveDocument.Characters
return 是其他两个的括号,它只是像括号一样处理它。
关于这些不同的输出以及如何正确处理这些字符,我有几个问题。
- 为什么
ActiveDocument.Characters
return读plus/minus和注册商标符号时有括号? - 为什么使用这些字符时
194
放在前面? - 我见过的大多数 ASCII 表都遵循所见的编码 here. But those indicate that
177
and174
should be dots and double arrows, respectively. Which does not match up to Word or vim. But this table 似乎与 Word 和 vim 一致。是否有多种 ASCII 编码?我以为这是一个标准。 - 读取这些特殊的多字节字符的正确方法是什么,以便我可以识别它们并用它们的 ASCII 等价物替换它们?
编辑:
刚刚了解了如何在 Word 中使用 AltX 将每个字符更改为其 unicode 编号。这在原始引号上工作正常,但当我在原始 plus/minus 和商标符号上尝试时它没有任何作用。不确定这些字符来自何处。
编辑 2:
我尝试保存到文本文件中。 plus/minus 和商标符号在西欧 (Windows) 编码中无法正确转换。 UTF-8 更好,但也有问题。 Unicode 将转换所有内容,但它将问题字符转换为前面的字符。
编辑 3:
编辑 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)的映射的特定字符块)。