Как проверить наличие пустого массива в макросе vba

Я хочу проверить наличие пустых массивов. Google дал мне разные решения, но ничего не помогло. Может я неправильно их применяю.

Function GetBoiler(ByVal sFile As String) As String
'Email Signature
    Dim fso As Object
    Dim ts As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set ts = fso.GetFile(sFile).OpenAsTextStream(1, -2)
    GetBoiler = ts.ReadAll
    ts.Close
End Function

Dim FileNamesList As Variant, i As Integer
' activate the desired startfolder for the filesearch
FileNamesList = CreateFileList("*.*", False) ' Returns File names
' performs the filesearch, includes any subfolders
' present the result
' If there are Signatures then populate SigString
Range("A:A").ClearContents
For i = 1 To UBound(FileNamesList)
    Cells(i + 1, 1).Formula = FileNamesList(i)
Next i

SigString = FileNamesList(3)

If Dir(SigString) <> "" Then
    Signature = GetBoiler(SigString)
Else
    Signature = ""
End If

Здесь, если массив FileNamesList пуст, GetBoiler(SigString) вообще не должен вызываться. Когда массив FileNamesList пуст, SigString также пуст, и это вызывает функцию GetBoiler() с пустой строкой. Я получаю сообщение об ошибке в строке

Set ts = fso.GetFile(sFile).OpenAsTextStream(1, -2)

поскольку sFile пусто. Есть ли способ избежать этого?


person Vicky    schedule 15.10.2008    source источник
comment
Ознакомьтесь с modArraySupport, полезным набором процедур Чипа Пирсона для поддержки обработки массивов. IsArrayEmpty делает то, что вы хотите.   -  person Jean-François Corbett    schedule 14.07.2011
comment
Если TypeName (FileNamesList) = Empty, то   -  person tsolina    schedule 06.04.2020


Ответы (22)


Когда вы имеете дело со строковым массивом, рассматривали ли вы возможность соединения?

If Len(Join(FileNamesList)) > 0 Then
person Fionnuala    schedule 15.10.2008
comment
Это элегантно и работает с пустым массивом. - person Patrick Honorez; 15.02.2010
comment
да это коротко. но потенциально он делает много ненужной работы. - person peter; 27.11.2011
comment
@VBOG Хотите уточнить? - person Fionnuala; 22.01.2014
comment
@Remou. Разве FileNamesList не массив? Разве вы не должны использовать Ubound вместо Len? - person ; 22.01.2014
comment
@VBOG Нет, потому что JOIN только что создал строку из массива. - person Fionnuala; 22.01.2014
comment
Я обнаружил, что мне нужно добавить "" в качестве аргумента-разделителя для присоединения, т.е. If Len(Join(FileNamesList, "")) > 0 Then - person logworthy; 02.12.2015
comment
@Remou, UBound не работает с грязным грязным нижним индексом вне допустимого диапазона. Я очень хочу, чтобы он вернулся -1 - person peterb; 18.04.2017
comment
Загвоздка в том, что это не работает, если у меня есть массив пустых строк ... На самом деле это не говорит мне, был ли массив инициализирован. - person Profex; 29.09.2017

Сделайте тройной минус:

If (Not Not FileNamesList) <> 0 Then
    ' Array has been initialized, so you're good to go.
Else
    ' Array has NOT been initialized
End If

Или просто:

If (Not FileNamesList) = -1 Then
    ' Array has NOT been initialized
Else
    ' Array has been initialized, so you're good to go.
End If

В VB по какой-либо причине Not myArray возвращает указатель SafeArray. Для неинициализированных массивов возвращается -1. Вы можете Not выполнить XOR с -1, возвращая ноль, если хотите.

               (Not myArray)   (Not Not myArray)
Uninitialized       -1                 0
Initialized    -someBigNumber   someOtherBigNumber

Источник

person ahuth    schedule 17.01.2013
comment
Замечательное решение. Никогда не знал об этом взломе. Ваше здоровье! - person hypers; 21.02.2016
comment
Хороший трюк, +1 от меня, но, к сожалению, я не могу использовать его как часть функции, так как получаю ошибку времени выполнения 13, несоответствие типа Function isArrayInitialized2(Arr As Variant) As Boolean If (Not Arr) = -1 Then isArrayInitialized2 = False Else isArrayInitialized2 = True End Function - person nkatsar; 31.05.2017
comment
@nkatsar вам нужно передать Arr как Arr () - person user425678; 16.07.2017

Если вы протестируете функцию массива, она будет работать для всех границ:

Function IsVarArrayEmpty(anArray As Variant)

Dim i As Integer

On Error Resume Next
    i = UBound(anArray,1)
If Err.number = 0 Then
    IsVarArrayEmpty = False
Else
    IsVarArrayEmpty = True
End If

End Function
person Lance Roberts    schedule 06.03.2009
comment
Неплохо, но попробуйте это на arr, определенном следующим образом _2 _... Это возвращает False, но по всем учетным записям arr является пустым массивом (или как еще его назвать?) С, как ни странно, UBound(arr)=-1 и LBound(arr)=0. Эта проблема решена в IsArrayEmpty Чипа Пирсона. Вот почему я связался с ним в своем комментарии к вопросу. - person Jean-François Corbett; 17.09.2014
comment
К сожалению, существует неопределенный массив, когда UBound выдает ошибку. Также есть пустой массив, где длина находится как и в любом другом случае: UBound (-1) - LBound (0) = 0 - person Andrew Dennison; 04.11.2015
comment
Привет, если вы находитесь на базе 0, ваш массив может добавить один элемент с индексом = 0. Это работает, если вы не устанавливаете массив, но не используете redim arr (0). В этом случае ваше решение не работает. Вам нужно проверить, если Ubound (arr) = 0 и len (Join (arr)) = 0. Это часть ответа @BBQ Chef - person GeoStoneMarten; 22.09.2016

Я вижу здесь похожие ответы ... но не мои ...

Вот как я, к сожалению, собираюсь с этим справиться ... Мне нравится подход len (join (arr))> 0, но он не сработал бы, если бы массив был массивом пустых строк ...

Public Function arrayLength(arr As Variant) As Long
  On Error GoTo handler

  Dim lngLower As Long
  Dim lngUpper As Long

  lngLower = LBound(arr)
  lngUpper = UBound(arr)

  arrayLength = (lngUpper - lngLower) + 1
  Exit Function

handler:
  arrayLength = 0 'error occured.  must be zero length
End Function
person Perposterer    schedule 26.06.2015

Когда я пишу VBA, у меня в голове возникает такая фраза: «Могло бы быть так просто, но ...»

Вот для чего я его адаптировал:

Private Function IsArrayEmpty(arr As Variant)
  ' This function returns true if array is empty
  Dim l As Long

  On Error Resume Next
  l = Len(Join(arr))
  If l = 0 Then
    IsArrayEmpty = True
  Else
    IsArrayEmpty = False
  End If

  If Err.Number > 0 Then
      IsArrayEmpty = True
  End If

  On Error GoTo 0
End Function

Private Sub IsArrayEmptyTest()
  Dim a As Variant
  a = Array()
  Debug.Print "Array is Empty is " & IsArrayEmpty(a)
  If IsArrayEmpty(a) = False Then
    Debug.Print "  " & Join(a)
  End If
End Sub
person BBQ Chef    schedule 01.04.2011
comment
Есть более короткие обсуждения проверки массива на пустоту в сборке. Это просто смешно. - person André Fratelli; 03.02.2015
comment
@ André Fratelli Как бы я хотел проголосовать за ваш комментарий - person GLRoman; 28.07.2017

Этот код не делает того, что вы ожидаете:

If Dir(SigString) <> "" Then
    Signature = GetBoiler(SigString) 
Else
    Signature = "" 
End If

Если вы передадите пустую строку ("") или vbNullString в Dir, он вернет имя первого файла в текущем пути к каталогу (путь, возвращенный CurDir$). Итак, если SigString пусто, ваше If условие будет оцениваться как True, потому что Dir вернет непустую строку (имя первого файла в текущем каталоге), и будет вызываться GetBoiler. И если SigString пусто, вызов fso.GetFile завершится ошибкой.

Вам следует либо изменить свое условие, чтобы проверить, что SigString не пусто, либо использовать метод FileSystemObject.FileExists вместо Dir для проверки существования файла. Dir сложно использовать именно потому, что он делает то, чего от него нельзя ожидать. Лично я бы использовал Scripting.FileSystemObject вместо Dir, потому что здесь нет забавных дел (FileExists возвращает True, если файл существует, и, ну, False, если нет). Более того, FileExists гораздо яснее выражает намерение вашего кода, чем Dir.

Метод 1. Сначала убедитесь, что SigString не пусто

If SigString <> "" And Dir(SigString) <> "" Then
    Signature = GetBoiler(SigString) 
Else
    Signature = "" 
End If

Метод 2. Используйте метод FileSystemObject.FileExists

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

If fso.FileExists(SigString) Then
    Signature = GetBoiler(SigString) 
Else
    Signature = "" 
End If
person Mike Spross    schedule 15.10.2008

Я просто вставляю ниже код великого Чипа Пирсона. Это прекрасно работает.
Вот его страница о функциях массивов.

Надеюсь, это поможет.

Public Function IsArrayEmpty(Arr As Variant) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' IsArrayEmpty
' This function tests whether the array is empty (unallocated). Returns TRUE or FALSE.
'
' The VBA IsArray function indicates whether a variable is an array, but it does not
' distinguish between allocated and unallocated arrays. It will return TRUE for both
' allocated and unallocated arrays. This function tests whether the array has actually
' been allocated.
'
' This function is really the reverse of IsArrayAllocated.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

    Dim LB As Long
    Dim UB As Long

    err.Clear
    On Error Resume Next
    If IsArray(Arr) = False Then
        ' we weren't passed an array, return True
        IsArrayEmpty = True
    End If

    ' Attempt to get the UBound of the array. If the array is
    ' unallocated, an error will occur.
    UB = UBound(Arr, 1)
    If (err.Number <> 0) Then
        IsArrayEmpty = True
    Else
        ''''''''''''''''''''''''''''''''''''''''''
        ' On rare occasion, under circumstances I
        ' cannot reliably replicate, Err.Number
        ' will be 0 for an unallocated, empty array.
        ' On these occasions, LBound is 0 and
        ' UBound is -1.
        ' To accommodate the weird behavior, test to
        ' see if LB > UB. If so, the array is not
        ' allocated.
        ''''''''''''''''''''''''''''''''''''''''''
        err.Clear
        LB = LBound(Arr)
        If LB > UB Then
            IsArrayEmpty = True
        Else
            IsArrayEmpty = False
        End If
    End If

End Function
person sancho.s ReinstateMonicaCellio    schedule 30.09.2013
comment
RE: В редких случаях, когда я не могу надежно воспроизвести, Err.Number будет 0 для нераспределенного пустого массива; это происходит, когда пустой массив создается с помощью arr = array () - person vbAdder; 22.09.2019

Auth был ближе всего, но его ответ выдает ошибку несоответствия типа.

Что касается других ответов, вам следует избегать использования ошибки для проверки условия, если вы можете, потому что, по крайней мере, это усложняет отладку (что, если что-то еще вызывает эту ошибку).

Вот простое и полное решение:

option explicit
Function foo() As Variant

    Dim bar() As String

    If (Not Not bar) Then
        ReDim Preserve bar(0 To UBound(bar) + 1)
    Else
        ReDim Preserve bar(0 To 0)
    End If

    bar(UBound(bar)) = "it works!"

    foo = bar

End Function
person Mike Bethany    schedule 08.04.2014

Упрощенная проверка пустого массива:

Dim exampleArray() As Variant 'Any Type

If ((Not Not exampleArray) = 0) Then
      'Array is Empty
Else
      'Array is Not Empty
End If
person Robert S.    schedule 21.10.2014
comment
Это единственный способ сделать это, не задерживая ошибку. Невероятный. - person Tom Malkin; 01.05.2017

Вот еще один способ сделать это. Я использовал его в некоторых случаях, и он работает.

Function IsArrayEmpty(arr As Variant) As Boolean

Dim index As Integer

index = -1
    On Error Resume Next
        index = UBound(arr)
    On Error GoTo 0

If (index = -1) Then IsArrayEmpty = True Else IsArrayEmpty = False

End Function
person Surya    schedule 12.05.2016

На основе ответа ahuth;

Function AryLen(ary() As Variant, Optional idx_dim As Long = 1) As Long
    If (Not ary) = -1 Then
        AryLen = 0
    Else
        AryLen = UBound(ary, idx_dim) - LBound(ary, idx_dim) + 1
    End If
End Function

Проверить наличие пустого массива; is_empty = AryLen(some_array)=0

person user425678    schedule 15.07.2017

Вы можете использовать приведенную ниже функцию, чтобы проверить, пуст ли вариант или строковый массив в vba.

Function IsArrayAllocated(Arr As Variant) As Boolean
        On Error Resume Next
        IsArrayAllocated = IsArray(Arr) And _
                           Not IsError(LBound(Arr, 1)) And _
                           LBound(Arr, 1) <= UBound(Arr, 1)
End Function

Пример использования

Public Function test()
Dim Arr(1) As String
Arr(0) = "d"
Dim x As Boolean
x = IsArrayAllocated(Arr)
End Function
person Vignesh Subramanian    schedule 04.05.2015

Другой способ - сделать это раньше. Вы можете создать логическую переменную и установить для нее значение true после загрузки данных в массив. поэтому все, что вам действительно нужно, это простой оператор if, когда вы загружаете данные в массив.

person Tim Boutchia    schedule 11.02.2016

Чтобы проверить, пуст ли массив байтов, самый простой способ - использовать функцию VBA StrPtr().

Если массив байтов пуст, StrPtr() возвращает 0; в противном случае он возвращает ненулевое значение (однако это не адрес первого элемента).

Dim ar() As Byte
Debug.Assert StrPtr(ar) = 0

ReDim ar(0 to 3) As Byte
Debug.Assert StrPtr(ar) <> 0

Однако он работает только с байтовым массивом.

person Fuzzier    schedule 30.10.2016

Function IsVarArrayEmpty(anArray As Variant) as boolean
    On Error Resume Next
    IsVarArrayEmpty = true
    IsVarArrayEmpty = UBound(anArray) < LBound(anArray)
End Function

Может ubound вылетает и остается истинным, а если ubound < lbound, то пусто

person Pierre    schedule 30.09.2016

Я обобщу проблему и вопрос, как задумано. Проверить задание на массив и отловить возможную ошибку

Function IsVarArrayEmpty(anArray as Variant)
Dim aVar as Variant

IsVarArrayEmpty=False
On error resume next
aVar=anArray(1)
If Err.number then '...still, it might not start at this index
    aVar=anArray(0)
    If Err.number then IsVarArrayEmpty=True ' neither 0 or 1 yields good assignment
EndIF
End Function

Конечно, он пропускает массивы со всеми отрицательными индексами или всеми> 1 ... это вероятно? в странной стране, да.

person jpinto3912    schedule 12.11.2008
comment
Если это не работает в странной стране, значит, это не повсеместно. - person Jean-François Corbett; 14.07.2011

Лично я думаю, что один из приведенных выше ответов можно изменить, чтобы проверить, есть ли в массиве содержимое:

if UBound(ar) > LBound(ar) Then

Это обрабатывает ссылки с отрицательными числами и занимает меньше времени, чем некоторые другие варианты.

person Jim Snyder    schedule 27.03.2013
comment
Не работает для нераспределенных массивов (не ReDimed), а также для массивов, которые имеют только один элемент. Также это в значительной степени заимствовано из других (неправильных) ответов на этот вопрос? - person Jean-François Corbett; 17.09.2014

Вы можете проверить, пуст ли массив, получив общее количество элементов с помощью объекта JScript VBArray() (работает с массивами вариантного типа, одномерного или многомерного):

Sub Test()

    Dim a() As Variant
    Dim b As Variant
    Dim c As Long

    ' Uninitialized array of variant
    ' MsgBox UBound(a) ' gives 'Subscript out of range' error
    MsgBox GetElementsCount(a) ' 0

    ' Variant containing an empty array
    b = Array()
    MsgBox GetElementsCount(b) ' 0

    ' Any other types, eg Long or not Variant type arrays
    MsgBox GetElementsCount(c) ' -1

End Sub

Function GetElementsCount(aSample) As Long

    Static oHtmlfile As Object ' instantiate once

    If oHtmlfile Is Nothing Then
        Set oHtmlfile = CreateObject("htmlfile")
        oHtmlfile.parentWindow.execScript ("function arrlength(arr) {try {return (new VBArray(arr)).toArray().length} catch(e) {return -1}}"), "jscript"
    End If
    GetElementsCount = oHtmlfile.parentWindow.arrlength(aSample)

End Function

Для меня это занимает около 0,3 мкс для каждого элемента + инициализация 15 мс, поэтому массив из 10M элементов занимает около 3 секунд. Те же функции могут быть реализованы с помощью ScriptControl ActiveX (он недоступен в 64-разрядных версиях MS Office, поэтому вы можете использовать обходной путь, например this < / а>).

person omegastripes    schedule 05.08.2016
comment
Также обратите внимание на this и это вопросы. - person omegastripes; 05.08.2016

Вы можете проверить его количество.

Здесь cid - это массив.

if (jsonObject("result")("cid").Count) = 0 them
MsgBox "Empty Array"

Надеюсь, это поможет. Хорошего дня!

person Mantej Singh    schedule 19.06.2018

Другое решение для проверки пустого массива

if UBound(ar) < LBound(ar) then msgbox "Your array is empty!"

Или, если вы уже знаете, что LBound равен 0

if -1 = UBound(ar) then msgbox "Your array is empty!"

Это может быть быстрее, чем join (). (А с отрицательными показателями не проверял)

Вот мой образец для фильтрации двух строковых массивов, чтобы они не использовали одни и те же строки.

' Filtering ar2 out of strings that exists in ar1

For i = 0 To UBound(ar1)

    ' filter out any ar2.string that exists in ar1
    ar2 = Filter(ar2 , ar1(i), False)    

    If UBound(ar2) < LBound(ar2) Then
       MsgBox "All strings are the same.", vbExclamation, "Operation ignored":
       Exit Sub

    End If

Next

' At this point, we know that ar2 is not empty and it is filtered 
'
person irm    schedule 15.07.2012
comment
UBound завершится ошибкой, если массив пуст, он не вернет -1 или меньше lbound - person PhilHibbs; 17.10.2012

person    schedule
comment
Если вы идете по этому маршруту, вы должны протестировать UBound > LBound. В VBA вы можете запустить индекс массива, например -5321 To -4112. Просто одна из забавных причуд языка. - person Chel; 16.01.2014

person    schedule
comment
Не работает для массивов с ненулевым отсчетом, например. ReDim arrayToCheck(1 To 10). - person Jean-François Corbett; 17.09.2014