Excel 将工作表转换为仅包含字符串的数组

Excel 将工作表转换为仅包含字符串的数组,excel,vbscript,vba,Excel,Vbscript,Vba,我需要将excel工作表中的数据提取到数组中,该数组将用于使用VBScript作为脚本语言的应用程序(Quick Test Professional)。为此,我们可以使用以下代码: ' ws must be an object of type Worksheet Public Function GetArrayFromWorksheet(byref ws) GetArrayFromWorksheet = ws.UsedRange.Value End Function myArray =

我需要将excel工作表中的数据提取到数组中,该数组将用于使用VBScript作为脚本语言的应用程序(Quick Test Professional)。为此,我们可以使用以下代码:

' ws must be an object of type Worksheet
Public Function GetArrayFromWorksheet(byref ws)
    GetArrayFromWorksheet = ws.UsedRange.Value
End Function

myArray = GetArrayFromWorksheet(myWorksheet)
MsgBox "The value of cell C2 = " & myArray(2, 3)
很好,但不幸的是,返回的数组不仅包含文本字符串,还包含date、integer、double等类型的原语。数据被转换了多次

[编辑]示例:在单元格中输入
=NOW()
并将单元格格式设置为
hh:mm
使显示值17:45,上述方法重新运行类型为
double
的变量和类似41194.7400990741的值

下面的解决方案工作得更好:我可以使用
.text
属性从单元格中获取文本,但它们只在一个单元格上工作,而不在一系列单元格上工作。我无法像使用
.Value
属性那样对数组一次执行此操作,因此我必须一次填充一个单元格:

Public Function GetArrayFromWorksheet_2(byref ws)
    Dim range, myArr(), row, col
    Set range = ws.UsedRange

    ' build a new array with the row / column count as upperbound
    ReDim myArr(range.rows.count, range.columns.count)

    For row = 1 to range.rows.count
        For col = 1 to range.columns.count
            myArr(row, col) = range.cells(row, col).text
        Next
    Next

    GetArrayFromWorksheet_2 = myArr
End Function
但是哎哟。。。循环的嵌套
。是的,在大的工作表上有明显的性能下降。

有人知道更好的方法吗?

正如我们在评论中所述,为了避免这个问题,您需要在某个时候循环数组。然而,我之所以发布这篇文章,是因为根据工作表上的数据类型,它可能会给你带来显著的速度提升。当200个单元格一半是数字时,速度大约快了38%。在600个相同比例的细胞中,改善率为41%

通过循环遍历数组本身,仅检索解释为双精度(数值)的值的
.Text
,如果存在大量非双精度数据,则可以看到速度的提高。对于包含文本的单元格、格式为日期的日期或空白单元格,这将不会选中
.Text

Public Function GetArrayFromWorksheet_3(ByRef ws)

    Dim range, myArr, row, col
    Set range = ws.UsedRange

    'Copy the values of the range to temporary array
    myArr = range
    'Confirm that an array was returned.
    'Value will not be an array if the used range is only 1 cells
    If IsArray(myArr) Then
        For row = 1 To range.Rows.Count
            For col = 1 To range.Columns.Count
                'Make sure array value is not empty and is numeric
                If Not IsEmpty(myArr(row, col)) And _
                            IsNumeric(myArr(row, col)) Then
                    'Replace numeric value with a string of the text.
                    myArr(row, col) = range.Cells(row, col).Text
                End If
            Next
        Next
    Else
        'Change myArr into an array so you still return an array.
        Dim tempArr(1 To 1, 1 To 1)
        tempArr(1, 1) = myArr
        myArr = tempArr
    End If

    GetArrayFromWorksheet_3 = myArr
End Function
  • 将工作表复制到新工作表中
  • 复制粘贴值以删除公式
  • 对每列进行文本转换,将每列转换为文本
  • 按照最初的方式加载阵列
  • 删除新工作表

    • 如果不循环浏览工作表,就无法快速轻松地完成此操作。 如果将上述技术与两行代码一起使用,则必须使用变量类型数组

      我已经从我的代码中包含了一个真实的例子,它分6行完成,因为我喜欢a)使用工作表对象,B)在原始的最后一行保留一个变量

      Dim wsKeyword As Worksheet
      Set wsKeyword = Sheets("Keywords")
      
      Dim iLastKeywordRow As Long
      iLastKeywordRow = wsKeyword.Range("A" & wsKeyword.Rows.Count).End(xlUp).Row
      
      Dim strKeywordsArray As Variant
      strKeywordsArray = wsKeyword.Range("A1:N" & iLastKeywordRow).Value
      
      注意您的数组必须是以这种方式使用的变体

      变体如此工作的原因是,当您创建变体数组时,数组中的每个“单元”都设置为变体类型。然后每个单元格将其变量类型设置为分配给它的任何类型的值。因此,分配字符串的变量被设置为variant.string,现在只能用作字符串。在最初的示例中,时间值似乎存储为variant.time而不是variant.string

      有两种方法可以解决原始问题 1) 循环并使用更多控制来执行流程,如双嵌套for循环。在另一个答案中解释,这会让你完全控制
      2) 将所有数据按原样存储在数组中,然后将其重新格式化为第二个数组,或者在使用时将其格式化为所需的文本(两者都应该更快)

      为什么不循环数组而不是范围并使用CStr?您能给出使用ooo方法无法使用的输入值的具体示例吗?@DanielCook我必须搜索:我在六个月前就遇到了这些问题,并找到了第二个解决方案。“当时已经足够了。@AutomatedChaos-没有内置的一行程序来做你想做的事情。通过不连续地重新计算
      列,您可能可以稍微提高性能。Count
      行。Count
      :将计数放入变量中,并将其用于循环限制。我认为您所做的是尽可能快的。唯一需要补充的是,如果您有不同的行高度,那么您的循环将显著减慢。请参阅Charles Williams的帖子:如果我这样做,使用=NOW()的示例,我会得到文本“=NOW”。OK。然后首先复制,然后在将文本运行到列之前粘贴值。(编辑即将到来)对于OPs示例,最终仍然会看到从NOW()派生的11:40,例如41194。4861681713@Daniel,这不是我测试复制粘贴格式、复制粘贴值、然后将文本复制到列时的经验。=Now()保留日期/时间格式。从注释中我们可以得出结论,不幸的是,这不是一个稳定的解决方案。它还增加了另一层复杂性和失败的可能性:使用变通方法可能会在将来产生问题。谢谢你一直思考!将其标记为迄今为止最好的解决方案,因为它非常适合我的情况:我的工作表中有很多空单元格和非数字单元格,因此这将带来巨大的速度增益。Protip:VBScript没有,因此我将把内部的
      IsEmpty
      IsNumeric
      拆分为两个嵌套的
      If
      语句。这是一个不错的解决方案,但是从
      range().text
      array
      的转换不起作用(这是问题的一部分。我也在考虑使用工作表对象,因为只有在需要时才进行数据转换,并且只使用希望从中获取数据的单元格,但我不想打开excel实例(我使用VBScript创建excel的COM实例)。因此,将其放入数组是我不得不做出的让步。啊,今天我有空时会尝试更新我的答案,说明2行程序如何仅与.value+变体一起工作。我最近工作有点忙,可能会删除它!感谢反馈。将我的答案更新为