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

43

Я хочу проверить пустые массивы. 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 пусто. Любой способ избежать этого?

  • 7
    Ознакомьтесь с modArraySupport , полезным набором процедур от Chip Pearson для поддержки обработки массивов. IsArrayEmpty делает то, что вы хотите.
Теги:
excel-vba
excel

22 ответа

59

Как вы имеете дело со строковым массивом, считаете ли вы Join?

If Len(Join(FileNamesList)) > 0 Then
  • 0
    Это элегантно и работает с пустым массивом.
  • 9
    да, это коротко. но это потенциально делает много ненужной работы.
Показать ещё 6 комментариев
33

Go с тройным отрицательным:

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

Источник

  • 3
    Изумительное решение. Никогда не знал об этом взломе. Ура!
  • 0
    Хороший трюк, +1 от меня, но, к сожалению, я не могу использовать это как часть функции, так как я получаю ошибку времени выполнения 13, несоответствие типов Function isArrayInitialized2(Arr As Variant) As Boolean If (Not Arr) = -1 Then isArrayInitialized2 = False Else isArrayInitialized2 = True End Function If (Not Arr) = -1 Then isArrayInitialized2 = False Else isArrayInitialized2 = True End Function
Показать ещё 1 комментарий
27

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

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
  • 3
    Неплохо, но попробуйте это на arr определенном так: Dim arr() As String : arr = Split("") : Debug.Print IsVarArrayEmpty(arr) ... Возвращает значение False , но для всех учетных записей arr является пустым массивом (или как еще это можно назвать?) со странным образом UBound(arr)=-1 и LBound(arr)=0 . Эта проблема рассматривается в IsArrayEmpty Чипа Пирсона. Вот почему я связался с этим в своем комментарии к вопросу.
  • 0
    К сожалению, существует неопределенный массив, когда UBound выдает ошибку. Существует также пустой массив, где длина находится, как и в любом другом случае: UBound (-1) - LBound (0) = 0
Показать ещё 1 комментарий
6

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

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

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
4

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

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

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
4

При написании 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
  • 4
    Есть более короткие дискуссии о проверке массива на пустоту в сборке. Это просто смешно.
  • 0
    @ Андре Фрателли Как бы я хотел проголосовать за ваш комментарий
3

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

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
2

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

Dim exampleArray() As Variant 'Any Type

If ((Not Not exampleArray) = 0) Then
      'Array is Empty
Else
      'Array is Not Empty
End If
  • 0
    Это единственный способ сделать это, который я могу найти, не фиксируя ошибку. Невероятно.
2

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
1

Основываясь на ahuth answer;

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

1

Чтобы проверить, является ли массив байтов пустым, самым простым способом является использование функции 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

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

1

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

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
1

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

1
Public Function IsEmptyArray(InputArray As Variant) As Boolean

   On Error GoTo ErrHandler:
   IsEmptyArray = Not (UBound(InputArray) >= 0)
   Exit Function

   ErrHandler:
   IsEmptyArray = True

End Function
  • 1
    Если вы идете по этому маршруту, вы должны проверить UBound > LBound . В VBA у вас может быть запущен индекс массива, например, от -5321 To -4112 . Просто одна из "забавных" причуд языка.
  • 0
    Спасибо @женя)
0
if Ubound(yourArray)>-1 then
 debug.print "The array is not empty"
else
 debug.print "EMPTY"
end if
0
Function IsVarArrayEmpty(anArray As Variant) as boolean
    On Error Resume Next
    IsVarArrayEmpty = true
    IsVarArrayEmpty = UBound(anArray) < LBound(anArray)
End Function

== > возможно, ubound сбой, и он остается верным, и если ubound

0

Вы можете проверить, нет ли массива пустым, путем подсчета общего количества элементов с использованием объекта 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 мс, поэтому массив из элементов 10М занимает около 3 секунд. Та же функциональность может быть реализована через ScriptControl ActiveX (она недоступна в 64-разрядных версиях MS Office, поэтому вы можете использовать обходной путь, например this).

  • 0
    Также взгляните на этот и этот вопросы.
0

Вы можете использовать следующую функцию, чтобы проверить, нет ли пустого или строкового массива в 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
0

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

if UBound(ar) > LBound(ar) Then

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

  • 1
    Сбой для нераспределенных массивов (не ReDim ed), а также для массивов, которые имеют только один элемент. Также это в значительной степени плагиат от других (неправильных) ответов на этот вопрос?
0

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

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

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

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

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

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

' 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 
'
  • 2
    UBound потерпит неудачу, если массив пуст, он не вернет -1 или на единицу меньше, чем lbound
0

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

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... это вероятно? в weirdland, да.

  • 6
    Если это не работает в странной стране, то это не "обобщено".
-1
Public Function arrayIsEmpty(arrayToCheck() As Variant) As Boolean
    On Error GoTo Err:
    Dim forCheck
    forCheck = arrayToCheck(0)
    arrayIsEmpty = False
    Exit Function
Err:
    arrayIsEmpty = True
End Function
  • 0
    Сбой для ненулевых массивов, например, ReDim arrayToCheck(1 To 10) .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню