VBA錯誤處理不只是讓程式繼續執行。贊贊小屋請ChatGPT撰寫匯率備份程式,卻遇到工作表已存在的誤判,再交叉運用Claude找出變數保留舊參照的原因,加入Set ws = Nothing後,回到Excel實測修正成功。文章依序記錄排查過程,並整理如何向AI提供證據、驗證結果。
一、向ChatGPT提出下載匯率的需求
贊贊小屋先在ChatGPT貼上臺灣銀行的匯率網址,再用一句話交代需求:
傳給ChatGPT的需求https://rate.bot.com.tw/xrt?Lang=zh-TW 希望設計一個 VBA 程式,可以自動去下載臺灣銀行的匯率。寫到工作表中,並且把工作表的名稱設定為「最新匯率」
ChatGPT回覆這個版本適用於Excel 2016,並說明程式會處理三件事:自動連線到臺灣銀行匯率網站、把資料寫進「最新匯率」工作表,而不是目前選取的工作表;如果「最新匯率」已經存在,就清除舊資料後重新下載,不存在就自動建立。
程式碼之後,ChatGPT另外列了「執行前請注意」。贊贊小屋特別留意其中三項:活頁簿要另存成「Excel 啟用巨集的活頁簿(.xlsm)」才能保存VBA程式;這個版本會刪除B至E欄沒有數值的資料列,但網頁匯入後的欄位位置可能隨網站格式改變,實際結果若出現多餘資料或欄位錯位,就要再依匯入結果調整;每次執行都會重新下載網頁資料,並清除「最新匯率」工作表原有內容,所以不要在這張工作表存放需要保留的其他資料。
請AI寫自動化程式時,需求最好把「資料從哪裡來、放到哪裡、再次執行時怎麼處理」三件事交代清楚。這次的第一句話已經包含來源網址與目標工作表名稱,ChatGPT才能順勢補上「工作表不存在就新增、存在就更新」的處理。另外,程式中的ws.Cells.Clear會清除儲存格內容與格式,這也是ChatGPT提醒不要在「最新匯率」放其他資料的原因:它應該當成專門接收下載資料的工作表。從提出需求到取得匯率資料的另一次完整操作,可以參考Excel 自動抓取網頁資料:利用 VBA 與 AI 自動更新 Excel 表格。
二、將VBA程式放入模組並執行
接下來是把ChatGPT給的程式放進Excel;完整的貼上、存檔與執行步驟,也可以參考ChatGPT寫ExcelVBA:1鍵完成自動化的教學。在準備存放匯率資料的活頁簿按Alt+F11,開啟「Microsoft Visual Basic for Applications」編輯器,從上方選單選「插入」→「模組」。左側「專案-VBAProject」下會出現「模組」資料夾與「Module1」,贊贊小屋把程式貼進右側的程式碼視窗,第一行就是Sub 取得臺灣銀行匯率()。程式完整內容如下:
VBA程式|取得臺灣銀行匯率()Sub 取得臺灣銀行匯率()
Dim ws As Worksheet
Dim 網址 As String
Dim 最後列 As Long
Dim i As Long
'設定臺灣銀行匯率網址
網址 = "https://rate.bot.com.tw/xrt?Lang=zh-TW"
'尋找「最新匯率」工作表
On Error Resume Next
Set ws = ThisWorkbook.Worksheets("最新匯率")
On Error GoTo 錯誤處理
'如果工作表不存在,就自動新增
If ws Is Nothing Then
Set ws = ThisWorkbook.Worksheets.Add
ws.Name = "最新匯率"
End If
'清除舊資料
ws.Cells.Clear
'從臺灣銀行網站匯入網頁表格
With ws.QueryTables.Add( _
Connection:="URL;" & 網址, _
Destination:=ws.Range("A1"))
.WebSelectionType = xlAllTables
.WebFormatting = xlWebFormattingNone
.Refresh BackgroundQuery:=False
.Delete
End With
'刪除沒有匯率數字的多餘資料列
最後列 = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
For i = 最後列 To 1 Step -1
If Application.WorksheetFunction.Count( _
ws.Range("B" & i & ":E" & i)) = 0 Then
ws.Rows(i).Delete
End If
Next i
'調整內容
ws.Columns.AutoFit
'回到最新匯率工作表
ws.Activate
MsgBox "臺灣銀行匯率下載完成!", vbInformation
Exit Sub
錯誤處理:
MsgBox "下載匯率時發生錯誤。" & vbCrLf & _
"請檢查網路連線、工作表名稱或網站是否正常。", _
vbExclamation
End Sub
回到Excel按Alt+F8選擇巨集執行,或在編輯器把游標放進Sub內按F5。程式跑完後,Excel新增了「最新匯率」工作表,美金、港幣、英鎊、澳幣等幣別的匯率依序排列,同時跳出訊息「臺灣銀行匯率下載完成!」。
不過仔細看資料,F、G欄是「查詢」兩個字,H到K欄又重複了前面B到E欄的數字,而且第一列直接就是「美金」。所以這時只能說「取得了網頁上的匯率資料」,還不能說已經整理成乾淨的匯率表。「取得資料」和「整理資料」是兩件事,訊息方塊顯示完成,只代表程式跑完,資料是否符合預期,仍要自己核對幣別、欄位與數值。程式裡的ThisWorkbook指的是存放這段巨集的活頁簿,所以模組要插在準備存放資料的那一本檔案裡;存檔時也記得選.xlsm,否則VBA程式不會保留。另外,臺灣銀行匯率頁面說明牌告匯率資訊是靜態顯示,不會隨後續異動自動更新,所以程式取得的是下載當下的網頁資料,不是持續更新的即時報價。
三、複製工作表卻出現已存在的警告
有了每天可更新的「最新匯率」,贊贊小屋接著想把當天的匯率保存下來,作為每日備份。於是再請ChatGPT撰寫第二支程式,需求是:
傳給ChatGPT的第二個需求我希望設計一個 VBA 程式,去複製一個名稱是「最新匯率」的工作表。複製之後,將它重新命名為最新的日期,用 6 個數字(分別是年月日)去命名。
ChatGPT確認採用yymmdd格式,並用表格說明:原始工作表是「最新匯率」,如果執行日期是2026年10月17日,複製後的工作表名稱就是「261017」,原始工作表保留不修改。要注意這裡的261017只是ChatGPT舉的範例日期,贊贊小屋實際執行時,程式產生的名稱是「261027」。
程式貼進模組執行後,Excel沒有複製工作表,而是跳出一個警告視窗:「工作表「261027」已經存在,請勿重複複製!」可是畫面下方的工作表標籤只有「最新匯率」和「工作表1」,根本沒有「261027」。
這個警告不是Excel自己發出的錯誤訊息,而是程式裡用MsgBox寫的提示文字。程式沒有語法錯誤,能夠順利執行,只是用來判斷「今天的工作表是否存在」的邏輯得到了錯誤的結果。這類「能執行、但結果與事實不符」的情況稱為邏輯錯誤,Excel不會主動報錯,必須從程式如何判斷去追查。日期命名採用Format(Date, "yymmdd"),在一般西曆設定下會得到兩位數年、月、日的六位數字串。要注意名稱取自電腦當天的日期,不等於銀行的掛牌日期,如果早上下載、晚上才複製,名稱仍是當天日期。如果想把每天的匯率另外整理成持續累積的紀錄表,可以參考Excel 自動產生報表:用 VBA 建立每日匯率紀錄。
四、依照AI建議檢查隱藏工作表
遇到這個狀況,贊贊小屋先回頭問ChatGPT。附上警告畫面,問:
再問ChatGPT明明沒有這個工作表,為什麼程式執行會顯示有這個工作表?
ChatGPT根據畫面判斷,最有可能是工作表被隱藏了,或是存在於其他開啟的活頁簿,並表示單憑圖片還無法確定。它提供的「方法一」是檢查隱藏工作表:在Excel下方的工作表標籤上按滑鼠右鍵,看選單中有沒有「取消隱藏」,能點選就進去查看清單。這裡可以看到,ChatGPT回覆裡寫的是「261017」,與實際警告中的「261027」不同,不過這不影響它提出的檢查方向。
贊贊小屋照著操作,在標籤上按右鍵,選單中「隱藏」可以使用,「取消隱藏」則是灰色,無法點選。這代表目前活頁簿裡找不到可以用一般選單取消隱藏的工作表,這條排查方向沒有找到原因。
這個畫面還需要小心解讀。Excel的工作表有三種顯示狀態:正常顯示、隱藏(可從選單取消隱藏),以及「深度隱藏」(xlSheetVeryHidden),後者無法從一般的取消隱藏選單恢復,要透過VBA修改Visible屬性。所以「取消隱藏」反灰,只能說明這次未能透過一般取消隱藏選單找到日期工作表,不能單憑它排除所有可能。更重要的是排查的態度:AI提出的原因只是假設,要實際操作才知道成不成立;一條路走不通時,就該回頭檢查程式如何判斷工作表存在,因為那句「已經存在」本來就是程式自己說的,並不是Excel確認過的事實。
五、交叉使用Claude找出變數誤判原因
ChatGPT的建議沒有找到原因,贊贊小屋決定換另一個AI看看,把問題帶到Claude。這次提供的資料比較完整:剛才右鍵選單的畫面,加上完整的問題描述與整段VBA程式:
傳給Claude的問題我設計了一個 VBA 程式,去複製一個名稱是「最新匯率」的工作表。複製之後,將它重新命名為最新的日期,用 6 個數字(分別是年月日)去命名。但是執行結果它會顯示已經有這一張工作表,但我明明在活頁簿裡面沒有這張工作表。幫我檢查這個程式,看到底問題出在哪裡?
後面接著貼上的,就是當時ChatGPT寫的完整程式:
原版程式|複製最新匯率並依日期命名()Sub 複製最新匯率並依日期命名()
Dim ws As Worksheet
Dim 新工作表名稱 As String
'設定原始工作表名稱
新工作表名稱 = Format(Date, "yymmdd")
'確認「最新匯率」工作表是否存在
On Error Resume Next
Set ws = ThisWorkbook.Worksheets("最新匯率")
On Error GoTo 0
If ws Is Nothing Then
MsgBox "找不到「最新匯率」工作表!", vbExclamation
Exit Sub
End If
'確認今天的工作表是否已經存在
On Error Resume Next
Set ws = ThisWorkbook.Worksheets(新工作表名稱)
On Error GoTo 0
If Not ws Is Nothing Then
MsgBox "工作表「" & 新工作表名稱 & _
"」已經存在,請勿重複複製!", vbExclamation
Exit Sub
End If
'複製「最新匯率」工作表
ThisWorkbook.Worksheets("最新匯率").Copy _
After:=ThisWorkbook.Worksheets( _
ThisWorkbook.Worksheets.Count)
'將複製後的工作表重新命名
ActiveSheet.Name = 新工作表名稱
MsgBox "匯率工作表複製完成!" & vbCrLf & _
"新工作表名稱:" & 新工作表名稱, vbInformation
End Sub
Claude的第一句就點出問題:變數ws被重複使用,而且沒有在第二次檢查前清空。它的說明是:第一次檢查「最新匯率」時,ws已經被設定為「最新匯率」工作表,不是Nothing;第二次檢查今天日期的工作表時,找不到該工作表,Set這一行會出錯,卻被On Error Resume Next略過,所以ws仍然保留著第一次設定的「最新匯率」;後面的If Not ws Is Nothing因此成立,就誤報「工作表已經存在」。
把兩次查找的過程攤開來看,會更容易理解:
| 步驟 | ws的狀態 |
|---|---|
| 第一次找到「最新匯率」 | ws參照「最新匯率」工作表 |
| 第二次尋找「261027」 | 找不到,Set發生錯誤,被On Error Resume Next略過,ws沒有改變 |
| 執行If Not ws Is Nothing | ws仍參照「最新匯率」,條件成立 |
| 顯示警告 | 誤報工作表已存在 |
用一句白話說:程式沒有找到日期工作表,卻拿著上一次找到的工作表,當成這一次查找成功的證據。On Error Resume Next的作用是讓程式在發生執行階段錯誤後繼續執行下一行,它並不代表出錯的那一行已經成功,也不會自動替程式完成正確的存在判斷。要注意,這是同一次執行中,變數保留了舊的參照,並不是記憶體洩漏,也不是上一次執行留下來的資料。On Error GoTo 0則是停用目前程序的錯誤處理,同樣不會把ws清空,兩個指令解決的是不同的事。這個差別,正是VBA錯誤處理中很容易被忽略的地方。
向另一個AI求助時,這次贊贊小屋把完整程式、原本的需求、實際看到的現象與右鍵選單的畫面一次集中提供,Claude便從變數參照的角度找出了原因。
六、加入Set ws = Nothing並驗證修正
Claude給的修正很簡單:在第二次檢查前,先加上Set ws = Nothing,把變數清空。修正後的完整程式如下,只有「確認今天的工作表是否已經存在」那一段多了一行:
修正版程式|第二次檢查前加入 Set ws = NothingSub 複製最新匯率並依日期命名()
Dim ws As Worksheet
Dim 新工作表名稱 As String
新工作表名稱 = Format(Date, "yymmdd")
'確認「最新匯率」工作表是否存在
On Error Resume Next
Set ws = ThisWorkbook.Worksheets("最新匯率")
On Error GoTo 0
If ws Is Nothing Then
MsgBox "找不到「最新匯率」工作表!", vbExclamation
Exit Sub
End If
'確認今天的工作表是否已經存在(先清空 ws)
Set ws = Nothing
On Error Resume Next
Set ws = ThisWorkbook.Worksheets(新工作表名稱)
On Error GoTo 0
If Not ws Is Nothing Then
MsgBox "工作表「" & 新工作表名稱 & _
"」已經存在,請勿重複複製!", vbExclamation
Exit Sub
End If
'複製「最新匯率」工作表
ThisWorkbook.Worksheets("最新匯率").Copy _
After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count)
'將複製後的工作表重新命名
ActiveSheet.Name = 新工作表名稱
MsgBox "匯率工作表複製完成!" & vbCrLf & _
"新工作表名稱:" & 新工作表名稱, vbInformation
End Sub
贊贊小屋把修正後的程式貼回模組,重新執行。這一次Excel沒有再誤報,而是跳出「匯率工作表複製完成!新工作表名稱:261027」,下方的工作表標籤也多了「261027」,與「工作表1」、「最新匯率」並列。「261027」成功建立,才證明修正有效。
Set ws = Nothing做的事,是解除ws原本對「最新匯率」的參照,讓它不再指向那張工作表;「最新匯率」本身與裡面的資料都還在,並沒有被清除。所以加入之後,第二次查找的流程變成:先解除舊參照;再嘗試尋找日期工作表;找到了,ws取得新的參照;找不到,ws維持Nothing;最後才判斷能不能複製。位置也要放對,必須在第二次Set之前,而不是放在判斷之後。
目前的畫面證明的是「當天日期工作表不存在時,修正後能成功建立」。若要進一步確認重複執行時的判斷,可以在同一天再執行一次,檢查是否出現「已經存在」的提示,並確認原本備份的資料沒有被覆蓋。
七、將解決方式帶回ChatGPT
問題解決後,贊贊小屋把Claude的診斷貼回原本的ChatGPT對話,開頭寫「人家解決了:」,後面接上Claude整段說明,包括「問題原因」與「修正方式」。這樣做的用意,是讓原本的對話也掌握最新程式與原因,供後續請ChatGPT繼續修改時參考。
ChatGPT讀完後回覆:你找出的原因完全正確,這次問題確實是VBA物件變數ws沒有重新清空,導致第二次檢查時仍保留第一次找到的「最新匯率」工作表;關鍵就是Set ws = Nothing這一行,它會把ws解除原本的工作表參照,讓第二次檢查能正確判斷是否找到日期工作表。
ChatGPT還補充了一個可以讓程式更穩定的方向:複製完成後,直接指定新工作表的名稱,不必依賴ActiveSheet。ActiveSheet是當下作用中的工作表,如果執行過程中畫面焦點改變,命名的對象就可能不是預期的那一張。Claude在診斷中也提醒過類似的做法,例如把最後的命名改成ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count).Name = 新工作表名稱,直接用工作表的位置指定對象。
不過ChatGPT認同原因,屬於回顧與確認,並不能取代實測。真正證明問題解決的,是前一節Excel裡成功新增「261027」的結果。至於改成明確指定工作表的寫法,採用後仍應回到Excel重新執行,確認命名對象與結果符合預期。
善用AI的第二意見,也要回到Excel驗證
回頭看整個過程,這次有三個做法派上用場。第一,AI給的原因只是假設,贊贊小屋照ChatGPT的建議實際檢查隱藏工作表,這條排查路徑沒有找到原因,問題因此回到程式本身。第二,卡關時,把完整程式、實際現象與畫面一起交給另一個AI重新檢查,這次Claude從變數狀態找到了原因。第三,最後仍然要回到Excel,執行修正後的程式,確認工作表真的建立,才算解決。
這次的經驗只能說明:同一個問題換一個AI,加上更完整的資料,可能找到不同的切入點;不能因此推論哪一個AI在所有VBA問題上都比較強。贊贊小屋長期同時使用ChatGPT、Gemini與Claude的分工心得,可以參考除了 ChatGPT 還有哪些 AI?Gemini、Claude 長期使用心得與付費理由。使用者能做的,是把證據交代清楚、理解原因是什麼,並且自己驗證結果。遇到「程式能執行,結果卻不對」時,不妨想想那一行用On Error Resume Next跳過的錯誤,是不是讓某個變數還拿著舊的內容。
