I developed the code for a vlookup but I’m having trouble with error handling. The values of a column on Sheet1 are being looked up in a column on Sheet2. The results are displayed on Sheet3.
There are 2 instances where there would be errors:
-
If cells in the lookup value column are blank
-
If the lookup values aren’t in the table array
If the cells of the lookup values are blank, I want the results to display blanks. If the lookup values aren’t blank but are missing from the table array, I want the cells to display «Missing». Right now the code is set up to just always give blanks.
The code I have so far is below. I’m an excel formula pro and a VBA novice. Any help would be appreciated!
On Error Resume Next
Dim Fund_Row As Long
Dim Fund_Clm As Long
Table1 = rangeA 'Column on Sheet1
Table2 = rangeB 'Column on Sheet2
Fund_Row = Sheets("Sheet3").Range("B2").Row
Fund_Clm = Sheets("Sheet3").Range("B2").Column
For Each cl In Table1
Sheets("Sheet3").Cells(Fund_Row, Fund_Clm)=
Application.WorksheetFunction.VLookup(cl, Table2, 1, False)
Fund_Row = Fund_Row + 1
Next cl
End Sub
asked Jan 8, 2018 at 7:58
1
For catching #NA! error, you could use just IFNA worksheet function. Just like that:
With Application.WorksheetFunction
Sheets("Sheet3").Cells(Fund_Row, Fund_Clm) = .IfNa(.VLookup(cl, Table2, 1, False), "Missing")
End With
You can deal with blanks two way. The simple is to add blanks to the table you are vlookuping. Little more sophisticated is add another if in code:
If cl.value = "" then ...
answered Jan 8, 2018 at 8:33
MarcinSzaleniecMarcinSzaleniec
2,2361 gold badge7 silver badges22 bronze badges
|
LightZ
Пользователь Сообщений: 1748 |
Всем привет. Всем заранее спасибо! Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать) P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке |
|
|
LightZ
Пользователь Сообщений: 1748 |
{quote}{login=Ластик}{date=21.01.2012 03:59}{thema=}{post}ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать) P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке{/post}{/quote} Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
LightZ
Пользователь Сообщений: 1748 |
к примеру если изменить код, следующим образом: на те строки, которые не совпадают с помощью ВПР — получаться ошибки #Н/Д. И как обойти ошибку VBA в вышеуказанном примере? Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано: ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр]) т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр) А вы в коде какую-то ерунду пишите |
|
|
Hugo Пользователь Сообщений: 23100 |
Как-то всё глупо… Но по существу вопроса — в этом примере можно так: Sub f() |
|
LightZ
Пользователь Сообщений: 1748 |
{quote}{login=Hugo}{date=21.01.2012 04:31}{thema=}{post}Как-то всё глупо… Но по существу вопроса — в этом примере можно так: Sub f() Спасибо, сам не додумался, писал просто On Error Resume Next, но ничего не получалось. Почему глупо? я выложил просто минимальный пример ВПР, только необходимо заменить #Н/Д на 0 с помощью VBA Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
LightZ
Пользователь Сообщений: 1748 |
{quote}{login=Ластик}{date=21.01.2012 04:29}{thema=}{post}сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано: ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр]) т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр) А вы в коде какую-то ерунду пишите{/post}{/quote} Что же тут не понятно ?:) Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
Hugo Пользователь Сообщений: 23100 |
Что за А:А, С:С? |
|
Юрий М
Модератор Сообщений: 60335 Контакты см. в профиле |
LightZ, для начала загляните сюда: http://www.planetaexcel.ru/tip.php?aid=26 поймёте свою ошибку. Также полезно почитать встроенную справку по этой функции. Особое внимание обратите на искомое значение. |
|
нарисовал для вас пример, см. файл |
|
|
Igor67
Пользователь Сообщений: 3704 |
Читал, читал ну не понимаю на какой Х? ВПР? |
|
KuklP Пользователь Сообщений: 14868 E-mail и реквизиты в профиле. |
Зря накинулись на человека. Каждый имеет право на ошибку. Пусть бы помучался. Зато потом бы ВПР на всю жизнь запомнил:-) А так быстро забудет. Я сам — дурнее всякого примера! … |
|
Hugo Пользователь Сообщений: 23100 |
И вообще — в коде использовать формулы листа как-то неправильно (на мой взгляд). |
|
а я вообще не понял, что человек хочет сделать. Спрашивает про ВПР, ответил про ВПР )) По-моему, человеку нужно было сделать так а мы бы уже что-нибудь придумали, а то «НД в ВПР», а что такое ВПР сам не знает |
|
|
Hugo Пользователь Сообщений: 23100 |
А если делать, как в примере Ластика, так там в каждой ячейке по два ВПР() отрабатывают… не всегда, но в большинстве случаев. |
|
Юрий М
Модератор Сообщений: 60335 Контакты см. в профиле |
{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)» |
|
Игорь, формулистов на тебя не хватает )) |
|
|
Hugo Пользователь Сообщений: 23100 |
А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф… |
|
KuklP Пользователь Сообщений: 14868 E-mail и реквизиты в профиле. |
{quote}{login=Юрий М}{date=21.01.2012 05:04}{thema=Re: }{post}{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)» Я сам — дурнее всякого примера! … |
|
{quote}{login=Hugo}{date=21.01.2012 05:07}{thema=}{post}А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф…{/post}{/quote} я думаю 2 раза, потому как нельзя проверить будет ли НД не рассчитав первый ВПР. |
|
|
LightZ
Пользователь Сообщений: 1748 |
Всем спасибо! Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
Hugo Пользователь Сообщений: 23100 |
В 10 раз быстрее: Sub tt() End If End With Сравните время: |
|
LightZ
Пользователь Сообщений: 1748 |
Спасибо Hugо, так я ещё не умею. Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
ну, и я свой вариант на массивах |
|
|
Hugo Пользователь Сообщений: 23100 |
Алгоритм выше я уже описАл, он не ноухау — диапазон в массив, его в словарь, второй в массив, проверяем по словарю. |
|
LightZ
Пользователь Сообщений: 1748 |
{quote}{login=Ластик}{date=21.01.2012 06:19}{thema=}{post}ну, и я свой вариант на массивах{/post}{/quote} Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там ) P.S. На больших объёмах Словарь выиграет, на небольших незаметно. |
|
|
Hugo Пользователь Сообщений: 23100 |
Ну на таком объёме тестить конечно несерьёзно — я вообще удивлён, что у меня такая разница получилась. Там может основная потеря времени на «инициализацию» формулы уходит, чего в моём коде нет. |
|
LightZ
Пользователь Сообщений: 1748 |
#30 21.01.2012 18:38:28 {quote}{login=Ластик}{date=21.01.2012 06:30}{thema=}{post}Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там ) P.S. На больших объёмах Словарь выиграет, на небольших незаметно.{/post}{/quote} Прикрепленные файлы
Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
I developed the code for a vlookup but I’m having trouble with error handling. The values of a column on Sheet1 are being looked up in a column on Sheet2. The results are displayed on Sheet3.
There are 2 instances where there would be errors:
-
If cells in the lookup value column are blank
-
If the lookup values aren’t in the table array
If the cells of the lookup values are blank, I want the results to display blanks. If the lookup values aren’t blank but are missing from the table array, I want the cells to display «Missing». Right now the code is set up to just always give blanks.
The code I have so far is below. I’m an excel formula pro and a VBA novice. Any help would be appreciated!
On Error Resume Next
Dim Fund_Row As Long
Dim Fund_Clm As Long
Table1 = rangeA 'Column on Sheet1
Table2 = rangeB 'Column on Sheet2
Fund_Row = Sheets("Sheet3").Range("B2").Row
Fund_Clm = Sheets("Sheet3").Range("B2").Column
For Each cl In Table1
Sheets("Sheet3").Cells(Fund_Row, Fund_Clm)=
Application.WorksheetFunction.VLookup(cl, Table2, 1, False)
Fund_Row = Fund_Row + 1
Next cl
End Sub
asked Jan 8, 2018 at 7:58
1
For catching #NA! error, you could use just IFNA worksheet function. Just like that:
With Application.WorksheetFunction
Sheets("Sheet3").Cells(Fund_Row, Fund_Clm) = .IfNa(.VLookup(cl, Table2, 1, False), "Missing")
End With
You can deal with blanks two way. The simple is to add blanks to the table you are vlookuping. Little more sophisticated is add another if in code:
If cl.value = "" then ...
answered Jan 8, 2018 at 8:33
MarcinSzaleniecMarcinSzaleniec
2,2361 gold badge7 silver badges22 bronze badges
Instead of WorksheetFunction.Vlookup, you can use Application.Vlookup. If you set a Variant equal to this it returns Error 2042 if no match is found. You can then test the variant — cellNum in this case — with IsError:
Sub test()
Dim ws As Worksheet: Set ws = Sheets("2012")
Dim rngLook As Range: Set rngLook = ws.Range("A:M")
Dim currName As String
Dim cellNum As Variant
'within a loop
currName = "Example"
cellNum = Application.VLookup(currName, rngLook, 13, False)
If IsError(cellNum) Then
MsgBox "no match"
Else
MsgBox cellNum
End If
End Sub
The Application versions of the VLOOKUP and MATCH functions allow you to test for errors without raising the error. If you use the WorksheetFunction version, you need convoluted error handling that re-routes your code to an error handler, returns to the next statement to evaluate, etc. With the Application functions, you can avoid that mess.
The above could be further simplified using the IIF function. This method is not always appropriate (e.g., if you have to do more/different procedure based on the If/Then) but in the case of this where you are simply trying to determinie what prompt to display in the MsgBox, it should work:
cellNum = Application.VLookup(currName, rngLook, 13, False)
MsgBox IIF(IsError(cellNum),"no match", cellNum)
Consider those methods instead of On Error ... statements. They are both easier to read and maintain — few things are more confusing than trying to follow a bunch of GoTo and Resume statements.
Instead of WorksheetFunction.Vlookup, you can use Application.Vlookup. If you set a Variant equal to this it returns Error 2042 if no match is found. You can then test the variant — cellNum in this case — with IsError:
Sub test()
Dim ws As Worksheet: Set ws = Sheets("2012")
Dim rngLook As Range: Set rngLook = ws.Range("A:M")
Dim currName As String
Dim cellNum As Variant
'within a loop
currName = "Example"
cellNum = Application.VLookup(currName, rngLook, 13, False)
If IsError(cellNum) Then
MsgBox "no match"
Else
MsgBox cellNum
End If
End Sub
The Application versions of the VLOOKUP and MATCH functions allow you to test for errors without raising the error. If you use the WorksheetFunction version, you need convoluted error handling that re-routes your code to an error handler, returns to the next statement to evaluate, etc. With the Application functions, you can avoid that mess.
The above could be further simplified using the IIF function. This method is not always appropriate (e.g., if you have to do more/different procedure based on the If/Then) but in the case of this where you are simply trying to determinie what prompt to display in the MsgBox, it should work:
cellNum = Application.VLookup(currName, rngLook, 13, False)
MsgBox IIF(IsError(cellNum),"no match", cellNum)
Consider those methods instead of On Error ... statements. They are both easier to read and maintain — few things are more confusing than trying to follow a bunch of GoTo and Resume statements.
Поискав по рунету материал на тему обработки ошибок в VBA, не увидал на первых двух страницах результатов поиска чего-то, что мне понравилось. Может плохо смотрел, но решил написать на эту тему свою статью.
Простите, но — немного словоблудия
Ошибки в программе
Ошибки времени исполнения программы возникают, когда среда программирования не может выполнить то, что вы хотите. Таких ситуаций может быть много. Например:
-
Вы обращаетесь к объекту по имени, а объекта с таким именем в коллекции нет
-
Вы хотите выделить ячеку на одном листе, а этот лист в данный момент не является активным (типичнейшая ошибка новичков в Excel VBA)
-
Вы хотите удалить отфильтрованные автофильтром строки, а фильтр вообще не вернул записей и удалять нечего
-
Вы ссылаетесь на элемент массива, который находится за пределами его границ.
-
Вы пытаетесь присвоить переменной значение, которое оно не может хранить. Например, переменной типа Long нельзя присвоить строковую константу или переменной типа Integer присвоить знанчение превышающее число 32767.
На любую из этих и сотни других ситуаций среда выполнения реагирует стандартно — прерывает ход выполнения программы на том операторе, где возникла ошибка или, как ещё принято говорить, исключение. На экран выводится информация о возникшей ошибке и предлагаются стандартные варианты для продолжения работы:
-
Continue (продолжить) — этот пункт во время возникновения ошибки всегда не активен. Он активен, когда по ходу выполнения программы вы использовали оператор Stop. Кстати это очень полезный оператор для отладки программы.
-
End (завершить) — завершение исполнения программы
-
Debug (отладка) — переход в режим отладки, в котором можно посмотреть, на каком операторе возникла ошибка, что содержат переменные, можно даже перетащить жёлтую полоску, подсвечивающую текущий оператор, назад, и модифицировать знанчение переменных через окно Immediate window (впрочем это экзотика). В общем случае кнопка Debug позволяет посмотреть, где случилась ошибка и попытаться понять почему так случилось.
Если вы — автор программы, в которой случилась ошибка, то вы, должно быть, в начале будете рады увидеть подобное окно, ибо только так вы сможете отловить основные ошибки, скрытые в вашем коде. Однако, если эту ошибку видит пользователь, то для него это, мягко говоря, безрадостное и малопонятное зрелище. Ещё хуже, если за эту программу вам заплатили деньги. Поэтому в среде худо-бедно профессиональных программистов принято предусматривать обработку ошибок в своих программах.
Почему вообще в коде возникают ошибки?
-
Много ошибок во время написания кода возникает по невнимательности или не совсем адекватного понимания того, что делаешь. Таких ошибок, как правило, очень много, особенно у начинающих программистов, но эти ошибки довольно легко отловить и исправить, так как, пока вы их не исправите, ничего не работает. Ну, например, вы должны извлечь данные из 5-го столбца, а вы извлекаете из 6-го, а их там банально нет. Ясно, что вы это очень быстро заметите.
-
Вторая группа ошибок — это ошибки оптимиста. Когда программа написана в целом правильно, но алгоритм не готов к ударам судьбы в виде неожиданных действий со стороны пользователя, ошибок ввода-вывода (вы рассчитывали считать данные из файла, а файла с таким именем не оказалось, либо он заблокирован другим приложением), особенностей конфигурации компьютера (разные версии ОС или офиса, которые в некоторых мелочах отличаются).
-
Тонкие логические ошибки. Чем сложнее программа, тем больше шансов, что модель задачи в вашей голове, ваша программа и реальность не совсем согласованы между собой. Пока вы не достигните достаточного погружения в задачу вы такие ошибки не найдёте и не исправите. Порой на это уходит много времени. Но это характерно для сложных задач.
-
Ошибки на стыке вашего приложения и сервисов ОС, приводящие к неожиданным крахам приложения. Такого вообще возникать не должно, но как мы понимаем, и ОС и офис содержат ошибки, да и вы (что более вероятно) можете пользоваться системными вызовами не правильно. Подобные ошибки — сущий кошмар, особенно когда они проявляются лишь на некоторых конфигурациях, при определенных условиях, их трудно поймать и надёжно воспроизвести.
Задачи механизмов обработки ошибок
-
Обеспечить стабильную работу программы. Возникновение ошибки, появление которой вы не предусмотрели, приведёт в большинстве случаев к аварийному завершению всей программы или её части. При определенном уровне подобных ситуаций это ведёт к тому, что программой пользоваться становится невозможно.
-
Информирование. Мало обработать ошибку и предотвратить завершение программы. Надо ещё и адекватно проинформировать пользователя о причинах нестандартного поведения программы. Частно причиной ошибок в программе являются некорректные действия пользователя, поэтому важно сообщать ему о них.
-
Защита данных от повреждения. Программа обязана защищать от непреднамеренных повреждений результаты своей или пользовательской работы. Деструктивные действия должны быть снабжены соответствующими предупредительными диалоговыми окнами. Часто ошибка, не обработанная должным образом может повредить нужные данные.
Файл примера
Скачать
Код без обработки ошибок
Вот простой пример с потолка. Если вызвать Example_00, то она прекрасно отработает без ошибок и вернёт это:

В функцию GetCalories передаётся строка с блюдом, а она должна вернуть его калорийность, сверившись с таблицей в A1:B7.

Давайте поищем слабые места в этом коде. Первое, что должно прийти в голову — если мы ищем, то, что произойдёт, если мы не найдём? А произойдёт, конечно же, ошибка. Её инициирует метод Match.


Ещё одно слабое место этой подпрограммы: функция возвращает вещественный тип Double, и даже, если поиск оказался удачным, то в Cells(intRow, 2) может случайно находиться текстовая строка, а потому, когда вы числовому типу попытаетесь присвоить строковый тип, также произойдёт ошибка. И, если вы второй ошибки сможете избежать за счёт дополнительного оператора if с проверкой через IsNumber(), то избежать первой ошибки таким способом нельзя. Что же делать? А вот тут на сцену выходят операторы обработки ошибок.
Есть 2 подхода к обработке ошибок: автономный подход и выносной. Эти термины я придумал только что, чтобы проще было их обсуждать.
Автономный подход
Смысл автономного подхода в том, чтобы не выносить сор из избы. Если в подпрограмме возникла ошибка, то мы должны предположить, на каком месте она возникнет и поджидать её там с дубиной. С ошибкой, в этом случае, разбираются обычно в операторе, идущем сразу после потенциально опасного места. Давайте смотреть, как это может выглядеть:
Итак, что тут сделано:
-
Сразу после объявления функции GetCalories_v1 идёт оператор on error resume next, который в случае возникновения в каком-либо месте ошибки, предписывает VBA просто передавать управление на следующий оператор, идущий после ошибочного.
-
Мы объявили переменные. Необъявленные переменные получают тип Variant и значение по умолчанию Empty. Объявленные переменные числовых типов инициируются нулём, строковые — пустой строкой, то есть я наперёд знаю, что они содержат, а это хорошо для обработки ошибок.
-
На вызове метода WorksheetFunction.Match у нас возникает ошибка, так как искомого значения в таблице нет. А это, между прочим, был оператор присваивания ( = ). Прежде, чем левой части оператора присваивания (intRow) что-то будет присвоено, необходимо вычислить правую часть оператора присваивания (WorksheetFunction.Match…), а поскольку в процессе этого вычисления возникает ошибка, то переменная intRow остаётся такой, какой была! А, как я уже сказал, VBA автоматически её инициализирует нулём до начала исполнения подпрограммы. Получается, что, если в этом операторе возникнет ошибка, то в intRow будет ноль. Если ошибки во время поиска не возникнет, то ноля там не будет ни при каких раскладах, так как строки на листе нумеруются с единицы.
-
И вот этот ноль мы и контролируем, добавляя оператор If. Если intRow больше нуля, то WorksheetFunction.Match отработала штатно, а если нет — то работу подпрограммы надо прерывать, но об этом чуть позже.
-
Далее мы помним, что Cells(intRow, 2) может теоретически вернуть строковое значение, которое вызовет ошибку Type missmatch при присвоении переменной типа Double (GetCalories_v1), поэтому мы вставляем дополнительную проверку промежуточной переменной varTemp тому, что она числовая. И если это так, то присваиваем GetCalories_v1 значение из varTemp.
-
В случае возникновения любой ошибки внутри GetCalories_v1 она просто вернёт ноль. Почему ноль? Потому что переменная GetCalories_v1 тоже инициализируется нулём и об этом не надо заботиться, а в случае ошибки она останется в неприкосновенности.
-
Соответственно родительский код (в нашем случае его роль играет процедура Example_01) должен проверить, а не вернёт ли GetCalories_v1 ноль, и быть готовым к этой ситуации.
-
А вот теперь тонкий момент, который не все понимают. Почему я использовал промежуточные переменные intRow и varTemp? Вроде бы есть очевидный ответ — чтобы не вычислять значение выражений с Match и Cells 2 раза. Отчасти это, конечно, так. Но это, в данном случае, не главная причина. Главная причина в том, что такой код
вызовет неправильное поведение программы. Если у нас Match вызовет исключение, то VBA передаст управление на СЛЕДУЮЩИЙ оператор, а следующий оператор в данном случае это то, что идёт после Then — присваивание переменной varTemp значения. Таким образом наша проверка на наличие ошибки сработает с точностью до наоборот, передав управление в ту часть кода, которая должна быть защищена от ситуации, когда Match не нашла строку в таблице. Вот почему важно в операторе If не иметь ничего такого, что могло бы вызвать ошибку.
-
Как видите, в этом подходе мне зачастую даже нет необходимости проверять объект Err, чтобы понять, что произошла ошибка, так как я ориентируюсь на то, что промежуточные переменные остаются неинициализированными, что является показателем наличия ошибки.
Выносной подход
Данный метод основан на том, что, когда возникает ошибка, то VBA передаёт управление на специальный участок кода — обработчик ошибок, который обычно размещают в конце подпрограммы. Это может выглядеть так:
Обратите внимание, что:
-
Оператор on error теперь в случае ошибки предписывает передавать управление на метку ErrorHandler, которая объявлена в конце кода процедуры GetCalories_v2
-
В коде мы никак не заботимся о каких-либо проверках. Возникла ошибка? Иди на метку — там разберутся.
-
Если ошибки не случилось, то, чтобы программа не стала исполнять строчки, предназначенные для обработки ошибок, перед меткой ErrorHandler обычно ставят оператор Exit Sub или Exit Function (в зависимости от типа подпрограммы).
-
Принципиальный момент — наличие оператора On Error Resume Next сразу после метки ErrorHandler. Дело в том, что после того, как вы перешли на метку ErrorHandler, очень опасно иметь действующим оператор On Error GoTo ErrorHandler, так как, если у вас в обработчике ошибки случится любая ошибка, то управление будет передано опять на метку и, как нетрудно понять, образуется бесконечный цикл. Поэтому сразу после метки мы возможность возникновения цикла ликвидируем оператором On Error Resume Next.
Что лучше?
Какой метод лучше применять зависит от ваших предпочтений и конкретных ситуаций. Грамотную обработку ошибок можно сделать и так и эдак. Вот несколько соображений по преимуществам и недостакам данных подходов:
Автономный подход
| Преимущества | Недостатки |
| Есть возможность точно идентифицировать каждую конкретную проблему (если вы её предусмотрели), возникающую во время исполнения, что позволит вам дать самые точные инстркции пользователю для предотвращения появления исключения в будущем. | Достаточно трудоёмок, так как подразумевает наличие большого количества проверок в коде. Каждое потенциально опасное действие должно быть снабжено соответствующим оператором If, в котором контролируется значение переменной или код ошибки. |
| Надо хорошо представлять себе ситуации, где могут возникнуть ошибки, в противном случае ряд ошибок вы просто не заметите на этапе отладки. | |
| Необходимо больше кода, а также требуется опыт и фантазия. | |
| Необходимо больше промежуточных переменных |
Выносной подход
| Преимущества | Недостатки |
| Ни одна ошибка не проскочит незамеченной. | Не смотря на то, что вы перехватите все ошибки, отреагировать на них правильно затруднительно, так как вы, по большому счёту, не знаете, на каком операторе произошла ошибка и почему. |
| Удобнее организовывать централизованный сбор логов по ошибкам в приложении. Однозначно, фаворит для больших проектов. |
Кратко пробежимся по операторам, функциям и объектам VBA, которые предназначены для обработки ошибок времени исполнения программы.
Операторы
On Error { GoTo label | Resume Next | GoTo 0 }
Оператор on error управляет тем, на какой участок вашего кода будет передано управление в случае возникновения ошибки. Данный оператор можно вставить в любое место вашей программы или подпрограммы. Есть 3 варианта:
-
On error goto label — после того, как этот оператор выполнен, ошибка, возникшая на других операторах программы приведёт к переходу на метку label.
-
On error resume next — после такого оператора, VBA будет игнорировать возникшую ошибку и передавать управление на следующий оператор, стоящий за тем, в котором возникла ошибка.
-
On error goto 0 — это режим по-умолчанию. В случае возникновения ошибки данный режим приведёт к появлению на экране стандартного обработчик ошибок VBA с кнопками End и Debug.
Resume { label | Next | [0] }
Данный оператор возобновляет выполнение программы. Применяется в выносном методе обработки ошибок.
-
resume label— возобновление с метки label
-
resume next — возобновление со следующего оператора
-
resume или resume 0 — возобновление с оператора, вызвавшего ошибку. Это имеет смысл, если вы устранили ошибку в своём обработчике. На мой взгляд, на практике такое применяется крайне редко.
Goto label
Переход на метку. Может пригодиться, однако, использование меток в коде для чего-то большего, чем обработка ошибок, считается страшным моветоном.
Exit { Do | For | Function | Sub }
Досрочный выход из циклов (Do или For) и досрочный выход из подпрограмм (функции или процедуры). Могут пригодиться при обработке ошибок, но вообще это операторы и без того чрезвычайно полезны.
Объект Err
-
Err — глобальный объект (его не надо объявлять, а можно сразу пользоваться), который содержит информацию о последней ошибке, случившейся в вашей программе. Проверяя Err сразу после возникновения исключения или после ситуации, которая могла привести к исключению, вы можете понять, что имело место на самом деле.
-
Свойство Err.Number — содержит числовой код ошибки, по которому их различают в программе. Поскольку Number — свойство по умолчанию, то вы можете его опускать, то есть Err и Err.Number — это эквиваленты. Значение ноль говорит о том, что ошибки не произошло.
-
Err.Description — содержит англоязычное краткое описание ошибки
-
Err.Source — возвращает имя модуля, в котором возникла ошибка
-
Err.Clear — сбрасывает последнюю ошибку. Err сбрасывается также при выполнении оператором Resume, Exit (любого типа кроме Do и For) и On Error.
-
Err.Raise — искусственно вызывает исключение указанного в переданном параметре типа. Можно использовать для тестирования вашей подсистемы обработки ошибок.
P.S.
Лично я привык в своих программах использовать автономный подход и, возможно, поэтому я не совсем осознаю все преимущества выносного подхода. Буду рад прочесть в комментариях ваше мнение на этот счёт. Тема обработки ошибок данной статьёй, конечно, быть исчерпана не может, но она послужит вам хорошей стартовой точкой в этом важном деле.
Читайте также:
-
Работа с объектом Range
-
Работа с объектом Range (часть 2)
-
Sheet happens
-
Поиск границ текущей области
-
Массивы в VBA
-
Структуры данных и их эффективность
-
Автоматическое скрытие/показ столбцов и строк
В VBA, когда мы работаем с кодами, мы можем столкнуться со многими различными типами ошибок, и способ устранения этих ошибок известен как обработка ошибок, теперь могут быть некоторые ошибки, которые сделаны в синтаксисе, который сам выделяет это, но когда есть какая-то ошибка который выходит за пределы диапазона или что-то, чего не существует Excel дает нам всплывающее окно для того же самого, важно знать, какой код ошибки для какой ошибки, чтобы идентифицировать ошибку в коде.
В Excel VBA при выполнении любого набора кодов мы получаем какие-то ошибки. Некоторые из этих ошибок являются синтаксическими; некоторые из них являются неисполняемыми ошибками. Синтаксическая ошибка, сделанная пользователем, выделяется красным цветом самим Excel. Но когда возникает какая-либо другая ошибка времени выполнения, как мы с ней справимся и как выйти за рамки этого, мы рассмотрим в этой статье.
Помимо синтаксических ошибок, другие ошибки времени выполнения необходимо обрабатывать при выполнении любого набора кодов. Во-первых, позвольте мне привести пример того, как возникает другая ошибка времени выполнения. Взгляните на приведенный ниже код,

Это пример кода, который при выполнении вернет то, что написано в функции msgbox. Но, как мы видим, во второй строке кода есть 4/0, что невозможно с математической точки зрения, поэтому он вернет ошибку времени выполнения. Давайте выполним приведенный выше код и увидим ошибку, которую мы получим.

Это ошибка, которую мы получаем при выполнении данного кода. Теперь, как мы обрабатываем эту ошибку, производится обработкой ошибок.
Существует два метода обработки ошибок:
- При ошибке Перейти к и
- При ошибке Возобновить дальше.
Объяснение
Как объяснялось выше, мы получаем много типов ошибок в VBA. Некоторые из них являются синтаксисом, а некоторые — временем выполнения. Синтаксические ошибки уже выделены красным цветом, например, см. скриншот ниже,

В то время как другие ошибки времени выполнения. По сути, Excel выполняет следующие три действия: либо показывает ошибку, либо игнорирует эту ошибку, либо показывает определенный набор инструкций. Для выполнения таких задач нам нужно давать инструкции, и это называется обработкой ошибок.
Как обрабатывать ошибки в коде VBA?
Вы можете скачать этот шаблон Excel для обработки ошибок VBA здесь — Шаблон Excel для обработки ошибок VBA
Пример №1
В качестве первого примера возьмем первый код, который мы взяли в качестве демонстрации. В приведенном выше примере мы видели, что код выдает ошибку времени выполнения во второй функции msgbox.
Напишите следующий код после открытия подфункции,
Код:
Sub Sample() On Error Resume Next MsgBox 4 / 2 MsgBox 4 / 0 MsgBox 4 / 1 End Sub

Теперь, когда мы выполняем приведенный выше код, мы видим, что строка кода с ошибкой не выполняется. Excel пропускает эту строку и возобновляет работу на следующей строке.

Существует еще один способ обработки ошибки — VBA. Заявление о переходеОператор VBA GoTo позволяет перейти к строке кода, пропуская все ошибки между ними. Существует два типа операторов GOTO: один — выбрать любой диапазон рабочего листа в указанной книге, а другой — обработчик ошибок.читать далее. Мы предоставляем Excel в качестве места назначения для перехода при обнаружении ошибки. Вместо предыдущего кода обработки ошибок мы вставили, запишите следующий код,
Код:
Sub Sample() On Error GoTo az MsgBox 4 / 2 MsgBox 4 / 0 MsgBox 4 / 1 End Sub

Мы даем Excel Az в качестве пункта назначения, к которому можно перейти, если он обнаружит ошибку. Теперь после msgbox напишите еще один код, как показано ниже,
Код:
Sub Sample() On Error GoTo az MsgBox 4 / 2 MsgBox 4 / 0 MsgBox 4 / 1 Done: Exit Sub

Теперь нам нужно определить пункт назначения az как то, что он должен делать, когда Excel находит ошибку в коде.
Код:
Sub Sample() On Error GoTo az MsgBox 4 / 2 MsgBox 4 / 0 MsgBox 4 / 1 Done: Exit Sub az: MsgBox "This is an error " & Err.Description End Sub

Теперь, когда мы запускаем этот код, мы видим отображаемый результат.

Это первый результат окна сообщения, и, поскольку мы знаем, что у нас есть ошибка в следующей строке нашего кода, давайте посмотрим результат, который даст Excel.

err.description выше в коде помогает нам точно показать, какая ошибка произошла в нашем коде.
Пример #2
Мы научились обрабатывать ошибки в наших кодах. Давайте посмотрим на другой пример того, как обрабатывать ошибки. Рассмотрим следующий код в качестве нашего второго примера.

У нас есть чем-то похожая ошибка из примера 1. Ошибка в строке d = i/b. Теперь мы будем обрабатывать эти ошибки, используя два описанных выше метода.
Напишите следующий код после открытия подфункции,
Код:
Sub Sample2() On Error Resume Next bx Dim i As Integer, b As Integer, c As Integer, d As Integer i = 2 b = 0 c = i + b MsgBox c d = i / b MsgBox d End Sub

Теперь, когда мы выполняем наш код, мы видим, что он игнорирует вторую строку и просто отображает значение для C.

Вышеупомянутый обработчик ошибок был следующим для возобновления, и теперь мы будем использовать Go to, в котором мы скажем, что превосходим пункт назначения для перехода, когда он обнаруживает ошибку. Запишите следующий код,
Код:
Sub Sample2() On Error GoTo bx Dim i As Integer, b As Integer, c As Integer, d As Integer i = 2 b = 0 c = i + b MsgBox c d = i / b MsgBox d

bx — это пункт назначения, который выдается при возникновении ошибки после того, как msgbox D запишет следующий код:
Код:
Sub Sample2() On Error GoTo bx Dim i As Integer, b As Integer, c As Integer, d As Integer i = 2 b = 0 c = i + b MsgBox c d = i / b MsgBox d DOne: Exit Sub

Теперь нам нужно определить пункт назначения Bx, что он должен делать при обнаружении ошибки. Поэтому запишите следующий код:
Код:
Sub Sample2() On Error GoTo bx Dim i As Integer, b As Integer, c As Integer, d As Integer i = 2 b = 0 c = i + b MsgBox c d = i / b MsgBox d DOne: Exit Sub bx: MsgBox " This is another Error " & Err.Description End Sub

Теперь, когда мы выполняем код, мы видим, что Excel сначала дает нам значение для C.

Теперь на другом этапе он даст нам подсказку, которую мы ему предоставили, когда он обнаружит ошибку.

Вот как мы обрабатываем обычную среду выполнения ошибки в экселеОшибки в Excel распространены и часто возникают во время применения формул. Список из девяти наиболее распространенных ошибок Excel: #DIV/0, #N/A, #NAME?, #NULL!, #NUM!, #REF!, #VALUE!, #####, Circular Reference.читать далее VBA.
То, что нужно запомнить
Есть несколько вещей, которые нам нужно помнить об обработке ошибок:
- При следующем возобновлении ошибки игнорирует ошибку.
- On Error GoTo Отображает пункт назначения excel при обнаружении ошибки.
- Описание используется для отображения точной ошибки, возникшей у пользователя.
УЗНАТЬ БОЛЬШЕ >>
Post Views: 532
Обработка ошибок
Если вы когда-нибудь учились водить автомобиль, то наверняка вспомните, что при первой посадке на водительское сиденье все внимание было приковано к трем деталям: рулю, педалям и рычагу переключения передач. Происходящее вне автомобиля уходило на второй план, так как вначале нужно было стронуться с места. По мере практики навыки вождения улучшались и эти три детали постепенно уходили на задний план. Как ни странно, но руль и рычаг переключения передач всегда оказывались там, куда вы не смотря протягивали руки, а ноги сами находили педали. Теперь все внимание стало занимать происходящее на дороге. Иными словами, вы стали опытным водителем.
В программировании все абсолютно также. Начинающие программисты больше обращают внимание на первые попавшиеся на глаза операторы, функции и другие элементы языка, а сам алгоритм уходит на задний план. Если программа скомпилировалась без ошибок, то это уже большое счастье, хотя это еще не означает, что программа работает правильно. По мере практики мышление программиста меняется, он начинает обращать внимание на мелочи, на форматирование программы, использует более эффективные алгоритмы и в результате всего этого допускает меньше ошибок. Подводя итоги, можно сказать, что начинающий программист просто пишет программу, а опытный программист пытается найти оптимальный алгоритм и предусмотреть поведение программы в различных ситациях. Однако, от ошибок никто не застрахован, поэтому очень важно знать как быстро найти ошибку.
Типы ошибок
Существуют три типа ошибок в программе:
- синтаксические — это ошибки в имени оператора или подпрограммы, отсутствие закрывающей или открывающей кавычек и т. д., то есть ошибки в синтаксисе языка. Как правило, компилятор предупредит о наличии ошибки, а программа не будет выполняться совсем;
- логические — это ошибки в логике работы программы, которые можно выявить только по результатам работы программы. Как правило, компилятор не предупреждает о наличии ошибки, а программа будет выполняться, так как не содержит синтаксических ошибок. Такие ошибки достаточно трудно выявить;
- ошибки времени выполнения — это ошибки, которые возникают во время работы программы. В одних случаях ошибки времени выполнения являются следствием логических ошибок, а в других случаях причиной являются внешние события, например, нехватка оперативной памяти, отсутствие прав для записи в файл и др.
Инструкция On Error
Ошибки времени выполнения можно перехватывать внутри подпрограммы. Для этого используется инструкция On Error, которая имеет три формата:
On Error GoTo <Метка>— при возникновении ошибки управление передается инструкции, помеченной меткой<Метка>. Метка должна быть допустимым идентификатором, к которому предъявляются такие же требования как и к переменным. Внутри подпрограммы метка указывается в самом начале помечаемой строки и после метки ставится двоеточие. В качестве примера создадим функцию для деления двух целых чисел. Внутри функции предусмотрим обработку ошибки деления на0:
Function Деление(x As Integer, y As Integer) As Double
On Error GoTo ПриОшибке
Деление = x / y
Exit Function
ПриОшибке:
Деление = 0
End Function
Если при вызове функции во втором параметре передать значение 0, то управление будет передано в строку, помеченную с помощью метки ПриОшибке. Обратите внимание на то, что метка расположена после инструкции Exit Function. В этом случае код после инструкции Exit Function будет выполнен только в том случае, если возникнет ошибка;
On Error Resume Next— при возникновении ошибки управление передается следующей инструкции;On Error GoTo 0— отключает перехват ошибок.
Если внутри подпрограммы не предусмотрен перехват ошибки, то при возникновении ошибки работа программы прерывается и выводится стандартное окно с описанием и несколькими кнопками: Continue (продолжить), End (завершить выполнение программы), Debug (перейти в режим отладки) и Help (вывод справки).
Инструкция Resume
Инструкция Resume позволяет указать куда следует переходить после обработки ошибки. Инструкция имеет несколько форматов:
Resume [0]— управление передается инструкции, вызвавшей ошибку;Resume Next— управление передается инструкции, следующей за инструкцией, вызвавшей ошибку;Resume <Метка>— управление передается инструкции, помеченной меткой<Метка>.
Если инструкция Resume не указана, то выполняется только код внутри обработчика ошибки и производится выход из подпрограммы.
Получение информации об ошибке и генерация ошибки
Вся информация о последней ошибке доступна через объект Err. Объект содержит следующие свойства:
Number— код ошибки, например, код11для ошибки деления на0. Если ошибки не произошло, то свойство содержит значение0;Description— описание ошибки, например, строка"Division by zero"для ошибки деления на0. Пример вывода кода и описания ошибки:
Debug.Print Err.Number; Err.Description
Source— название текущего проекта;HelpFile— путь к файлу справки;HelpContext— идентификатор раздела в справочном файле;LastDLLError— системный код ошибки при работе с DLL.
Объект Err содержит следующие методы:
Clear()— очищает всю информацию о последней ошибке. Этот метод следует вызвать после успешной обработки ошибки. Информация об ошибке автоматически очищается при выходе из подпрограммы и ряде других случаев;Raise()— позволяет сгенерировать ошибку в программе. Формат метода:
Raise Number[, Source][, Description][, HelpFile][, HelpContext]
В параметре Number указывается код генерируемой ошибки (целое число от 0 до 65 535). Коды от 0 до 512 зарезервированы под системные ошибки, а остальные коды можно использовать под пользовательские ошибки. Чтобы сгенерировать ошибку с пользовательским кодом необходимо сложить код с константой vbObjectError. Остальные параметры являются необязательными и полностью аналогичны одноименным свойствам объекта Err. Пример генерации и обработки пользовательской ошибки:
Sub ГенерацияОшибки()
On Error GoTo ПриОшибке
Err.Raise vbObjectError + 513
Exit Sub
ПриОшибке:
Debug.Print Err.Number; Err.Description
' -2147220991 Automation error
End Sub
Способы поиска ошибок в программе
В предыдущих разделах мы научились обрабатывать ошибки времени выполнения. Однако, наибольшее количество времени программист затрачивает на другой тип ошибок — логические ошибки. В этом случае программа компилируется без ошибок, но результат выполнения программы не соответствует ожидаемому результату. Ситуация еще более осложняется, когда неверный результат проявляется лишь периодически, а не постоянно. Инсценировать такую же ситуацию, чтобы получить этот же неверный результат, бывает крайне сложно и занимает очень много времени. В этом разделе мы рассмотрим лишь «дедовские» (но по прежнему актуальные) способы поиска ошибок, а современные способы отладки приложений, доступные в VBA, изучим в следующем разделе.
Первое, на что следует обратить внимание, — на объявления переменных. Например, рассмотрим простой пример:
Как вы думаете, какое значение отобразится в окне Immediate после выполнения этого кода? Думаете, что число 10? Не факт! Вот тут-то и кроется проблема не видная на первый взгляд. В первой инструкции присваивается значение переменной x, имя которой набрано на английской раскладке клавиатуры, а вот во второй инструкции выводится значение переменной x, имя которой набрано на русской раскладке клавиатуры. В результате значение присваивается одной переменной, а выводится значение другой переменной. Такие ситуации очень часто встречаются в программах на языке VBA, так как объявлять переменную не обязательно. Чтобы избежать такой ситуации следует обязательно объявлять переменные явным образом. Контроль за соблюдением этого правила можно возложить на компилятор, добавив в начале модуля следующую инструкцию:
При наличии инструкции компилятор производит проверку объявления всех переменных. Если переменная не была объявлена явным образом, то компилятор выведет сообщение об ошибке и выполнение программы будет остановлено. Таким образом, код должен выглядеть следующим образом:
Option Explicit
...
Dim x As Integer
x = 10
Debug.Print x ' 10
Далее следует обратить внимание на форматирование кода. Начинающие программисты обычно не обращают на это никакого внимания, считая этот процесс лишним. А на самом деле зря! Компилятору абсолютно все равно, разместите вы все инструкции на одной строке или выполните форматирование кода. Однако, при поиске ошибок форматирование кода позволит найти ошибку гораздо быстрее.
Перед всеми инструкциями внутри блока должно быть расположено одинаковое количество пробелов. Обычно используют три или четыре пробела. От применения символов табуляции лучше отказаться. Если все же используете, то не следует в одном файле совмещать и пробелы и табуляцию. Для вложенных блоков количество пробелов умножают на уровень вложенности. Если для блока первого уровня вложенности использовалось три пробела, то для блока второго уровня вложенности должно использоваться шесть пробелов, для третьего уровня — девять пробелов и т. д. Пример форматирования вложенных блоков приведен в листинге 11.1.
Dim Массив As Variant, i As Integer, j As Integer
Массив = Array(Array(0, 1), Array(2, 3), Array(4, 5))
For i = 0 To 2
For j = 0 To 1
Debug.Print Массив(i)(j)
Next
Next
Длина одной строки не должна содержать более 80 символов. Если количество символов больше, то следует выполнить переход на новую строку. При этом продолжение смещается относительно основной инструкции на величину отступа или выравнивается по какому-либо элементу. Иначе приходится пользоваться горизонтальной полосой прокрутки, а это очень неудобно при поиске ошибок.
Если программа слишком большая, то следует задуматься о разделении программы на отдельные подпрограммы или классы, которые выполняют логически законченные действия. Помните, что отлаживать отдельную подпрограмму гораздо легче, чем «спагетти»-код. Причем прежде чем вставить подпрограмму (или класс) в основную программу ее следует протестировать в отдельном проекте, передавая подпрограмме различные значения и проверяя результат ее выполнения.
Обратите внимание на то, что форматирование кода должно выполняться при написании кода, а не во время поиска ошибок. Этим вы сократите время поиска ошибки и скорее всего заметите ошибку еще на этапе написания. Если все же ошибка возникла, то вначале следует инсценировать ситуацию, при которой ошибка проявляется. После этого можно начать поиск ошибки.
Причиной периодических ошибок чаще всего являются внешние данные. Например, если числа получаются от пользователя, а затем производится деление чисел, то вполне возможна ситуация, при которой пользователь введет число 0. Деление на ноль приведет к ошибке. Следовательно, все данные, которые поступают от пользователей, должны проверяться на соответствие допустимым значениям. Если данные не соответствуют, то нужно вывести сообщение об ошибке, а затем повторно запросить новое число или прервать выполнение всей программы. Кроме того, нужно обработать возможность того, что пользователь может ввести вовсе не число, а строку.
Метод Print() объекта Debug удобно использовать для вывода промежуточных значений. В этом случае значения переменных вначале выводятся в самом начале программы и производится проверка соответствия значений. Если значения соответствуют, то инструкция с методом Print() перемещается на следующую строку программы и опять производится проверка и т. д. Если значения не совпали, то ошибка возникает в инструкции, расположенной перед инструкцией с методом Print(). Если это пользовательская подпрограмма, то проверку значений производят внутри подпрограммы, каждый раз перемещая инструкцию с выводом значений. На одном из этих многочисленных этапов ошибка обычно обнаруживается. В больших программах можно логически догадаться о примерном расположении инструкции с ошибкой и начать поиск ошибки оттуда, а не с самого начала программы.
Инструкции для вывода промежуточных значений можно расставить уже при написании программы, не дожидаясь возникновения ошибки. В этом случае в начале программы объявляется константа с помощью инструкции #Const, а внутри программы производится проверка значения константы:
Проверить значение константы позволяет следующая конструкция:
#If MY_DEBUG Then
' Здесь размещаем инструкции вывода значений
#End If
Таким образом, меняя значение константы MY_DEBUG с 1 на 0, можно отлючать вывод всех промежуточных значений.
Сделать поиск ошибок более эффективным позволяет отладчик, встроенный в редактор VBA. С его помощью можно выполнять программу по шагам, при этом контролируя значения переменных на каждом шагу. Отладчик позволяет также проверить, соответствует ли порядок выполнения инструкций разработанному ранее алгоритму.
Прежде чем начать отладку необходимо пометить строки внутри программы с помощью точек останова. Для добавления точки останова делаем строку активной, а затем из меню Debug выбираем пункт Toggle Breakpoint. Слева от строки появится кружок, обозначающий точку останова. Добавить точку останова можно еще быстрее. Для этого достаточно щелкнуть слева от строки левой кнопкой мыши. Повторный щелчок позволяет удалить точку останова. Кроме того, для добавления или удаления точки отстанова можно воспользоваться клавишей <F9>. Чтобы удалить все точки останова следует из меню View выбрать пункт Clear All Breakpoints.
Когда точки останова расставлены можно начать отладку. Для этого запускаем программу на выполнение обычным способом. При достижении точки останова выполнение программы прерывается и отладчик ожидает дальнейших действий программиста. Инструкция, которая будет выполняться на следующем шаге, помечается желтой стрелкой слева от строки.
В режиме прерывания можно посмотреть значения различных переменных в окне Locals. Если окно не отображается, то отобразить его можно выбрав в меню View пункт Locals Window. Посмотреть значение переменной можно также если навести указатель мыши на переменную. Значение переменной отобразится во всплывающей подсказке.
При отладке можно контролировать значения отдельных переменных, а не всех сразу. Для этого следует выделить название переменной и из меню Debug выбрать пункт Add Watch. Можно также выделить название переменной и из контектного меню выбрать пункт Add Watch. В открывшемся окне устанавливаем флажок Watch Expression и нажимаем кнопку OK. Значение переменной будет отображаться в окне Watches. Чтобы отобразить окно Watches из меню View выбираем пункт Watch Window. Чтобы отменить отслеживание нужно выделить строку в окне Watches и нажать клавишу <Delete>.
Для пошагового выполнения программы предназначены следующие пункты в меню Debug или соответствующие кнопки на панели инструментов Debug (View | Toolbars | Debug):
- Step Into (клавиша <F8>) — выполняет переход к следующей инструкции;
- Step Over — выполняет одну инструкцию. Если в этой инструкции производится вызов подпрограммы, то подпрограмма выполняется за один шаг и отладчик переходит в режим ожидания после выхода из подпрограммы;
- Step Out — при заходе в подпрограмму этот пункт позволяет выполнить подпрограмму за один шаг и выйти из нее. Отладчик переходит в режим прерывания после выхода из подпрограммы;
- Run To Cursor — выполняет переход к инструкции, в которой расположен курсор.
Если необходимо посмотреть последовательность вызова подпрограмм, то следует открыть окно Call Stack, выбрав в меню View пункт Call Stack.
Подача звукового сигнала
При возникновении ошибки или при неправильном вводе данных имеет смысл привлечь внимание пользователя звуковым сигналом. Сгенерировать звуковой сигнал позволяет инструкция Beep. Пример:
Dim Результат
Beep
Результат = InputBox("Необходимо ввести значение")

Статьи по Visual Basic for Applications (VBA)
|
LightZ Пользователь Сообщений: 1748 |
Всем привет. Всем заранее спасибо! Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать) P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке |
|
|
LightZ Пользователь Сообщений: 1748 |
{quote}{login=Ластик}{date=21.01.2012 03:59}{thema=}{post}ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать) P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке{/post}{/quote} Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
LightZ Пользователь Сообщений: 1748 |
к примеру если изменить код, следующим образом: на те строки, которые не совпадают с помощью ВПР — получаться ошибки #Н/Д. И как обойти ошибку VBA в вышеуказанном примере? Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано: ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр]) т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр) А вы в коде какую-то ерунду пишите |
|
|
Hugo Пользователь Сообщений: 23379 |
Как-то всё глупо… Но по существу вопроса — в этом примере можно так: Sub f() |
|
LightZ Пользователь Сообщений: 1748 |
{quote}{login=Hugo}{date=21.01.2012 04:31}{thema=}{post}Как-то всё глупо… Но по существу вопроса — в этом примере можно так: Sub f() Спасибо, сам не додумался, писал просто On Error Resume Next, но ничего не получалось. Почему глупо? я выложил просто минимальный пример ВПР, только необходимо заменить #Н/Д на 0 с помощью VBA Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
LightZ Пользователь Сообщений: 1748 |
{quote}{login=Ластик}{date=21.01.2012 04:29}{thema=}{post}сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано: ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр]) т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр) А вы в коде какую-то ерунду пишите{/post}{/quote} Что же тут не понятно ?:) Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
Hugo Пользователь Сообщений: 23379 |
Что за А:А, С:С? |
|
Юрий М Модератор Сообщений: 60809 Контакты см. в профиле |
LightZ, для начала загляните сюда: http://www.planetaexcel.ru/tip.php?aid=26 поймёте свою ошибку. Также полезно почитать встроенную справку по этой функции. Особое внимание обратите на искомое значение. |
|
нарисовал для вас пример, см. файл |
|
|
Igor67 Пользователь Сообщений: 3733 |
Читал, читал ну не понимаю на какой Х? ВПР? |
|
KuklP Пользователь Сообщений: 14868 E-mail и реквизиты в профиле. |
Зря накинулись на человека. Каждый имеет право на ошибку. Пусть бы помучался. Зато потом бы ВПР на всю жизнь запомнил:-) А так быстро забудет. Я сам — дурнее всякого примера! … |
|
Hugo Пользователь Сообщений: 23379 |
И вообще — в коде использовать формулы листа как-то неправильно (на мой взгляд). |
|
а я вообще не понял, что человек хочет сделать. Спрашивает про ВПР, ответил про ВПР )) По-моему, человеку нужно было сделать так а мы бы уже что-нибудь придумали, а то «НД в ВПР», а что такое ВПР сам не знает |
|
|
Hugo Пользователь Сообщений: 23379 |
А если делать, как в примере Ластика, так там в каждой ячейке по два ВПР() отрабатывают… не всегда, но в большинстве случаев. |
|
Юрий М Модератор Сообщений: 60809 Контакты см. в профиле |
{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)» |
|
Игорь, формулистов на тебя не хватает )) |
|
|
Hugo Пользователь Сообщений: 23379 |
А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф… |
|
KuklP Пользователь Сообщений: 14868 E-mail и реквизиты в профиле. |
{quote}{login=Юрий М}{date=21.01.2012 05:04}{thema=Re: }{post}{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)» Я сам — дурнее всякого примера! … |
|
{quote}{login=Hugo}{date=21.01.2012 05:07}{thema=}{post}А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф…{/post}{/quote} я думаю 2 раза, потому как нельзя проверить будет ли НД не рассчитав первый ВПР. |
|
|
LightZ Пользователь Сообщений: 1748 |
Всем спасибо! Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
Hugo Пользователь Сообщений: 23379 |
В 10 раз быстрее: Sub tt() End If End With Сравните время: |
|
LightZ Пользователь Сообщений: 1748 |
Спасибо Hugо, так я ещё не умею. Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
ну, и я свой вариант на массивах |
|
|
Hugo Пользователь Сообщений: 23379 |
Алгоритм выше я уже описАл, он не ноухау — диапазон в массив, его в словарь, второй в массив, проверяем по словарю. |
|
LightZ Пользователь Сообщений: 1748 |
{quote}{login=Ластик}{date=21.01.2012 06:19}{thema=}{post}ну, и я свой вариант на массивах{/post}{/quote} Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
|
Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там ) P.S. На больших объёмах Словарь выиграет, на небольших незаметно. |
|
|
Hugo Пользователь Сообщений: 23379 |
Ну на таком объёме тестить конечно несерьёзно — я вообще удивлён, что у меня такая разница получилась. Там может основная потеря времени на «инициализацию» формулы уходит, чего в моём коде нет. |
|
LightZ Пользователь Сообщений: 1748 |
#30 21.01.2012 18:38:28 {quote}{login=Ластик}{date=21.01.2012 06:30}{thema=}{post}Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там ) P.S. На больших объёмах Словарь выиграет, на небольших незаметно.{/post}{/quote} Прикрепленные файлы
Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
I am using a Vlookup in a VBA function as follows:
result = Application.WorksheetFunction.VLookup(target_arg, table_arg, 2, False)
When an entry exists in the lookup table (table_arg), then all is well.
If an entry does not exist in the table, then the cell has a #Value! result. I’ve tried to do some error trapping, but once the Vlookup doesn’t find a value, the function exits and none of my code after the above statement is executed.
I would like to trap this case so that I can do additional processing before I exit the function.
“Constant effort and frequent mistakes are the stepping stones to genius” – Elbert Hubbard
Contents
- 1 A Quick Guide to the VBA VLookup
- 2 Introduction
- 3 A Simple VBA VLookup example
- 4 The Parameters
- 4.1 Parameter 1: lookup_value
- 4.1.1 Using VLookup on a Date Type
- 4.2 Parameter 2: table_array
- 4.3 Parameter 3: col_index-num
- 4.4 Parameter 4: range_lookup
- 4.1 Parameter 1: lookup_value
- 5 Dealing with Errors
- 5.1 Using WorksheetFunction
- 5.2 Using Application
- 5.3 VLookup Error Types
- 5.4 Errors and Causes
- 6 VBA VLookup Speed
- 7 What’s Next?
A Quick Guide to the VBA VLookup
| Parameters | Type |
|---|---|
| Lookup value | The value you are searching for |
| Table array | The range you are searching through |
| Column index | The column number of the value to return. |
| Range look up | Optional — set to False for exact match only. |
Introduction
The VLookup function can be a useful Excel function. Even though it is straightforward to use can often be confusing when used in VBA. In this post, I am going to show how anyone can easily use the VLookup function. I’ll also cover the pitfalls and how to avoid them. Of course, no post would be complete without a ton of examples that you can try for yourself.
If you are not familiar with VLookup in Excel then this page provides a great introduction.
Notes: I use the Underscore character(_) in the code examples. In VBA this allows you to split a line over multiple lines e.g.
' One line sResult = Application.VLookup("Apricot", Sheet1.Range("A10:B10000"), 1)
' Split up with underscore sResult = Application.VLookup( _ "Apricot", Sheet1.Range("A10:B10000"), 1)
A Simple VBA VLookup example
Note: The variable shData in the examples refers to the worksheet by the code name. You can replace this with the code name of the worksheet you are using.
Take a look at the following data:

Use this code to generate this data on any worksheet:
' Use this sub to generate the data ' https://excelmacromastery.com Sub GenerateData() ' Change the sheet name as required With ThisWorkbook.Worksheets("Sheet1") .Range("A1").CurrentRegion.ClearContents .Range("A1:A7").Value = WorksheetFunction.Transpose(Array("Fruit", "Apple", "Apricot", "Orange", "Peach", "Pair", "Plum")) .Range("B1:B7").Value = WorksheetFunction.Transpose(Array("Price", 1.56, 2.33, 1.45, 2.28, 1.67, 1.22)) End With End Sub
The code below will return the price for the Pear i.e. 1.45
' https://excelmacromastery.com/ Sub SimpleVLookup() Dim sRes As String sRes = Application.VLookup("Pear",shData.Range("A2:B7"),2) ' This will print 1.67 to the Immediate Window(Ctrl + G) Debug.Print sRes End Sub
The code looks for the text Pear in the range A2:B7. When it finds the text it returns a value from the same row as the text. The value in determined by the column number argument. We used 2 in this example.


Let’s look at some more examples and results
' Returns 1.45 sRes = Application.VLookup("Orange",shData.Range("A2:B7"),2) ' Returns 1.56 sRes = Application.VLookup("Apple",shData.Range("A2:B7"),2) ' Returns 1.22 sRes = Application.VLookup("Plum",shData.Range("A2:B7"),2) ' Returns Orange as column is 1 sRes = Application.VLookup("Orange",shData.Range("A2:B7"),1) ' Returns Apple as column is 1 sRes = Application.VLookup("Apple",shData.Range("A2:B7"),1) ' Returns Plum as column is 1 sRes = Application.VLookup("Plum",shData.Range("A2:B7"),1)
The Parameters
In this section we will look at the four parameters. These are
- lookup_value – The value to look up. It must be in the first column of the range.
- table_array – This is the range to search. This can also be a VBA array although it very slow using this.
- col_index_num – This contains the column number of the return value. Starts at column number one.
- range_lookup(optional) – Use True(default) to find closest match. Use False to find exact match. Using True assumes that the first columnis sorted alphabetically or numerically.
We will look at these parameters individually starting with the lookup_value parameter.
Parameter 1: lookup_value
This is the value that you are looking up. It must be in the first column of the Range. If you are using the range C4:X10 then the lookup value must be in column C. If you are using the range Z1:AB5 then the lookup value must be in column Z.
The type of value you search for will normally be a string as this example shows
' https://excelmacromastery.com/ Sub StringVLookup() Dim sFruit As String sFruit = "Plum" Dim sRes As Variant sRes = Application.VLookup( _ sFruit, shData.Range("A2:B7"), 2, False) End Sub
We can also search for a number but you have to be careful here:
- If the number is stored as text then the search value must be a string.
- If the number is stored as a number then the search value must be a number.
For example in this data we have the lookup column stored as numbers

In this case, the lookup value must be a Long or you will get an error message.
' https://excelmacromastery.com/ Sub NumberVLookup() Dim num As Long num = 7 Dim sRes As Variant sRes = Application.VLookup( _ num, shData.Range("F2:G7"), 2, True) Debug.Print sRes End Sub
You can also use the Double data type if you are looking up a decimal value. As in the case of an integer it must be stored as a number if you want to use Double.
Using VLookup on a Date Type
Using a Date type is a tricky business. VBA has a Date data type but the worksheet does not.
So the date type needs to be converted to a Long as the following examples show
theDate = CLng(#1/14/2017#) theDate = CLng(CDate("1/14/2017")) theDate = CLng(shData.Range("H10"))
You can then use it as normal in the VLookup function when the search column contains dates
' https://excelmacromastery.com/ Sub DateVLookup() Dim theDate As Long theDate = CLng(#1/14/2017#) Dim sRes As Variant sRes = Application.VLookup( _ theDate, shData.Range("I2:J7"), 2, False) Debug.Print sRes End Sub
Parameter 2: table_array
This parameter refers to the range of the data we are looking up. You will normally use a range for this as we have seen in the examples so far.
If you are using a worksheet table you can use the range of the table.
' https://excelmacromastery.com/ Sub SimpleVLookupTable() Dim sRes As Variant ' Get the table Dim table As ListObject Set table = shData.ListObjects("Table1") ' Use the table for the table_array parameter sRes = Application.VLookup( _ "Plum", table.Range, 2, False) Debug.Print sRes End Sub
You can also use a VBA array with VLookup but this tends to be very slow.
Parameter 3: col_index-num
This parameter refers to the column which contains the value you want to return. Column 1 is the leftmost column of the table_array.
If the column number is greater than the number of columns in the range you will get an error. See The VLookup Error Types section below.

© BigStockPhoto.com
Parameter 4: range_lookup
This is an optional parameter. If it is not used then it takes True as the default value.
False means that an exact match must be found.
True means that an approximate match will be returned. The first column must be ordered numerically or alphabetically for this to work correctly.
Let’s look at the sample data again

The following code shows some examples of how this parameter works:
' https://excelmacromastery.com/ Sub SimpleVLookup() Dim rg As Range Set rg = shData.Range("A2:B7") Dim sRes As Variant ' Stops at Orange - the last item before a P item sRes = Application.VLookup("P", rg, 2, True) ' Stops at Orange - the last item before a Pea item sRes = Application.VLookup("Pea", rg, 2, True) ' Stops at Peach - the last item before a Pead item sRes = Application.VLookup("Pead", rg, 2, True) ' Error - no exact match found sRes = Application.VLookup("Pea", rg, 2, False) End Sub
Dealing with Errors

© BigStockPhoto.com
We can use VLookup in two ways in VBA. With Application or with WorksheetFunction
Application.WorksheetFunction.VLookup Application.VLookup
The difference between them is how we handle errors. Let’s look at each of these in turn.
Using WorksheetFunction
Using WorksheetFunction.VLookup requires us to use On Error to trap the error. We can then check the error number Err.Number to see if the value is valid.
' https://excelmacromastery.com/ Sub UseWorksheetFunction() Dim sRes As Variant ' Turn on error trapping On Error Resume Next Err.Clear sRes = Application.WorksheetFunction.VLookup _ ("Plum", shData.Range("A2:B7"), 2, False) ' Check if value found If Err.Number = 0 Then Debug.Print "Found item. The value is " & sRes Else Debug.Print "Could not find value: " & "Plum" End If End Sub
Using Application
Using Application.VLookup we can simply check the return value to see if there was an error
' https://excelmacromastery.com/ Sub UsingApplication() Dim sRes As Variant sRes = Application.VLookup _ ("Plum", shData.Range("A2:B7"), 2, False) ' Check if value found If IsError(sRes) = False Then Debug.Print "Found item. The value is " & sRes Else Debug.Print "Could not find value: " & "Plum" End If End Sub
VLookup Error Types
The following table shows a list of the Excel cell error numbers and what they mean. These are the error numbers we get when we use Application.VLookup. This is taken from this MSDN Page
| Constant | Error number | Cell error value |
|---|---|---|
| xlErrDiv0 | 2007 | #DIV/0 |
| xlErrNA | 2042 | #N/A |
| xlErrName | 2029 | #NAME? |
| xlErrNull | 2000 | #NULL! |
| xlErrNum | 2036 | #NUM! |
| xlErrRef | 2023 | #REF! |
| xlErrValue | 2015 | #VALUE! |
Errors and Causes
The following table shows some common errors you may encounter with VLookup. If you’re having trouble with a particular VLookup error then it is a good idea to try it in Excel first.
| Error | Cell | Possible causes |
|---|---|---|
| Error 2015 | #VALUE! | The column number is less than one. |
| Error 2015 | #VALUE! | You used a string instead of a range for the table_array parameter. |
| Error 2023 | #REF! | The column number is greater than the number of columns. |
| Error 2042 | #N/A | The value is not found. See possible causes below. |
If you cannot find the value then check the following:
- Ensure the Table/Range is correct.
- Ensure the Table/Range does not include the header(VBA will think list is unsorted).
- Ensure the Table/Range is using the correct worksheet.
- If searching for a number use a long or double data type in the lookup_value parameter. See lookup_value section above
- If searching for a number stored as text use a string data type in the lookup_value parameter.
- If searching for a date convert it to a long(see Date Type above) in the lookup_value parameter.
If you are getting the wrong value then check the following:
- If the range_lookup parameter is True or not used, then ensure the first column is sorted alphabetically or numerically (see range_lookup above)
VBA VLookup Speed
Sometimes you may need to perform a large number of lookups. In these cases, VLookup could be too slow. The VBA Dictionary is faster when dealing with a large number of lookups. The following code shows an example of using the Dictionary.
' https://excelmacromastery.com/ Sub UseDictionary() ' Get the range of values Dim rg As Range Set rg = shData.Range("M1:N20000") ' Create the dictionary Dim dict As Object Set dict = CreateObject("Scripting.Dictionary") ' Fill the dictionary Dim cell As Range For Each cell In rg dict(cell.Value) = cell.Offset(0, 1).Value Next ' Perform the Lookups For Each cell In rg Debug.Print dict(cell.Value) Next End Sub
What’s Next?
Free VBA Tutorial If you are new to VBA or you want to sharpen your existing VBA skills then why not try out the The Ultimate VBA Tutorial.
Related Training: Get full access to the Excel VBA training webinars and all the tutorials.
(NOTE: Planning to build or manage a VBA Application? Learn how to build 10 Excel VBA applications from scratch.)

