最近用百度AI接口识别了一本小说,扫描图片不是很清晰,结果识别出的引号方向大部分不对。就像下面这样:
省略文字”被两个右引号括起来的文字”是一个勾心斗角,相互倾轧的旋涡,人与人之间的关系是一条赤裸裸的利害索链。作者感到压抑、苦闷、愤懑,他只有拿起沉重的笔,抒发心中的“被两个左引号封闭的文字“
小说有112页A4纸,错误的引号数量没有计算,反正手工修改估计可以烦死两个人。粗看了一下,基本上引号都在同一段落中,数量和位置差不多对,只是方向没有匹配好,像这种情况正是VBA大显身手的时候(如果引号位置或数量不对,那VBA很难解决,因为VBA不知道引号该放在哪里,那得靠人工智能来分析自然语言),于是编出了以下代码:
Sub 引号配对()
Dim oRng As Range
Dim oDoc As Document, aPara As Paragraph
Dim iStart, iEnd As Long, needLeft, needRight As Boolean
Set oDoc = Word.ActiveDocument
Application.ScreenUpdating = False
For Each aPara In oDoc.Paragraphs '一个个段落检查
needLeft = True '可以有左引号
needRight = False '不能来右引号
iEnd = aPara.Range.End '段落末尾索引
iStart = aPara.Range.start '段落开头索引
For i = iStart To iEnd - 1
Set oRng = oDoc.Range(i, i + 1) '逐字符遍历
If oRng.Text = "“" Then '遇到左引号
If Not needLeft Then '不需要左引号
oRng.Text = "”" '改成右引号
needLeft = True '需要左引号
needRight = False '不再需要右引号
Else '需要左引号,不做修改
needRight = True '需要右引号配对
needLeft = False '不再需要左引号
End If
ElseIf oRng.Text = "”" Then '遇到右引号
If Not needRight Then '不需要右引号
oRng.Text = "“" '改成左引号
needLeft = False '不再需要左引号
needRight = True '需要右引号
Else '需要右引号,不做修改
needLeft = True '需要左引号
needRight = False '不再需要右引号
End If
End If
Next
Next
Application.ScreenUpdating = True
End Sub
代码的逻辑注释很详尽,其中有个关键技巧:逐字符遍历段落内容。VBA中没有类似于遍历段落和词那样的结构用于逐个字符来遍历,这里先取得了段落开始和结尾的字符序号,再使用的方法来取得字符。也许这种遍历方式也可以实现上面程序的功能,但是为了记录下逐字符遍历的方法,就没有再试了。
补充:如果先用查找和替换将所有的引号都改成直引号(如果替换后引号仍然是弯引号而不是直引号,那是因为中文版word默认将直引号自动修改为弯引号,需要在word选项的自动更正中将这一选项取消),再用单词(word)遍历的方式,代码可以简化:
Sub 引号配对又一法()
Dim aW As Word, needLeft, needRight As Boolean
needLeft = True '需要左引号标志
needRight = False '需要右引号标志,初始状态下可以有左引号,不能有右引号
Application.ScreenUpdating = False
For Each aW In ActiveDocument.Words
If aW.Text = """" Then '遍历到直引号
If needLeft Then '需要左引号
aW.Text = "“" '改成左引号
needLeft = False '不再需要左引号
needRight = True '需要右引号
ElseIf needRight Then '需要右引号
aW.Text = "”" '改成右引号
needRight = False '不再需要右引号
needLeft = True '需要左引号
End If
End If
Next
Application.ScreenUpdating = True
End Sub
运行上面的宏,在我的上出错。这应该是VBA解释器的bug,你不能把aW声明为Word(可能与Word代表这个Application的名称有关)。去掉这条声明语句即可运行成功,但如果是在严格模式下运行VBA,变量又需要预先声明,怎么办呢?经试验,aW声明为Word的父类Range的对象则无论是否严格模式都可以运行成功,即将上述代码第二行的声明语句改为:
Dim aW As Range, needLeft, needRight As Boolean
这段代码的技巧在于用两个直引号才能表示一个直引号。也就是在判断语句“If aW.Text = """" Then”中,等号后面要写4个直引号(""""),而不能是3个直引号("""),如果只用3个直引号,前面两个直引号优先匹配出一个空字符串,后面多了一个没有匹配的直引号,发生语法错误。用4个直引号,中间两个直引号发生转义变成一个直引号,首尾两个直引号表示字符串,于是产生了一个内容为直引号的字符串。