For i = 1 To 12 ' 在最后一张表后面新建一张表 Sheets.Add after:=Sheets(Sheets.Count) ' 最后一张表命名为i月 Sheets(Sheets.Count).Name = i & "月" Next End Sub
1.2.2 Delete
删除表记得加上取消提示框和恢复提示框。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Sub 删除12个月的表() Dim i As Integer
'取消提示框 Excel.Application.DisplayAlerts = False
For i = 1 To 12 ' 删除第一张,后面表就变为sheets(1) Sheets(2).Delete Next
' 恢复提示框 Excel.Application.DisplayAlerts = True
End Sub
1.2.3 Copy
1 2 3
Sub 复制表() Sheets(1).Copy after:=Sheets(Sheets.Count) End Sub
1.2.4 模板创建日报表
1 2 3 4 5 6 7 8 9 10 11 12
Sub 模板创建31天日报表() Dim i As Integer
For i = 1 To 31 ' 复制第一张表 Sheets(1).Copy after:=Sheets(Sheets.Count) ' 最后一张表命名为5月i日 Sheets(Sheets.Count).Name = "5月" & i & "日" ' 表内的e5单元格赋予内容 Sheets(Sheets.Count).Range("e5") = "2016-5-" & i Next End Sub
1.2.5 多表汇总
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Sub 多表汇总() Dim i As Integer ' 把第一张表的B列设为日期 Columns("B:B").Select Selection.NumberFormatLocal = "yyyy-m-d"
' 从第二张表开始汇总 For i = 2 To Sheets.Count ' 把下表的e5内容存到表1的b10单元格内 Sheets(1).Range("b" & i + 8) = Sheets(i).Range("e5") Sheets(1).Range("c" & i + 8) = Sheets(i).Range("e6") Sheets(1).Range("d" & i + 8) = Sheets(i).Range("e44") Next End Sub
1.3 操作工作簿
1.3.1 For Each
1 2 3 4 5 6 7 8 9 10 11
Sub 删除工作簿() Dim 表 As Worksheet ' 不提示警告框 Application.DisplayAlerts = False For Each 表 In Sheets If 表.Name <> "决不能删" Then 表.Delete End If Next Application.DisplayAlerts = True End Sub
Sub 将多个表拆分并另存为多个工作簿() Dim sht As Worksheet ' 遍历每一张表 For Each sht In Sheets ' 表.copy默认复制当前活动表并新建为一个新工作簿的新表 sht.Copy ' 文件名保存为表名 ActiveWorkbook.SaveAs Filename:="d:\temp\" & sht.Name & ".xlsx" ActiveWorkbook.Close Next End Sub
Sub 合并单元格() Dim rng As Range ' 合并a1、a2;b1、b2;c1、c2单元格 For Each rng In Range("a1:c1") rng.Resize(2, 1).Merge Next End Sub
1 2 3 4 5 6 7 8 9 10 11 12 13
Sub 自动填充() ' 定义rng为单元格格式 Dim rng As Range ' 遍历b2到a列最后一个不为空的单元格的那一行 For Each rng In Range("b2:b" & Range("a65535").End(xlUp).Row) ' 若本单元格左边的一格内为男,则本单元格的值为先生 If rng.Offset(0, -1) = "男" Then rng.Value = "先生" Else rng.Value = "0" End If Next End Sub
Sub 清空内容() For i = 2 To Sheets.Count ' 清除内容不清除格式 Sheets(i).Range("a2:z10000").ClearContents Next End Sub
Sub 拆分表初版() Dim i, j, k As Integer ' 调用函数 清空内容() Call 清空内容 ' 遍历所有工作表 For i = 2 To Sheets.Count ' 遍历表1的第二行到最后一行 For j = 2 To Sheet1.Range("a65535").End(xlUp).Row ' 若表1的d列值等于表i的名字 If Sheet1.Range("d" & j) = Sheets(i).Name Then ' 表i的单元格最后一行有数据的行号为k k = Sheets(i).Range("a65535").End(xlUp).Row ' 表1的第j行整行复制到表i的k行的下一行 Sheet1.Range("d" & j).EntireRow.Copy Sheets(i).Range("a" & k + 1) End If Next Next End Sub
1.4.2 一张表拆分为多张表
1 2 3 4 5 6 7 8 9 10 11
Sub 用筛选来拆分表() Dim i As Integer For i = 2 To Sheets.Count ' 表1的所有行,筛选出第4列D,数据为表名。PS:Criteria1后面是a一 Sheets(1).Range("a1:f1048").AutoFilter Field:=4, Criteria1:=Sheets(i).Name ' 筛选出的表1的所有单元格复制到对应表 Sheet1.Range("a1:f1048").Copy Sheets(i).Range("a1") Next ' 关闭筛选 Sheet1.Range("a1:f1048").AutoFilter End Sub
Sub 未重名则新建表(列, sht0) Dim sht As Worksheet ' 默认k=0 Dim i, k As Integer Dim irow As Integer ' irow为共多少行 irow = sht0.Range("a65536").End(xlUp).Row For i = 2 To irow k = 0 For Each sht In Sheets ' 遍历表,如果表重名,则k=1,那么不新建 If sht.Name = sht0.Cells(i, 列) Then k = 1 End If Next If k = 0 Then ' 没重名 ' 在最后一张表后面新建表并定义表名 Sheets.Add(after:=Sheets(Sheets.Count)).Name = sht0.Cells(i, 列) End If Next End Sub
Sub 拷贝数据(列, sht0) Dim i As Integer Dim irow As Integer ' irow为共多少行 irow = sht0.Range("a65536").End(xlUp).Row Call 未重名则新建表(列, sht0) For i = 2 To Sheets.Count ' 表1的所有行,筛选出第4列D,数据为表名。PS:Criteria1后面是a一 sht0.Range("a1:z" & irow).AutoFilter Field:=列, Criteria1:=Sheets(i).Name ' 筛选出的表1的所有行复制到对应表 sht0.Range("a1:z" & irow).Copy Sheets(i).Range("a1") Next ' 关闭筛选 sht0.Range("a1:z" & irow).AutoFilter End Sub
Sub 拆分数据表完成版() Dim sht, sht0 As Worksheet ' 将一张表的指定列的所有不同的数据拆分为不同的表 Dim i i = InputBox("此操作会删除除第一张表外的所有表,如不需要请关闭。那么请问你要拆分第几列(输入数字)?")
' 如果i不是数字或不在范围内,则终止,防止出错 If IsNumeric(i) = False Or i < 1 Or i > 20 Then MsgBox ("请输入正确的数字,如4。") Exit Sub End If
' 将i转换为整型 i = Val(i)
' 先备份当前活动表数据到sht0,防止误删 Set sht0 = ActiveSheet
Application.DisplayAlerts = False '关闭提示 ' 删除除了第一张表外的所有表 If Sheets.Count > 1 Then For Each sht In Sheets If sht.Name <> sht0.Name Then sht.Delete End If Next End If
Call 拷贝数据(i, sht0) Application.DisplayAlerts = True '打开提示 sht0.Select End Sub
' 在sheet1事件SelectionChange里输入如下代码 Private Sub Worksheet_SelectionChange(ByVal Target As Range) Call 聚光灯 End Sub
Sub 聚光灯() ' 整表无填充色 Cells.Interior.Pattern = xlNone ' 选中的单元格所在行改为黄色 Selection.EntireRow.Interior.Color = 65535 End Sub
自动筛选。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Private Sub Worksheet_Change(ByVal Target As Range) ' 关闭事件触发。事件里一般都要加上,否则一change就触发,会死机 Application.EnableEvents = False Call 输入条件自动完成筛选 ' 打开事件触发 Application.EnableEvents = True End Sub
Sub 输入条件自动完成筛选() '清除掉L列及往后的数据 Sheets(1).Range("L1:Q" & Sheets(1).Range("a65536").End(xlUp).Row).Clear ' 筛选表1的第4列,关键词为i2的值 Sheets(1).Range("a1:f" & Sheets(1).Range("a65536").End(xlUp).Row).AutoFilter Field:=4, Criteria1:=Range("i2") Sheets(1).Range("a1:f" & Sheets(1).Range("a65536").End(xlUp).Row).Copy Range("L1") ' 关闭筛选 Sheets(1).Range("a1:f" & Sheets(1).Range("a65536").End(xlUp).Row).AutoFilter End Sub
自动备份。
1 2 3 4 5
' 在工作薄触发事件中 Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean) ' Format把日期时间数据转换成一个字符串 ThisWorkbook.SaveCopyAs "d:\temp\" & Format(Now(), "yyyymmddhhmmss") & ".xls" End Sub
1.6 VBA中使用公式
1.6.1 统计人数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Sub 统计人数() Dim i, k, l, m As Integer ' 遍历工作表 For i = 2 To Sheets.Count ' 统计所有工作表中A列中有数据的个数 k = k + Application.WorksheetFunction.CountA(Sheets(i).Range("a:a")) - 1 ' 统计所有表中F列为“男”的个数 l = l + Application.WorksheetFunction.CountIf(Sheets(i).Range("f:f"), "男") m = m + Application.WorksheetFunction.CountIf(Sheets(i).Range("f:f"), "女") Next
Sheets(1).Range("d26") = k Sheets(1).Range("d27") = l Sheets(1).Range("d28") = m
Sub 跨表查询数据() ' 运行错误,则继续执行出错语句的下面的那句 On Error Resume Next
' 清除d14的姓名数据 Sheets(1).Range("d14").ClearContents
Dim i As Integer For i = 2 To Sheets.Count Sheets(1).Range("d14") = Application.WorksheetFunction.VLookup(Sheets(1).Range("d9"), Sheets(i).Range("a:h"), 5, 0) Sheets(1).Range("d16") = Application.WorksheetFunction.VLookup(Sheets(1).Range("d9"), Sheets(i).Range("a:h"), 6, 0) Sheets(1).Range("d18") = Application.WorksheetFunction.VLookup(Sheets(1).Range("d9"), Sheets(i).Range("a:h"), 3, 0) Sheets(1).Range("d20") = Application.WorksheetFunction.VLookup(Sheets(1).Range("d9"), Sheets(i).Range("a:h"), 8, 0) Sheets(1).Range("d22") = Sheets(i).Name ' 如果查询到数据,则跳出循环 If Sheets(1).Range("d14") <> "" Then Exit For End If Next
End Sub
1.6.3 Instr 和 Split
Instr用于寻找指定字符; Split用于切片字符串,分割为字符数组。
1 2 3 4 5 6 7 8 9 10
Sub 寻找字符() ' 寻找@字符 Range("a1").Value = VBA.Strings.InStr(Range("a2"), "@") End Sub
Sub 切片() ' 通过指定分隔符,把一个字符串分割成字符串数组,下标从0开始 Range("b2") = Split(Range("a2"), "-")(1) End Sub
1.6.4 常用的几类VBA函数
With VBA 函数|说明 -|- .Strings|文本函数 .Math|数学函数 .DateTime|日期时间 .FileSystem|文件信息 .Financial|财务函数 .Information|信息函数 .Interaction|交互函数 End With
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Sub 转换日期() ' 将20200102转换为2020-01-02 With Sheets(1) For i = 2 To .Range("a65536").End(xlUp).Row ' DateSerial函数返回包含指定的年、月、日的变量 .Range("b" & i) = DateSerial(Left(.Range("a" & i), 4), Mid(.Range("a" & i), 5, 2), Right(.Range("a" & i), 2)) Next End With End Sub
Sub 身份证号提取生日() With Sheets(2) For i = 2 To .Range("a65536").End(xlUp).Row .Range("b" & i) = DateSerial(Mid(.Range("a" & i), 7, 4), Mid(.Range("a" & i), 11, 2), Mid(.Range("a" & i), 13, 2)) Next End With End Sub
1.7 其他
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Sub 批量创建文件夹() On Error Resume Next Dim path As String ' //路径是当前文件所在路径的temp文件夹下 myPath = ThisWorkbook.path & "\temp\" If Dir(myPath) = "" Then '// 文件夹不存在就建立 MkDir mypath End If For i = 1 To Range("A65536").End(xlUp).Row myFilename = myPath & Range("A" & i).Value '//A列 If Dir(Name) = "" Then '// 文件夹不存在就建立 VBA.MkDir (myFilename) End If Next End Sub
二: 进阶
2.1 函数和过程
函数有返回值,过程可以无返回值。VBA中直接函数名=返回值。
2.1.1 简单的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Function 转美金(x) 转美金 = x / 6.03 - x * 0.03 End Function
Function 性别(str As String) If str = "男" Then 性别 = "先生" ElseIf str = "女" Then 性别 = "女士" End If End Function
Function 日期转换(str As String) 日期转换 = DateSerial(Left(str, 4), Mid(str, 5, 2), Right(str, 2)) End Function
Function 截取字符(str As String, str1 As String, i As Integer) 截取字符 = Split(str, str1)(i - 1) End Function
2.1.2 创建表通用过程
创建表的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Sub 创建表(str As String) Dim sht As Worksheet ' 表名重复,则k=1 For Each sht In Sheets If sht.Name = str Then k = 1 End If Next ' 表名不重复,则新建表 If k = 0 Then Sheets.Add after:=Sheets(Sheets.Count) Sheets(Sheets.Count).Name = str End If
End Sub
2.1.3 加载宏
将有代码的工作簿存为.xla为后缀的文件,存放文档地点默认,即可保存为加载宏。
直接点击加载项就可以调用。
以后每次打开新的工作簿都可以调用此代码。
2.2 合并多个文件
2.2.1 对象赋值给变量
Set的用法
1 2 3 4 5 6 7 8 9
Sub 对象赋值给变量() Dim i As Integer Dim sht As Worksheet For i = 2 To 5 ' 用set将对象赋值给变量 Set sht = Sheets.Add sht.Name = Sheet1.Range("a" & i) Next End Sub
2.2.2 Dir,路径的使用
1 2 3 4 5 6 7 8 9 10 11 12 13
Sub dir的用法() Dim i As Integer For i = 1 To 5 ' dir有则返回文件名,无则返回空值;*为通配符,xlsx,xls都可以 ' 若有n个文件符合,第一个dir加路径返回第一个值,第二个dir直接返回第二个值, ' 到第n+1个dir返回空值,n+2个dir返回错误 If Dir("d:\data\" & Range("a" & i) & ".xls*") = "" Then Range("b" & i) = "无此文件" Else Range("b" & i) = "有文件" End If Next End Sub
Sub 遍历指定文件夹的文件() Dim str As String Dim wb As Workbook str = Dir("d:\data\*.xls*") For i = 1 To 100 ' 遍历打开指定文件夹内的所有xls*文件 Set wb = Workbooks.Open("d:\data\" & str)
' 这里该干嘛干嘛
' 关闭打开的工作簿 wb.Close ' dir不带参数,返回下一个符合的文件名 str = Dir ' dir返回空,则下一个dir会出错,所有需要跳出循环 If Dir = "" Then Exit For End If Next End Sub
2.2.3 多文件合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Sub 多文件合并() Dim str As String Dim wb As Workbook Dim sht As Worksheet str = Dir("d:\data\*.xls*") For i = 1 To 100 Set wb = Workbooks.Open("d:\data\" & str) For Each sht In wb.Sheets ' 存放在当前活动工作薄的最后一张表 sht.Copy after:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count) ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count).Name = Split(wb.Name, ".")(0) & "_" & sht.Name Next wb.Close str = Dir If str = "" Then Exit For End If Next End Sub
2.2.4 查找的标准化写法
1 2 3 4 5 6 7 8 9 10
Sub 使用查找功能() ' find标准化写法,否则找不到会报错 Dim rng As Range ' find返回一个单元格,赋值于对象 Set rng = Range("d:d").Find(Range("l3")) ' 否否得有,为有数据 If Not rng Is Nothing Then Range("m3") = rng.Offset(0, 3) End If End Sub
2.3 VBA数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Sub 使用数组提升运行效率() Dim i, k Dim t Dim str As String Dim arr() ' 用timer计算程序运行时间 t = Timer ' 数组 arr() = Range("g1:j200000") str = Range("n5") For i = 2 To 200000 If arr(i, 1) = str Then k = k + arr(i, 4) End If Next Range("p5") = k MsgBox Timer - t End Sub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Sub 小示例() Dim arr() Dim j, i As Integer j = Range("a65536").End(xlUp).Row - 1 ' 重新定义用ReDim ReDim arr(1 To j)
For i = 1 To j arr(i) = Range("b" & i + 1) * Range("c" & i + 1) Next ' 用自带的函数要加WorksheetFunction. Range("h3") = Application.WorksheetFunction.Max(arr) ' Match用于返回一个值在指定数组中的位置 Range("h2") = Range("a" & Application.WorksheetFunction.Match(Range("h3"), arr, 0) + 1) ' UBound返回数组上限和LBound下限 MsgBox LBound(arr) End Sub
Sub 排列组合计算4个数相加等于124704() Dim i, j, k, l As Integer Dim arr() t = Timer arr = Range("a1:a80") For i = 2 To 80 For j = 2 To 80 For k = 2 To 80 For l = 2 To 80 If arr(i, 1) + arr(j, 1) + arr(k, 1) + arr(l, 1) = 124704 Then Range("f3") = arr(i, 1) Range("g3") = arr(j, 1) Range("h3") = arr(k, 1) Range("i3") = arr(l, 1) ' 找到就用goto跳出循环 GoTo 100 End If Next Next Next Next