经常会有在excel查找多个字符串的需求,比如在一个大excel表里找几门课,之前一直是导出成 csv 然后对文本文档进行操作,这样最终还要导回 excel,今天花费3、4个小时学了下vb,做了一个宏,直接在excel上操作。

#最长公共子序列

逻辑是将课程名与要查找的课程名进行最长公共子序列匹配,达到阈值则算找到相应的课程。

所用到的算法可以参考:
最长公共子序列(Longest Common Subsequence)问题

代码copy自 Longest Common Subsequence implemented in VBA (Visual Basic for Applications),做了简单的修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Public Function max(ByRef a As Long, ByRef b As Long) As Long
If a >= b Then
max = a
Else
max = b
End If
End Function

Public Function longestCommonSubsequence(ByRef string1 As String, ByRef string2 As String) As Double
If string1 = vbNullString Or string2 = vbNullString Then
MsgBox "出错"
Exit Function
End If

Dim num() As Long

'define the array, note rows of zeros get added to front automatically
ReDim num(Len(string1), Len(string2))

Dim i As Long, j As Long

For i = 1 To Len(string1)
For j = 1 To Len(string2)
If Mid$(string1, i, 1) = Mid$(string2, j, 1) Then
num(i, j) = num(i - 1, j - 1) + 1
Else
num(i, j) = max(num(i - 1, j), num(i, j - 1))
End If
Next j
Next i

If Len(string1) > Len(string2) Then
longestCommonSubsequence = num(Len(string1), Len(string2)) * 100.0 / Len(string2)
Else
longestCommonSubsequence = num(Len(string1), Len(string2)) * 100.0 / Len(string1)
End If

End Function

#构建查询字符串

没有想到在vb里创建一个字符串数组变量这么麻烦,最终还是选择了Declare and Initialize String Array in VBA里的方法。

1
2
Dim classList() As String
classList = Split("符号学,大学语文,统计", ",")

有点担心需要查询的课程名中有 ,,到时候可能要找些别的符号作为分隔符。

#遍历并标记

参考Using VBA to Select and Highlight Excel Rows,使用 .EntireRow.Interior.ColorIndex 方法来将整行标记,在excel里,白色的数值是2,黄色是6

1
2
3
If longestCommonSubsequence(Cells(pos, nameRow).text, classList(classNum)) > 40 Then
Cells(pos, nameRow).EntireRow.Interior.ColorIndex = 6
End If

#完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Option Explicit

Public Function max(ByRef a As Long, ByRef b As Long) As Long
If a >= b Then
max = a
Else
max = b
End If
End Function

Public Function longestCommonSubsequence(ByRef string1 As String, ByRef string2 As String) As Double
If string1 = vbNullString Or string2 = vbNullString Then
MsgBox "出错"
Exit Function
End If

Dim num() As Long

'define the array, note rows of zeros get added to front automatically
ReDim num(Len(string1), Len(string2))

Dim i As Long, j As Long

For i = 1 To Len(string1)
For j = 1 To Len(string2)
If Mid$(string1, i, 1) = Mid$(string2, j, 1) Then
num(i, j) = num(i - 1, j - 1) + 1
Else
num(i, j) = max(num(i - 1, j), num(i, j - 1))
End If
Next j
Next i

If Len(string1) > Len(string2) Then
longestCommonSubsequence = num(Len(string1), Len(string2)) * 100.0 / Len(string2)
Else
longestCommonSubsequence = num(Len(string1), Len(string2)) * 100.0 / Len(string1)
End If

End Function

Sub FindAndMark()
Dim pos As Integer
Dim nameRow As Integer
Dim classNum As Integer
Dim rate As Integer
nameRow = 1
rate = 60

For pos = 1 To Sheet1.UsedRange.Rows.Count
Cells(pos, nameRow).EntireRow.Interior.ColorIndex = 2
Next pos

Dim classList() As String
classList = Split("品味《诗经》,老子的智慧,庄子新解,汉书,魏晋风度,唐宋词史,品味朱子,红楼问梦,“胡”说四大名著,儒家养心课,禅道智慧,道德经,古希腊哲学,古典文明,中世纪文明,文艺复兴,启蒙运动,冷战,世界当代史,西方史学名著选读,西方都市与文明,世界主要宗教掠影,美育与实践:书法的构形美学溯源,美育与实践:茶道,美育与实践:花艺,美育与实践:黑白之道——围棋,美育与实践:行为之美,美育与实践:民乐,美育与实践:古琴,美育与实践:色彩美学,跨界•对话,周游列国:中国人开眼看世界,城•人:多学科视野中的都市空间、历史与文化,音乐的观念,音乐的观念(下),音乐的观念·音乐的多维视角,人文经典导读,生命科学导论,“走进海洋”系列讲座,人文大讲堂,人文大讲堂·中国文化巡礼,人文大讲堂·周游列国,人文大讲堂·艺术的观念,人文大讲堂·性别与社会,人文大讲堂·哲学与世界,人文大讲堂·人文经典导读,人文大讲堂·认识中国,人文大讲堂·文艺鉴赏,性别教育,影像的世界,媒体第一课,与社会学同游", ",")

For pos = 1 To Sheet1.UsedRange.Rows.Count
For classNum = 0 To UBound(classList)
If longestCommonSubsequence(Cells(pos, nameRow).text, classList(classNum)) > rate Then
Cells(pos, nameRow).EntireRow.Interior.ColorIndex = 6
End If
Next
Next pos

classList = Split("生态之美,一“诺”千金——诺贝尔文学奖作家经典作品导读,海陆相望话丝路,西方古代建筑艺术史,世界艺术博物馆之旅,西方经典歌剧与音乐剧欣赏,西方摇滚文化,英国创造了现代世界吗? ——英国近代史,知日之智——中日之间文化交涉的诠释与反思,东山魁夷与日本艺术,中东漫记,全球化与中国史,认识地球,美国文化史,中国古代城市史,中国暨东方古代建筑艺术史,文化中国,官僚,中国文学(先秦魏晋南北朝部分),中国文学(唐宋部分),中国科举文化,大唐盛世:唐代的政治与社会,明清小说中的社会生活,灯谜中的中国智慧,当代中国的文化生产,中国经济与文化地理,十二孔陶笛演奏,男声合唱(上),小提琴演奏训练,礼乐之邦的乐文化巡礼,艺术欣赏与创作,影像工作坊,校园戏剧创作实践,纪录片与实践,基地实践与教学(上),迷人的音乐与即兴的乐趣,聆听经典音乐,美国大众流行音乐,声乐艺术赏析,性别与文学,性别教育与生活,身份与认同,中医养生(原名:走近中医,生活中的化学A,磷与生活,疾病与健康,无知之知——哲学的智慧,生活中的伦理,物质文明,家屋与族性", ",")

For pos = 1 To Sheet1.UsedRange.Rows.Count
For classNum = 0 To UBound(classList)
If longestCommonSubsequence(Cells(pos, nameRow).text, classList(classNum)) > rate Then
Cells(pos, nameRow).EntireRow.Interior.ColorIndex = 6
End If
Next
Next pos

classList = Split("文学与文化研究热点概念解析,知识分子与公共领域,公民常识,科技伦理,闽南方言与文化,闽南话入门,应用医疗人类学与身心健康,文化人类学,逻辑与科学,推理与论证,流行文化——爱与哀愁,比较文学与文化,跨界论道:科学走进人文,国故新知:沟通文理,启迪智慧,恋人絮语—世界电影的情感教育,教育学电影批评,思维规则,沉舟帆影-海洋考古,乡村仪式与戏剧,乡村、乡愁与新乡村建设,中外文化比较,简明天文学,药学与化妆品学,文物鉴赏,大数据时代的信息安全,财税基础,环境与能源,智能制造与工业4.0,水与生活,航空航天基础,Positive Psychology(积极心理学),大学历史与文化,大自然探秘:自然与生态,东西方文化巡礼,人文与公共卫生,华夏文明传播,经济学经典,两岸国学与文化传承,美国文学经典,品悟西游,逻辑学,社会心理学,项目管理:案例与实践,中国历史地理,国剧赏析", ",")

For pos = 1 To Sheet1.UsedRange.Rows.Count
For classNum = 0 To UBound(classList)
If longestCommonSubsequence(Cells(pos, nameRow).text, classList(classNum)) > rate Then
Cells(pos, nameRow).EntireRow.Interior.ColorIndex = 6
End If
Next
Next pos
End Sub

完整的代码如上,需要自己拿去修改的地方有:

  1. nameRow 是需要查找的名字所在的列号
  2. classList 是需要查找的名字的集合
  3. rate 是重复率,即阈值
  4. 如果classList特别长,看情况分几段来查找吧,像上面这样,注意每一段的开头和结尾不要带逗号,

一列数据转,格式的网址在这:https://onns.xyz/tools/line-break-to-comma/,效果如图:

效果图

#测试

测试结果如图所示:

测试结果图

#其它

如果只显示标黄的相关问题可以百度。

#参考文档

如果有任何问题可以留言问我~~