
简介《Visual Basic编程基础教程》是面向零基础读者、办公自动化初学者及需要维护VBA宏的编程入门者的PDF教程围绕集成开发环境、图形界面构建与基础语法展开帮助读者建立从编写代码、调试到运行程序的基本能力。压缩包内共1个PDF文件约2.17MB单文件承载完整教程便于在电脑或平板上按章节查阅。教程从语言与翻译器概念讲起安排首个编程实验及配套练习并系统讲解变量声明、数值表达式与类型转换、MsgBox与InputBox输入输出、If-Then条件执行、字符串处理、for与while循环、数组和矩阵以及文件读写等知识点书中还配有内置函数、运算符优先级等说明。目前已有112人学习内容组织由浅入深每章后设练习题适合希望结合实例循序练习、夯实VBA编程基础的初学者使用。1. 从集成开发环境到文件一张可视化的 VBA 入门地图Visual Basic 编程入门的路线概括起来就是先用集成开发环境把界面画出来再用变量和表达式把数据存起来然后用条件与循环把流程跑通最后用数组、文件和过程把结果保留下来。教材把它拆成十几章看上去零散实际上是从“会拖控件”到“能写出完整小程序”的一条连贯路径。很多人卡住不是因为语法难而是顺序学反了跳过变量数值处理直接写界面事件代码里全是隐式类型转换一出错连问题出在哪个变量都定位不到。这套内容适合两类人一类是刚接触 VBA想把表格和文档里的重复劳动自动化另一类是被作业或项目推着走需要在一周内把循环语句、条件执行、字符串函数这些高频考点串成体系。教材本身的章节顺序已经隐含依赖关系前面那一章的练习没做透后面示例就只能照抄抄完还是不知道哪一行在干什么。所以学之前先看清地图比急着敲第一条Sub更省时间。2. 数据基础变量声明、数值类型边界与表达式求值顺序所有 VBA 的错误里有一大半不是逻辑写错而是数据没放对地方。数值类型搞错、表达式求值顺序想当然、类型转换随手乱用这三个毛病在初学阶段几乎人人都会犯一次。把基础打好后面的条件、循环、文件读写才有可靠的基础。2.1 变量声明与八种数值类型的边界VBA 的变量可以不声明就用但这恰恰是最容易引发问题的写法。没有Option Explicit时把total打成totol编译器不会报错只会默默创建一个新变量值永远是 0然后你花半小时去找“为什么累加结果为空”。模块顶部的Option Explicit是每个新模块都应该加的第一行它把拼写错误从“难以察觉的 bug”变成“立刻报错的编译错误”。类型占用取值范围典型用途Byte1 字节0 ~ 255二进制字节、小计数Integer2 字节-32768 ~ 32767循环计数、控件索引Long4 字节约 ±21 亿累加、行号、文件长度Single4 字节约 ±3.4E387 位有效数字精度要求低的度量值Double8 字节约 ±1.8E30815 位有效数字一般小数运算Currency8 字节定点数固定 4 位小数金额避免二进制误差Boolean2 字节True-1/ False0标志位、开关String变长极长受内存限制文本、读入的行Option Explicit 强制显式声明拼错变量名立刻编译报错 Sub TypeBoundary() Dim n As Integer -32768 ~ 32767装得下小计数 Dim total As Long 累加结果用它别用 Integer Dim ratio As Double 需要小数精度时用它 Dim ok As Boolean 只有 True(-1) / False(0) n 30000 total n * 1000 危险先按 Integer 算乘法溢出后才赋值给 Long ratio total / 7 除法 / 的结果一定是浮点 ok (ratio 1000) End Sub这段代码的关键在第 9 行n * 1000的两个操作数都是 IntegerVBA 会先按 Integer 做乘法结果 30000000 超出 32767直接抛“溢出”运行时错误total根本没机会接收。修法是把参与运算的一方显式转成 Long写成total CLng(n) * 1000。这条规则同样适用于Single与Double混算——精度低的一方不会自动“升级”而是先把结果压在自己的精度里。2.2 运算符优先级与求值顺序表达式不按数学直觉走是初学阶段最常见的问题来源。2 3 * 4 ^ 2的结果是 50 不是 400因为^先算而Not 3 2的结果也不是你期待的那个因为比较运算优先于Not。优先级运算符说明1最高^乘方2-取负一元负号3*/乘、除结果必为浮点4\整除向零截断5Mod取余符号跟随被除数6-加、减7字符串连接8比较结果为 True/False9Not逻辑非10AndOrXor逻辑与、或、异或Sub Precedence() Dim a As Double, b As Integer, c As Integer a 2 3 * 4 ^ 2 先 4^216再 3*1648最后 2 50 b 7 \ 2 整除结果 3 c -7 Mod 3 取余结果 -1符号跟随被除数 Debug.Print a; b; c End Sub\和Mod这两个运算符在负数上的行为经常被记错整除一律向零截断-7 \ 2得-3不是-4取余的符号跟着左边的数走-7 Mod 3得-1。写“判断能否被整除”时用x Mod 2 0没问题但如果 x 可能是负数就别指望结果总为正。拿不准的时候加括号比背优先级表可靠得多。2.3 类型转换CInt、Int、Fix 与 Val 的差别转换函数看着都是“把小数变整数”行为却完全不同这是考试和实际编码都爱考的点。函数行为2.53.5-2.5CInt四舍六入五取偶银行家舍入24-2Int向下取整向负无穷23-3Fix向零截断23-2Sub Convert() Dim s As String, v As Variant s 12abc Debug.Print Val(s) 12从左往右解析遇到非数字就停 Debug.Print IsNumeric(s) False整串不是合法数字 v 3.7 Debug.Print CInt(v) 4 Debug.Print Int(-2.5) -3 Debug.Print Fix(-2.5) -2 End SubVal是最典型的“不会报错的函数”Val(abc)返回 0Val(12abc)返回 12。用它读用户输入一旦用户填了带单位的字符串比如 “30kg”你会得到 30 而毫无提示。稳妥的写法是先IsNumeric判断整串再用CDbl或CLng转换Val只适合处理格式已经确定的文本。2.4 常量、默认值与变量初始化Const Pi As Double 3.1415926定义的是编译期常量后面再赋值会直接报错这比用变量存“不该变的数”安全。变量的默认值则要背下来数值型初始为 0String 初始为空串Boolean 初始为 FalseVariant 初始为Empty可以用IsEmpty判断。过程里的局部变量每次调用都重新初始化而Static声明的变量会跨调用保留值——这个区别在计数器和累加器场景里特别关键。3. 交互与判定MsgBox、InputBox 的返回值到 If 与 Select Case 的判定链程序要和用户打交道无非两条路弹框问一句、弹框答一句。看着简单但返回值类型、取消行为、逻辑运算符的真值表示这三处全是容易被忽略的陷阱。条件执行则是把“问答”变成“决策”的那一层。3.1 MsgBox 的按钮常量与返回值对应MsgBox的第一个参数是提示文字第二个参数决定显示哪些按钮第三个是标题。真正要记的是返回值点了哪个按钮函数就返回对应的数字常量。按钮常量显示按钮可能返回值vbOKOnly确定vbOK(1)vbOKCancel确定/取消1 /vbCancel(2)vbYesNo是/否vbYes(6) /vbNo(7)vbYesNoCancel是/否/取消6 / 7 / 2vbRetryCancel重试/取消vbRetry(4) / 2Sub Confirm() Dim ans As VbMsgBoxResult ans MsgBox(记录已修改是否保存, vbYesNoCancel vbQuestion, 确认) Select Case ans Case vbYes Debug.Print 保存 Case vbNo Debug.Print 放弃修改 Case vbCancel Debug.Print 返回编辑 End Select End Sub第二个参数用组合图标常量vbQuestion、vbExclamation、vbCritical不要用那是字符串连接。返回值的类型写成VbMsgBoxResult枚举比写Integer可读性好也方便用Select Case分支。判断时尽量用vbYes这类具名常量而不是硬编码6否则半年后回看只会一脸茫然。3.2 InputBox 的返回值陷阱InputBox(prompt, title, default)的返回值永远是 String哪怕用户输入的是数字你也得自己转。更关键的是两件事一是用户点“取消”时返回空串和用户直接点确定但什么都没填返回值一模一样代码无法区分二是用户输入的内容不做任何校验字母、空格、全角符号都可能进来。Sub AskCount() Dim s As String, n As Long s InputBox(请输入要处理的行数1-100, 行数, 10) If Len(Trim(s)) 0 Then MsgBox 未输入内容已取消操作 Exit Sub End If If Not IsNumeric(s) Then MsgBox 请输入数字 Exit Sub End If n CLng(s) If n 1 Or n 100 Then MsgBox 超出允许范围 Exit Sub End If Debug.Print 准备处理 n 行 End Sub这段代码的顺序值得记住先判空Len(Trim(s)) 0再判是不是数字IsNumeric最后判范围。三步缺一不可因为CLng(abc)会抛类型不匹配错误而CLng()同样报错。给InputBox一个非空默认值是个好习惯至少能让用户知道该填什么格式如果业务上必须区分“取消”和“空输入”就别用InputBox改用带“确定/取消”按钮的自定义窗体返回值由按钮的点击事件自己控制。3.3 If-Then 的三种形态与关系表达式If有三种写法单行只处理一个语句、块状带ElseIf和Else、以及嵌套。单行写法If x 0 Then y 1简洁但只能挂一条语句多语句必须换行加End If。关系表达式的六个运算符、、、、、结果都是 Boolean但要注意字符串比较是按字符编码逐位比大小“10” “9” 是成立的因为先比第一个字符1和9。Sub Judge(ByVal score As Double) Dim grade As String If score 0 Or score 100 Then MsgBox 分数不合法 Exit Sub ElseIf score 90 Then grade A ElseIf score 80 Then grade B Else grade C End If Debug.Print grade End SubElseIf链是从上往下依次判断命中一个就跳出整条链所以条件的顺序决定了结果。把score 80写在score 90前面90 分也会被判成 B。这类顺序错误不会报错只会悄悄给出错误结果只能靠自己在写完后逐条走一遍边界值。3.4 逻辑运算符与 VBA 的“真值”VBA 里True的数值是-1不是 1。这不是怪癖而是因为逻辑运算符在整数上做的是按位运算Not 0得到 -1Not -1得到 0。把 Boolean 强转成整数做算术结果常常让人困惑。表达式结果说明True-1所有二进制位为 1Not 0-1按位取反5 And 31按位与0101 0011 00015 Or 37按位或0101 | 0011 01115 Xor 36按位异或不同为 1True And False0False逻辑与Sub Logic() Dim a As Boolean, b As Boolean a True: b False Debug.Print a And b False Debug.Print a Or b True Debug.Print Not a False Debug.Print CInt(True) -1不是 1 Debug.Print (5 And 3) 1按位与不是逻辑判断 End Sub实际写业务判断时And/Or只用来连接关系表达式比如If age 18 And score 60 Then别直接对整数用那是在做位运算。Xor用得少但有个好场景判断“两个条件恰好成立一个”Xor比写两遍And/Or组合更清晰。Not的优先级高于And和Or所以Not a And b等于(Not a) And b拿不准就加括号。3.5 Select Case 的四种 Case 写法与匹配顺序分支超过三个If-ElseIf就开始难读Select Case更合适。它的匹配方式有四种单值、值列表、区间、条件表达式从上到下依次试命中即跳出。Sub Grade(ByVal score As Integer) Dim g As String Select Case score Case Is 90 g 优秀 Case 80 To 89 g 良好 Case 70, 71, 72 值列表逗号分隔 g 中等 Case Else g 待补考 End Select Debug.Print g End SubCase Is 90里的Is不能省写成Case 90是语法错误。区间写法80 To 89包含两端。Case Else不是必需的但强烈建议写上否则所有未匹配的值都静默地什么也不做出问题时连线索都没有。还有一点Select Case只对单个表达式求值一次如果每个分支判断的是不同变量那还是得用If。3.6 常用控件的取值方式界面上的控件分“给人看”和“给人填”两类。Label只显示不能编辑改内容用CaptionTextBox让用户输入取内容用Text。选择类控件里OptionButton的Value是 True/False且必须放在同一个Frame里才互斥CheckBox的Value有三个状态0 未选、1 选中、2 灰显变灰不可用但可见。控件关键属性取值说明LabelCaption显示文本用户不可改TextBoxText用户输入的内容永远是字符串OptionButtonValueTrue / False同组互斥CheckBoxValue0 / 1 / 2未选/选中/灰显通用EnabledFalse 时变灰不可操作通用VisibleFalse 时整个隐藏Enabled False和Visible False的区别常被混用前者是“看得见但点不动”适合表单未填完时禁用提交按钮后者是“彻底不显示”适合按角色隐藏整块区域。二者在布局上的影响也不同Visible False会让控件原先占的位置空出来界面上可能出现一块空白需要配合其他控件的Top/Left手动调整。4. 重复与集合字符串函数、For/Do 循环、数组矩阵与文件读写这一块是 VBA 真正开始“干活”的地方。字符串处理解决文本拆解循环解决重复劳动数组解决批量数据的暂存文件解决数据持久化的问题过程与递归解决代码复用。四者组合起来才是一个完整的“读入—处理—输出”程序。4.1 字符串函数Len、Left、Right、Mid、InStr 的边界规则VB 的字符串函数几乎每个都有边界陷阱最典型的是Mid和InStr的起始位置。函数参数边界行为Len(s)字符串空串返回 0Left(s, n)n 为字符数n 大于长度时返回整串不报错Right(s, n)同上同上Mid(s, start, [len])起始位置从 1 开始省略 len 时取到末尾start 超过长度返回空串InStr([start,] s1, s2)查找子串找不到返回 0不会抛错Sub ParseText() Dim s As String, p As Integer s 张三,技术部,10086 p InStr(s, ,) 返回 3第一个逗号的位置 Debug.Print Left(s, p - 1) 张三 Debug.Print Mid(s, p 1, InStr(p 1, s, ,) - p - 1) 技术部 Debug.Print Mid(s, InStrRev(s, ,) 1) 10086 End Sub这段代码演示了“用分隔符切字段”的标准手法InStr找到分隔符位置Left取它左边Mid从它右边接着取。注意InStr(p 1, s, ,)这种带起始位置的写法第三个参数之前先传起点别把参数顺序记反。最后一行用InStrRev从右往左找正好取最后一个字段。如果字段数量固定这套写法比Split 数组更直观字段数量不定时Split(s, ,)配合UBound更省事。4.2 循环结构For-Next 与 Do-Loop 的四种形态For-Next用于次数已知的循环Do-Loop用于次数未知、靠条件退出的循环。选错结构不会报错但代码会变难读也容易写出死循环。Sub LoopDemo() Dim i As Integer, s As Long For i 1 To 10 Step 2 s s i If s 20 Then Exit For 提前跳出 Next i Debug.Print i, s i 停在 7不是 10 Dim k As Integer k 1 Do While k 3 Debug.Print k k k 1 Loop End Sub第一个循环的关键在注释For正常结束时循环变量停在“终值 步长”的位置本例里是 11因为 10 之后再加 2 超过终值就退出如果中途Exit For跳出i就停在跳出那一刻的值。拿循环变量去做后续计算很容易用到这个“多走一步”的值。结构判断时机可能执行次数适用场景For i a To b Step n每次进循环前0 次或多次a 已越过 b 则 0 次次数已知Do While cond ... Loop循环体前0 次或多次条件不满足就不做Do ... Loop While cond循环体后至少 1 次必须先做一次的菜单、输入Do Until cond ... Loop循环体前0 次或多次条件成立即停语义更直白For的步长写成 0 是经典死循环For i 1 To 10 Step 0会让i永远等于 1永远不超过 10。步长为负时初值必须大于终值循环才会执行For i 10 To 1 Step -1才是倒着数。4.3 数组与矩阵下标下界、ReDim Preserve 与二维遍历Dim a(10) As Integer声明的是11 个元素下标从 0 到 10。这个差异是第一大常见错误来源习惯从 1 开始数的人循环写成For i 1 To 10结果第 0 个元素永远没被赋值输出的最小值总是 0。Sub ArrayDemo() Dim a() As Integer, i As Integer ReDim a(1 To 5) 显式指定下界 1避开 0 For i 1 To 5 a(i) i * i Next i ReDim Preserve a(1 To 8) 保留原有数据只扩容 Debug.Print LBound(a), UBound(a) 1 和 8 Dim m(1 To 3, 1 To 4) As Integer, r As Integer, c As Integer For r 1 To 3 For c 1 To 4 m(r, c) r * c 二维遍历外层行、内层列 Next c Next r End SubReDim Preserve只保留一维数组的数据二维数组里它只能改最后一维的大小改第一维会直接报错。要动态增行常规做法是把矩阵转置后处理或者用元素为数组的数组嵌套数组替代。LBound/UBound是循环边界的可靠来源写For i LBound(a) To UBound(a)就永远不会越界代价是比写死数字略啰嗦。4.4 文件读写Open 的三种模式与 EOF 判断文件操作固定四步Open打开、Write或Print写入或Input/Line Input读取、Close关闭。文件号用FreeFile取别硬编码 1否则两个过程同时操作文件会冲突。模式用途读写语句For Output新建或覆盖写入Write #/Print #For Append追加到末尾Write #/Print #For Input只读打开已有文件Input #/Line Input #For Binary按字节读写Get #/Put #Sub FileDemo() Dim fn As Integer, line As String fn FreeFile Open C:\temp\out.txt For Output As #fn Write #fn, 张三, 100, 3.14 写入带引号、逗号分隔的格式 Close #fn fn FreeFile Open C:\temp\out.txt For Input As #fn Do Until EOF(fn) 一定要先判 EOF 再读 Line Input #fn, line Debug.Print line Loop Close #fn End SubWrite #和Print #的区别必须记住Write #会给字符串自动加双引号、字段之间加逗号所以能用Input #按字段精确读回Print #是原样输出文本读回时只能用Line Input #整行读然后自己用Split拆。两者混用会导致读回来的字段错位这类问题不会报错只会让数据悄悄地错位。4.5 过程与递归Function/Sub 的区别、参数传递与静态变量Sub执行动作不返回值Function通过给函数名赋值来返回结果。参数默认是ByRef传地址过程内修改会影响调用方的变量——这既是便利也是隐患。Function Factorial(ByVal n As Long) As Long If n 1 Then Factorial 1 返回值靠给函数名赋值 Else Factorial n * Factorial(n - 1) 递归调用自身 End If End Function Sub AddOne(x As Integer) 默认 ByRef会改到调用方的变量 x x 1 End Sub Sub Counter() Static hits As Integer Static跨调用保留值 hits hits 1 Debug.Print hits End SubByVal传值、ByRef传地址这是参数传递的核心区别。默认ByRef意味着AddOne y会真的把y加一如果本意只是“借用一下这个值”必须显式写ByVal否则调用方的数据会被意外改写而且要跟踪很久才能发现。递归必须有两个要素一个能终止的基线条件n 1一个每次向基线靠近的调用n - 1缺一个就是无限递归最终因栈溢出崩溃。5. 易漏易错五处最容易出错的地方与补救写法有些知识点单独看都懂一到实际编码就出错。下面这五处是我在复现教材练习时反复出错的地方每一条都按“错在哪、为什么没意识到、怎么改”来整理。5.1 数组下标下界Dim a(10)是 11 个元素易漏点默认下界是 0Dim a(10)得到下标 0~10 共 11 个元素循环写1 To 10会漏掉第 0 个。为什么漏教材前面的示例大多从 1 开始遍历视觉上形成了“数组从 1 开始”的错觉而 VBA 的默认下界其实由模块顶部的Option Base决定默认是 0加了Option Base 1才变成 1。多数人根本没注意过这个语句。怎么补声明时显式写下界Dim a(1 To 10) As Integer让意图写死在代码里遍历一律用LBound(a)到UBound(a)不用魔法数字。如果接手别人的代码先看模块顶部有没有Option Base再决定循环边界。5.2 真值是 -1Not和位运算混着用易漏点True在数值上是 -1Not 0得 -15 And 3得 1 而不是布尔结果。为什么漏其他语言里 True 常等于 1迁移过来就默认CInt(True) 1而And/Or在 VBA 里对整数做的是按位运算只有当操作数是 Boolean 时才表现为逻辑运算。怎么补逻辑判断只连接关系表达式If a 0 And b 0 Then不要直接对整数用And。需要把 Boolean 转成 1/0 时用IIf(flag, 1, 0)或Abs(CInt(flag))不要指望CInt直接给出 1。5.3 InputBox 的“取消”和“空输入”无法区分易漏点用户点取消返回空串用户什么都不填点确定也返回空串代码无法分辨这两种情况。为什么漏InputBox的文档只写了“返回字符串”没强调取消行为而测试时通常只测正常输入很少专门点一次取消。怎么补业务上必须区分时别用InputBox改用自定义窗体加“确定/取消”两个按钮由按钮事件自己设置标志位如果坚持用InputBox至少给一个非空默认值并把空返回值统一当作“取消”处理同时在提示语里写清格式要求。5.4CInt的银行家舍入与Int/Fix的方向差异易漏点CInt(2.5)得 2 不是 3CInt(3.5)得 4Int(-2.5)得 -3Fix(-2.5)得 -2。为什么漏“四舍五入”这四个字在脑子里根深蒂固而CInt用的是四舍六入五取偶负数上Int向负无穷取整、Fix向零截断的差别更容易被忽略。怎么补需要标准四舍五入时用Round或Int(x 0.5)注意负数要单独处理需要“不要小数部分”时明确写Fix别用Int因为负数上两者结果不同。涉及金额的计算统一用Currency避免二进制浮点误差累积。5.5 文件号与Close忘了关就锁死易漏点Open之后没Close文件被占用下一次Open ... For Output直接报“权限被拒绝”或者硬编码文件号#1被其他过程占用。为什么漏程序正常跑完时过程结束会隐式释放所以小脚本里不写Close也不出错一旦中途有Exit Sub或抛错跳走文件句柄就泄漏了。怎么补文件号一律用FreeFile取Open之后立刻在心里记下“这段必须配对Close”。更稳的写法是给关键读写加错误处理在Exit Sub之前和错误处理段里都写一遍Close #fn重复关闭已关闭的文件号不会报错这是少数可以放心冗余的地方。6. 复习清单从默写代码到查漏补缺的六步复习 VBA 最忌讳“看一遍觉得都会”。下面这套流程按天推进每一步都要求动手不看答案。步骤内容自查标准1默写一个含变量声明、If、For的完整Sub不看资料能写出 20 行且无语法错误2说清 8 种数值类型的范围与溢出后果能举出Integer * Integer溢出的例子3手算7 Mod 3、-7 Mod 3、7 \ 2、-7 \ 2四个结果全对且能解释符号规则4写出MidInStr切分“a,b,c”的代码能处理“字段缺失”和“末尾空字段”5写一个把数组写入文件再读回的过程读写结果一致且用了FreeFile和EOF6复述五处易错点每条都能说出“为什么错”和“怎么改”第 1 步值得反复做因为它是所有后续内容的骨架。默写的时候刻意用上Option Explicit逼自己把每个变量都声明出来包括循环变量——这一步能把“拼错变量名”和“类型不匹配”两类错误提前暴露。写完先别运行逐行读一遍重点看If的条件顺序和For的边界然后才按 F5。第 4 步和第 5 步是分水岭。字符串切分和文件读写是“能写小工具”和“只会做作业”之间的差别练的时候别用教材里的示例数据换成自己造的脏数据中间多一个逗号、末尾少一个字段、某一行为空。程序要能跑通而不是一遇到空行就报错。写文件时先在草稿纸上画一遍“写进去长什么样、读出来是什么类型”Write #加了引号、Input #会剥掉引号这条对应关系想清楚后面的问题就少一半。 复习用自测模板把这些空填完就算过了一遍基础 Option Explicit Sub Review() Dim n As Integer, i As Integer, total As Long Dim s As String, arr() As String n 5 For i 1 To n total total i * i 1491625 55 Next i s a,b,c,d arr Split(s, ,) Debug.Print UBound(arr) - LBound(arr) 1 元素个数 4 Debug.Print total, arr(UBound(arr)) 55 和 d End Sub这段模板把变量声明、循环累加、字符串切分、数组边界四件事串在一起每个Debug.Print后面都标了预期结果。跑出来对不上就回到对应章节找原因total不对多半是循环边界或类型溢出UBound算错多半是把元素个数和最大下标混了。自己加两行把结果写进文件再读回来就顺带复习了文件操作。最后一个习惯每次卡住先在纸上写出“此刻每个变量的值、类型、边界”再去看代码。我吃过一次亏一个“求和结果总是少 1”的 bug 查了整整一晚上最后发现是数组下标从 1 开始、循环从 0 开始第 0 个元素装的是上一次的残留值。从那以后我每次写循环前都强制先写LBound和UBound两行再也没在这个坑里摔过第二次。希望帮到你。本文还有配套的精品资源点击获取