3.1-项目介绍 3.2-项目架构 3.2.1-系统可见部分 3.2.2-项目代码逻辑 3.3-代码 3.4-看代码前的几个基础知识 3.4.1-Sub间的调用 3.4.2-工作表某行最大列 3.4.3-代码窗口的两种视图 3.4.4-代码调试 3.4.5-代码字体等设置 3.5-本章示例代码详解 3.5.1-代码结构解析 3.5.2-代码解析:S01_初始化 3.5.3-代码解析:S02_读入数据 3.5.4-代码解析:S0201_文件处理 3.5.5-代码解析:S03_计算 3.6-本章小结 ------------------------------ 3.1-项目介绍 引言 本章依然采用一个小项目的方式,介绍新的知识点。本章介绍的知识点如下: 读入Excel文件 文件及文件夹操作 时间获取及字符串转换 代码调试 弹窗 某工厂有员工甲乙丙丁,分别手工制作同一种小蛋糕。现在有4个Excel文件,分别记录每位员工制作的小蛋糕重量,如图3-1所示。工厂对于小蛋糕的重量有一个标准:23~27g之间为合格,其余为不合格。通过反馈的Excel数据,分别计算出每名员工的小蛋糕重量的符合率,对于符合率较低的员工进行再培训。 符合率 = 重量在标准内的蛋糕数目 / 小蛋糕总数 小蛋糕因为口味不错,即将扩产,目前正在招聘新员工。对于编制的VBA系统有一个延伸的需求,适用于任意多人的场景。 图3-1 小蛋糕重量 3.2-项目架构 3.2.1-系统可见部分 3.2.2-项目代码逻辑 3.2.1-系统可见部分 新建立一个Excel文件来编制VBA系统,取名为小蛋糕重量符合率自动计算系统,简称符合率计算系统。该Excel文件包含两个工作表:操作界面和数据缓存,如图3-2所示。 操作界面:如图3-3所示 A2-C1048576区域,显示符合率信息,适用于更多员工的情况 K8-K9区域:质量标准放置在单元格位置,可以人工修改,标准自定义 K2-K4区域:按钮点击提示区,告知当前点击到哪一步,防止误点击 数据缓存:默认是一张空表,所有外部的Excel数据首先读入该表格,然后再进行处理,减少跨Excel的计算 图3-2 工作簿构成工作表 图3-3 操作界面 从图3-3可以看到三个按钮外有一个框,这是在开发工具菜单栏下-插入-分组框,如图3-4所示,只是为了更加好看,可加可不加。 图3-4 分组框 3.2.2-项目代码逻辑 如操作界面区域按钮所显示的,整个代码分为3个部分: 清空上一次使用的数据,该系统可能在不同时候使用,例如每周使用一次,为了避免上次数据带来的干扰,先清除,包括以下两个部分 操作界面工作表:A-C列输出符合率区域;命令提示区K2-K4 数据缓存工作表:全表清空 读入员工数据,将员工B列的有效数据依次复制到数据缓存表 计算:对数据缓存表中的每名员工依次进行计算,并将结果写入操作界面工作表 3.3-代码 初学者,建议将以下代码打印出来,拿出笔一行一行去看,当然后文我们一是一行一行解读的 Sub S01_初始化() ' 清空 工作表操作界面 上次遗留的信息 Set shtFirst = ThisWorkbook.Worksheets("操作界面") maxRow = shtFirst.Cells(Rows.Count, "B").End(xlUp).Row If maxRow > 2 Then shtFirst.Range("A2:C" & maxRow).ClearContents End If shtFirst.Range("K2:K4").ClearContents '清空 工作表数据缓存 所有信息 Set shtData = ThisWorkbook.Worksheets("数据缓存") shtData.Cells.ClearContents shtFirst.Range("K2") = "已点击" End Sub Sub S02_读入数据() Dim longName As String Dim shortName As String Dim selectFile Dim fso Dim shtFirst Set fso = CreateObject("Scripting.FileSystemObject") Set shtFirst = ThisWorkbook.Worksheets("操作界面") With Application.FileDialog(msoFileDialogOpen) .Title = "选择文件" .AllowMultiSelect = True .Filters.Clear .Filters.Add "Excel文件", "*.xlsx" .FilterIndex = 2 '默认的文件筛选条件的索引号 .InitialFileName = ThisWorkbook.Path & "\" & "【1】输入" .Show For Each selectFile In .SelectedItems longName = selectFile shortName = fso.GetFile(longName).Name Call S0201_文件处理(longName, shortName) Next End With shtFirst.Range("K3") = "已点击" End Sub Sub S0201_文件处理(longName, shortName) Set wb = Workbooks.Open(longName) '打开工作簿 Set sht = wb.Worksheets(1) '获取员工姓名 employeeName = Split(shortName, "-")(0) Debug.Print (employeeName) '获取输入Excel最大行 maxRow = sht.Cells(Rows.Count, "B").End(xlUp).Row ' 拟输入列列号 Set shtData = ThisWorkbook.Worksheets("数据缓存") a1Value = shtData.Range("A1") If a1Value = "" Then inputCol = 1 Else inputCol = shtData.Cells(1, Columns.Count).End(xlToLeft).Column + 1 End If shtData.Cells(1, inputCol) = employeeName ' 复制每个员工的数据至目标工作表 sht.Range("B2:B" & maxRow).Copy Destination:=shtData.Cells(2, inputCol) wb.Close End Sub Sub S03_计算() Set shtFirst = ThisWorkbook.Worksheets("操作界面") up_tol = shtFirst.Range("K8") down_tol = shtFirst.Range("K9") Set shtData = ThisWorkbook.Worksheets("数据缓存") maxCol = shtData.Cells(1, Columns.Count).End(xlToLeft).Column inputRow = 2 ' 对数据缓存工作表数据按列遍历,每列按照行遍历 For j = 1 To maxCol Step 1 maxRow = shtData.Cells(Rows.Count, j).End(xlUp).Row qualifiedCount = 0 totalCount = 0 employeeName = shtData.Cells(1, j) For i = 2 To maxRow Step 1 weightValue = shtData.Cells(i, j) If weightValue <= up_tol And weightValue >= down_tol Then qualifiedCount = qualifiedCount + 1 End If totalCount = totalCount + 1 Next i qualifiedRate = Round(qualifiedCount / totalCount, 3) ' 写入数据 shtFirst.Cells(inputRow, "A") = inputRow - 1 shtFirst.Cells(inputRow, "B") = employeeName shtFirst.Cells(inputRow, "C") = qualifiedRate inputRow = inputRow + 1 Next j shtFirst.Range("K4") = "已点击" End Sub 以上代码在一个模块中,如图3-5所示,共有4个Sub构成,分别如下。 S01_初始化() Sub S02_读入数据() S0201_文件处理(longName, shortName) S03_计算() 操作界面按钮关联其中的S01/S02/S03即可,其中S0201在S02中进行调用。 3.4-看代码前的几个基础知识 3.4.1-Sub间的调用 3.4.2-工作表某行最大列 3.4.3-代码窗口的两种视图 3.4.4-代码调试 3.4.5-代码字体等设置 3.4.1-Sub间的调用 如上所述,本章代码涉及4个Sub过程,其中Sub-S0201被Sub-S02调用,调用方式如下:Call Sub名称 Call S0201_文件处理(longName, shortName) 一个完整的代码需要的实现的功能有很多,我们可以将所有代码写在一个Sub中,但是后期的维护优化将会非常困难。就像一台汽车有很多零件,当某个零件损坏时我们可以花很少的成本进行维修,而不是直接重新买一台汽车。如何写代码?这里我们联想到积木拼装的方法,将整个代码分解成多个功能块,每个功能块只负责实现一部分功能,所有功能块组合实现全部功能。当某个功能块出现问题,我们只需要关注该部分即可,而不需要通读全篇代码,一行一行排查。 在VBA中每一个Sub过程即为一个功能块,实现特定功能,对于重复且需要被在不同地方反复使用的功能模块,特别建议将其独立为一个Sub,当然每一个Sub代码都不宜过长也不宜过短,这里就要看个人的经验了。Sub之间除了顺序执行,也有可能存在一些调用关系。 那么问题来了,不同Sub之间如何调用?如果每次调用需要的功能虽然一样,但是某些变量的取值不同,就像长方形面积S = 长a * 宽b,这就像一个独立的模块,计算不同长方形该公式一直成立,但是针对具体的长方形,我们需要使用具体的长度如3m,宽度如4m。在调用S0201过程中,我们也传递了两个参数:longName, shortName。本章中4个Sub间关系如图3-6 图3-6 Sub间关系 3.4.2-工作表某行最大列 在数据缓存工作表,每一列表示一个员工生产的小蛋糕信息,那么具体有多少位员工呢?其实就是该工作表第一行非空列数。如何获取某一行最大列也是一个比较常见的需求,有效的减少不必要的循环,提升代码运行效率。 maxCol = shtData.Cells(1, Columns.Count).End(xlToLeft).Column 以上代码翻译成汉语: 第1行最大列 = 工作表.单元格(第1行,最大列).从最大列向左数到最后一列有信息的列.列号 对比如何获取最大行,是否看出了区别? maxRow = shtYuwen.Cells(Rows.Count, "B").End(xlUp).Row 获取最大行,固定某一列,从下往上(xlUp)数到的第一个非空行,最终取值和选择列相关 获取最大列,固定某一行,从右往左(xlToLeft)数到的第一个非空列,最终取值和选择行相关 3.4.3-代码窗口的两种视图 在上一个示例中,所有的代码都是写在一个Sub内,本示例涉及到4个Sub,代码的行数有显著增长,我们在使用鼠标滚动代码过程中,会跳跃的比较快,如果在某个时间段只是关注某一个Sub,怎么让代码窗口中只显示该过程,其它过程不出现呢? 方法如下:将鼠标放置到你想显示的Sub中任一行代码,点击代码窗口左下角的过程视图按钮,如图3-7所示。 那么此时又想看整个模块的代码呢?同样点击左下角的全模块视图,如图3-8所示。 图3-7 代码窗口的过程视图按钮 图3-8 代码窗口的全模块视图按钮 又问是不是必须要人工找到自己所希望看到的Sub过程呢,假如代码非常多,是不是找起来也很费劲。其实不必,代码窗口的右上角过程框展示了该模块的所有Sub过程,可以在这里进行下拉列表选择,如图3-9所示。 图3-9 代码窗口的过程选择区 3.4.4-代码调试 写代码的过程中难免有错误,那么如何发现这些错误呢?这里介绍几个调试支持工具: 逐语句调试:平常代码点击运行,瞬间结束,完全不知道过程。通过手动点击一行一行运行判断代码是不是按照预期的方式运行。将鼠标定位到拟运行的Sub过程,点击调试工具栏的逐语句按钮,如图3-10所示 本地窗口:代码调试过程中,可以看到每个变量的取值及数据类型。执行逐行调试时,打开本地窗口,就可以看见每个变量的最新取值,如图3-11所示。本地窗口包括3列:表达式、值、类型 断点:一行一行运行代码有的时候太累,直接将代码运行可能出错的前几步,然后再分步运行。点击调试工具栏上的运行子过程按钮后,会运行到第一个断点处停下来。如果有多个断点,再点击运行子过程按钮,会运行到当前过程(或者通过Call进入下个Sub)的下个断点处。设置断点的方法,鼠标左键点击图3-12红点区域即可,在鼠标点击所在行设置断点,不同行点击即在不同行设置断点。 立即窗口:本地窗口只保留变量的最新取值,如果想将过程数据打印出来,可以使用Debug.Print将变量取值输出到立即窗口,如图3-13所示。如果发现找不到立即窗口,可以通过菜单视图-选择立即窗口 3.4.5-代码字体等设置清晰好看的代码让工作事倍功半,尤其对于后续的阅读来说。如何设置呢?菜单工具-选项,如图3-14所示。 在弹出的窗口中,设置编辑器,建议不要勾选自动语法检测,不要勾选要求变量声明,Tab宽度设置为4。 在弹出的窗口中,设置编辑器格式,如字体等。根据个人喜好及屏幕大小合理设置。 3.5-本章示例代码详解 3.5.1-代码结构解析 3.5.2-代码解析:S01_初始化 3.5.3-代码解析:S02_读入数据 3.5.4-代码解析:S0201_文件处理 3.5.5-代码解析:S03_计算 3.5.1-代码结构解析 本章涉及代码3大主体功能,接下来的文章会依次介绍: 1)S01_初始化。因为该工具会在不同时间段重复使用,为了防止上次的数据影响到本次的使用,有必要进行初始化,删除之前遗留的不必要数据 2)S02_读入数据。被统计人员的数据,是单独放在一个个Excel文件中,需要逐个录入。这里数据源的标准化是需要事前规范好的。在做任何信息化系统前,数据源的标准化都是需要认真考虑的,标准化的数据将会大大降低系统编制的复杂程度 3)S03_计算。对导入的数据分别计算符合率。所有的操作都是对数据缓存工作表的操作,大大提升处理速度。 3.5.2-代码解析:S01_初始化 该过程的实现的功能如下: 1)清空上一次遗留的合格率信息,即清除操作界面工作表A2至C列最大行区域 2)虽然本示例只有3个按钮,但是为了防止重复点击和遗漏点击,每点击一个按钮需要做出提示,即分别在操作界面工作表K2:K4区域记录已点击。在本过程中首先将上一次操作的信息清除掉,即操作界面工作表K2:K4区域清空 3)清空数据缓存工作表 4)第1个按钮点击后在K2单元格写入已点击 代码前面的序号和 | 只是方便记录其代码行数,方便后续写代码说明,真实使用过程中无需序号和前面的 |。 代码如下 1 |Sub S01_初始化() 2 | ' 清空 工作表操作界面 上次遗留的信息 3 | Set shtFirst = ThisWorkbook.Worksheets("操作界面") 4 | maxRow = shtFirst.Cells(Rows.Count, "B").End(xlUp).Row 5 | If maxRow > 2 Then 6 | shtFirst.Range("A2:C" & maxRow).ClearContents 7 | End If 8 | shtFirst.Range("K2:K4").ClearContents 9 | 10 | '清空 工作表数据缓存 所有信息 11 | Set shtData = ThisWorkbook.Worksheets("数据缓存") 12 | shtData.Cells.ClearContents 13 | 14 | shtFirst.Range("K2") = "已点击" 15 | 16 |End Sub 以上代码的中文解析: 1 |Sub S01_初始化() 2 | ' 注释 3 | 定义工作表shtFirst,表示操作界面工作表 4 | 获取操作界面工作表B列最大行maxRow 5 | 判断开始:maxRow是否大于2:如果小于2表示该表没有数据,无需操作 6 | 操作界面工作表的A2至C列最大行区域全部清空,删除数据 7 | 判断结束: 8 | 操作界面工作表的K2至K4区域数据全部删除 9 | 10 | '注释 11 | 定义工作表shtFirst,表示数据缓存工作表 12 | 清空数据缓存工作表所有数据 13 | 14 | 操作界面工作表的K2单元格赋值"已点击" 15 | 16 |End Sub 关键代码解读 清空工作表某区域单元格的数据,构成方式:单元格区域.ClearContents,其实从英文释义也可以快速理解为清空内容。关于单元格区域本Sub中有两种方式,如下所示 shtFirst.Range(“A2:C” & maxRow).ClearContents shtData.Cells.ClearContents 第一种是明确单元格区域Range(“A2:C” & maxRow),这种方式其实就是指定一个矩形的左上角和右下角,如图3-17所示。选择单元格区域A2至C11矩形区域,只需指定左上角及右下角位置即可 第二种指的是全表,使用表.Cells即可。 图3-17 单元格区域表示 3.5.3-代码解析:S02_读入数据 该过程的实现的功能如下: 1)弹出文件选择框 2)获取已选择文件信息 3)将选择文件的绝对地址和文件名信息作为参数传给过程S0201_文件处理 4)在K3单元格写入已点击 代码如下 1 | Sub S02_读入数据() 2 | Dim longName As String 3 | Dim shortName As String 4 | Dim selectFile 5 | Dim fso 6 | Dim shtFirst 7 | 8 | Set fso = CreateObject("Scripting.FileSystemObject") 9 | Set shtFirst = ThisWorkbook.Worksheets("操作界面") 10 | 11 | With Application.FileDialog(msoFileDialogOpen) 12 | .Title = "选择文件" 13 | .AllowMultiSelect = True 14 | .Filters.Clear 15 | .Filters.Add "Excel文件", "*.xlsx" 16 | .FilterIndex = 2 '默认的文件筛选条件的索引号 17 | .InitialFileName = ThisWorkbook.Path & "\" & "【1】输入" 18 | .Show 19 | 20 | For Each selectFile In .SelectedItems 21 | longName = selectFile 22 | shortName = fso.GetFile(longName).Name 23 | Call S0201_文件处理(longName, shortName) 24 | Next 25 | End With 26 | 27 | shtFirst.Range("K3") = "已点击" 28 | End Sub 代码中文解析 1 | Sub S02_读入数据() 2 | 申明变量longName为字符串类型 3 | 申明变量shortName为字符串类型 4 | 申明变量selectFile 5 | 申明变量fso 6 | 申明变量shtFirst 7 | 8 | 定义变量fso为FileSystemObject对象,用于对文件或者文件夹进行操作 9 | 定义工作表shtFirst,表示操作界面工作表 10 | 11 | With开始:使用FileDialog(msoFileDialogOpen)打开一个窗口 12 | 定义打开窗口标题为:选择文件 13 | 是否允许多选文件:是 14 | 清空文件选项 15 | 新增Excel文件,尾椎为xlsx的选项 16 | 可省略,应与filter联合使用,此处未发挥作用 17 | 初始打开位置为:当前文件下【1】输入文件夹 18 | 显示 19 | 20 | 循环开始:对于选择的文件进行遍历循环 21 | 定义变量longName为选择的文件 22 | 获取文件的名称 23 | 调用过程S0201_文件处理,变传递2个参数 24 | 循环结束 25 | With结束 26 | 27 | 工作表shtFirst的K3单元格赋值已点击 28 | End Sub 关键代码解读 1)第11-25行:With…End With,第11行至25行,等价为以下代码。目标在于重复的代码只需要写一次,了解即可,新手无需深究。 Set mso = Application.FileDialog(msoFileDialogOpen) mso.Title = "选择文件" mso.AllowMultiSelect = True mso.Filters.Clear mso.Filters.Add "Excel文件", "*.xlsx" mso.FilterIndex = 2 mso.InitialFileName = ThisWorkbook.Path & "\" & "【1】输入" mso.Show For Each selectFile In mso.SelectedItems longName = selectFile shortName = fso.GetFile(longName).Name Call S0201_文件处理(longName, shortName) Next 2)第8行:Set fso = CreateObject("Scripting.FileSystemObject"),定义了一个文件系统对象。用来对文件和文件夹进行操作,例如获取文件名称等。对于很多常见操作,如文件操作、字典操作等,VBA提供了一些工具包,通过引用这些工具包就可以使用,大大提升工作效率。 关于工具包的引用有两种方法,一种通过代码的方式,如本文中所示。另外通过菜单工具-引用,如图3-18所示。 在弹出的窗口中勾选Microsoft Scripting Runtime,如图3-19所示。另外需要在代码中定义fso,然后就可以使用相关功能。推荐使用第一种方法,这样将程序发给其他人的时候可以直接使用。第二中方法,当程序文件发送给另外人员使用时,也许要使用人员的电脑上在VBE界面中操作绑定。 Dim fso As New Scripting.FileSystemObject 3)第11行:
若希望支持多种文件的选择, 4)第17行: 3.5.4-代码解析:S0201_文件处理
当一个需求比较复杂时,我们会将其分隔成几个Sub过程去完成,某一个过程对应的功能可能被重复调用,只是每次使用的参数不同,这样我们就需要带参数的Sub过程。如本示例中,需要将不同员工的数据读入数据缓存工作表。对于每个员工的操作基本是一样的,每名员工信息占据一列,依次写入。 基本逻辑过程如下: 1)打开员工的数据工作簿 2)获取当前数据缓存工作表最大列maxCol 3)将新员工信息写入maxCol+1列 代码如下 1 Sub S0201_文件处理(longName, shortName) 2 Set wb = Workbooks.Open(longName) '打开工作簿 3 Set sht = wb.Worksheets(1) 4 '获取员工姓名 5 employeeName = Split(shortName, "-")(0) 6 Debug.Print (employeeName) 7 '获取输入Excel最大行 8 maxRow = sht.Cells(Rows.Count, "B").End(xlUp).Row 9 10 ' 拟输入列列号 11 Set shtData = ThisWorkbook.Worksheets("数据缓存") 12 a1Value = shtData.Range("A1") 13 If a1Value = "" Then 14 inputCol = 1 15 Else 16 inputCol=shtData.Cells(1,Columns.Count).End(xlToLeft).Column + 1 17 End If 18 19 shtData.Cells(1, inputCol) = employeeName 20 21 ' 复制每个员工的数据至目标工作表 22 sht.Range("B2:B" & maxRow).Copy Destination:=shtData.Cells(2, inputCol) 23 24 wb.Close 25 End Sub 代码中文解析 1 Sub S0201_文件处理(longName, shortName) 2 打开文件地址为longName的Excel工作簿,用变量wb表示 3 定义工作表sht,表示wb工作簿的第1个工作表,该工作簿只含有1个工作表 4 '获取员工姓名 5 表示文件名的字符串,使用Split函数进行分割,获取员工姓名 6 输出显示员工姓名 7 '获取输入Excel最大行 8 sht工作表B列最大行 9 10 ' 拟输入列列号 11 定义数据缓存工作表为shtData 12 获取shtData工作表A1单元格内容a1Value 13 判断开始:a1Value是否为空 14 如果是,则inputCol = 1 15 如果不是 16 inputCol为shtData工作表第1列最大行+1 17 循环结束 18 19 shtData单元格(行号=1,列号=inputCol)赋值员工姓名employeeName 20 21 ' 复制每个员工的数据至目标工作表 22 将员工工作表B2至B最大行区域复制,在单元格(行号=2,列号=inputCol)处粘贴 23 24 关闭工作簿wb 25 End Sub 其实就是将员工生产的小蛋糕重量信息分别复制到数据缓存工作表,因为不知道员工有多少信息(即多少行),以及不知道数据缓存工作表已有多少信息。所以需要找到每名员工有多少行信息,以及当前数据缓存表中当前写到哪一列。 关键代码解读 1)第2行: 2)第3行: 3)第5行: 数组是什么?可以看做一个容器,可以将数据放入进去暂存,当程序运行结束后,自动消失,但对它的读写速度远远高于对于Excel文件的读写。这里先只介绍一维数组,类似于Excel中的一行,每个单元格可以分别写入信息,如图3-24所示。通过关键字Array定义一个新数组,数组有如下特征:
4)第22行: 3.5.5-代码解析:S03_计算 该Sub过程的作用是计算出每一个员工的符合率。基本逻辑过程如下: 1)获取小蛋糕重量的标准信息,即公差上下限,超出则该小蛋糕不合格 2)两层循环,分别计算出每个员工生产的小蛋糕的重量的符合率: 2.1)外层循环,对数据缓存工作表的列进行循环,即对每名员工进行循环 2.2)内层循环,一个员工有多个数据,对每一行进行循环,计算该名员工生产小蛋糕的符合率 3)将符合率信息写入操作界面 代码如下 1 Sub S03_计算() 2 Set shtFirst = ThisWorkbook.Worksheets("操作界面") 3 up_tol = shtFirst.Range("K8") 4 down_tol = shtFirst.Range("K9") 5 6 Set shtData = ThisWorkbook.Worksheets("数据缓存") 7 8 maxCol = shtData.Cells(1, Columns.Count).End(xlToLeft).Column 9 10 inputRow = 2 11 12 ' 对数据缓存工作表数据按列遍历,每列按照行遍历 13 For j = 1 To maxCol Step 1 14 maxRow = shtData.Cells(Rows.Count, j).End(xlUp).Row 15 qualifiedCount = 0 16 totalCount = 0 17 employeeName = shtData.Cells(1, j) 18 19 For i = 2 To maxRow Step 1 20 weightValue = shtData.Cells(i, j) 21 If weightValue <= up_tol And weightValue >= down_tol Then 22 qualifiedCount = qualifiedCount + 1 23 End If 24 25 totalCount = totalCount + 1 26 Next i 27 28 qualifiedRate = Round(qualifiedCount / totalCount, 3) 29 30 ' 写入数据 31 shtFirst.Cells(inputRow, "A") = inputRow - 1 32 shtFirst.Cells(inputRow, "B") = employeeName 33 shtFirst.Cells(inputRow, "C") = qualifiedRate 34 35 inputRow = inputRow + 1 36 37 Next j 38 39 shtFirst.Range("K4") = "已点击" 40 End Sub 代码中文解析 1 Sub S03_计算() 2 定义工作表shtFirst,表示操作界面工作表 3 获取公差上限 4 获取公差下限 5 6 定义工作表shtData,表示数据缓存工作表 7 8 获取数据缓存工作表第1行最大非空列maxCol 9 10 初始化inputRow=2,用于向操作界面工作表写入符合率信息,确定行信息 11 12 ' 对数据缓存工作表数据按列遍历,每列按照行遍历 13 循环开始:j从1开始到maxCol,步长为1 14 获取数据缓存表第j列最大行maxRow 15 计数:设置合格蛋糕数为qualifiedCount = 0 16 计数:设置蛋糕总数totalCount = 0 17 获取员工姓名employeeName 18 19 循环开始:i从2开始到maxRow,步长为1 20 获取第j列第i行蛋糕重量weightValue 21 判断开始:如果蛋糕重量小于等于公差上限,且大于等于公差下限,表示重量达标 22 合格蛋糕数+1 23 判断结束 24 25 蛋糕总数+1 26 循环结束 27 28 符合率=合格蛋糕数/蛋糕总数,保留3位小数 29 30 ' 写入数据 31 在操作界面工作表的inputRow行A列写入序号 = inputRow - 1 32 在操作界面工作表的inputRow行B列写入该原工姓名 33 在操作界面工作表的inputRow行C列写入符合率 34 35 inputRow+1 36 37 循环结束 38 39 操作界面的K4单元格赋值已点击 40 End Sub 在本段代码中,有多处寻找位置的代码 例如计算完某员工生产的小蛋糕重量符合率后,如何写入到正确的位置。设置一个变量存储将要写入的行号,从第2行开始写,每写一次,该变量+1,这样就保证了一行一行向下写,不会出现空行或者覆盖上一次写入的情况 其余就是通过最大行最大列来确定位置,进而确认循环范围 关键代码解读 1)第21行:If weightValue <= up_tol And weightValue >= down_tol Then。这里使用了大于等于和小于等于,它们都属于比较运算符。比较运算符返回的结果是布尔数,即False或者True,可以直接用于判断语句,不同的编程语言可能会使用不同的符号表示同一个比较运算符,VBA中使用的比较运算符如下: 1.1)大于:> 1.2)小于:< 1.3)等于:= 1.4)大于等于:>= 1.5)小于等于:<= 1.6)不等于:<> 当有两个条件都必须满足时,可以通过And连接两个条件,同理如果不只2个条件,只需在不同条件间用And连接即可。那么大家自然会问,如果两个条件满足其中任意一个时,怎么写?请看以下示例,输出结果为3 Sub test3() a = 3 If a > 5 Or a <= 5 Then Debug.Print (a) Else Debug.Print ("不满足条件") End If End Sub 2)第28行:qualifiedRate = Round(qualifiedCount / totalCount, 3),该代码的效果是保留几位小数。请看以下示例,输出结果分别为:1.3 ,1.33 ,1.333 Sub test2() a = 4 / 3 b1 = Round(a, 1) b2 = Round(a, 2) b3 = Round(a, 3) Debug.Print (b1) Debug.Print (b2) Debug.Print (b3) End Sub 3.6-本章小结 本章使用一个示例展示了以下知识点,回顾一下是否都已经掌握了,如果没有,不妨回看一下: 1)Sub过程间的调用 2)某工作表某列的非空最大列数获取 3)单元格区域内容清空 4)打开文件窗口 5)FSO文件操作 6)打开另外的Excel文件获取数据 7)复制单元格区域到另外的位置 8)比较运算符 9)小数点位数控制 在Sub过程S03_计算中,可以发现使用到的代码,基本在之前都有过介绍了,其实对于一般需求使用的VBA功能是非常有限的,所以VBA学习起来并不会很复杂,如果只是完成常规的工作。 |
|
来自: asaser > 《 Excel-VBA简明教程》