0

我不知道这个问题一开始是否有意义......

给定示例:为单个单元格(我们将其称为)给出以下值A1Sub-value #1|Here's another sub-value #2|Yet again, last but not least, sub-value #3我已经知道有人会告诉我这是应该使用数据库的地方(相信我,我的专业是数据库管理,我知道,但我需要这种方式的数据)。我的分隔符是|. 现在说我想创建一个函数,它将获取LEN()每个子值的 并返回AVERAGE()所有子值的 。如果我想创建一个函数来执行此操作,我可以使用 an split(),获取每个值,执行 anLEN()并返回AVERAGE().

对于给出的示例,让我们使用 cell B1。我过去创建了类似的函数,可以通过以下方法工作(虽然不是这个确切的方法),但它每次都需要拆分和加入数组/单元格值:=ARRAY_AVERAGE(ARRAY_LEN(A1,"|","|"),"|","|").

  • ARRAY_LEN(cell,delimiter[,Optional new_delimiter])
  • ARRAY_AVERAGE(cell,delimiter[,Optional new_delimiter])

但是,我想知道是否有更动态的方法来解决这个问题。基本上,我想要split()一个带有一些自定义 VBA 函数的数组,将它传递给父单元格函数,然后我通过一个将数组合并在一起的函数来包装数组。

下面是 cell 函数的运行方式 =ARRAY_AVERAGE(ARRAY_LEN(ARRAY_SPLIT(A1,"|")))

  • ARRAY_SPLIT(cell,delimiter)将拆分数组。
  • ARRAY_LEN(array)将返回数组的每个子值的长度。
  • ARRAY_AVERAGE(array)将返回数组的每个子值的平均值。由于此函数返回多个值的单个值,因此这将采用ARRAY_JOIN(array,delimiter)将再次合并数组的虚构形式。

这需要在单元格中添加一个或两个附加函数,但它也降低了单元格将与单个单元格值和 VBA 数组相互转换的迭代次数。

你怎么看?可能的?可行的?或多或少的代码效率?

4

2 回答 2

2

现在,这是一个非常粗略的示例,但它应该让您了解如何开始以及如何自定义此方法以满足您的需求。假设您在名为example.txt的文本文件中有以下数据:

Name|Age|DoB|Data1|Data2|Data3
David|25|1987-04-08|100|200|300
John|42|1960-06-21|400|500|600
Sarah|15|1997-02-01|700|800|900

此文件位于文件夹C:\Downloads中。要在 VBA 中使用 ADO 进行查询,您需要参考Microsoft ActiveX Data Objects 2.X Library,其中 X 是您安装的最新版本。我还参考了Microsoft 脚本库来创建我的Schema.ini运行时的文件,以确保我的数据被正确读取。如果没有 Schema.ini 文件,您将面临驱动程序无法读取数据的风险。文本中的数字有时会无缘无故地被读取为 null,日期也经常返回 null。Schema.ini 文件为文本驱动程序提供了数据的准确定义以及如何处理它。您不必像我所做的那样明确定义每一列,但至少您应该设置Format、ColNameHeader 和 DateTimeFormat值。

使用的示例 Schema.ini 文件:

[example.txt]
Format=Delimited(|)
ColNameHeader=True
DateTimeFormat=yyyy-mm-dd
Col1=Name Char
Col2=Age Integer
Col3=DoB Date
Col4=Data1 Integer
Col5=Data2 Integer
Col6=Data3 Integer

您会注意到文件名包含在第一行的括号中。这不是可选的,它还允许您为不同的文件定义不同的模式。如前所述,我在运行时在 VBA 中创建了我的 Schema.ini 文件,其内容如下:

Sub CreateSchema()
    Dim fso As New FileSystemObject
    Dim ts As TextStream

    Set ts = fso.CreateTextFile(FILE_DIR & "Schema.ini", True)

    ts.WriteLine "[example.txt]"
    ts.WriteLine "Format=Delimited(|)"
    ts.WriteLine "ColNameHeader=True"
    ts.WriteLine "DateTimeFormat=yyyy-mm-dd"
    ts.WriteLine "Col1=Name Char"
    ts.WriteLine "Col2=Age Integer"
    ts.WriteLine "Col3=DoB Date"
    ts.WriteLine "Col4=Data1 Integer"
    ts.WriteLine "Col5=Data2 Integer"
    ts.WriteLine "Col6=Data3 Integer"

    Set fso = Nothing
    Set ts = Nothing
End Sub

您会注意到我使用了变量 FILE_DIR,它是我在模块顶部定义的常量。您的 Schema.ini 文件 - 必须 - 与您的数据文件位于同一位置。您查询的连接字符串也使用此目录,因此我定义了常量以确保它们引用相同的位置。这是我的模块的顶部,带有 FILE_DIR 常量以及连接字符串和 SQL 查询:

Option Explicit

Const FILE_DIR = "C:\Downloads\"
Const TXT_CONN = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" & FILE_DIR & ";Extensions=asc,csv,tab,txt;"
Const SQL = "SELECT Name, DoB, ((Data1 + Data2 + Data3)/3) AS [Avg_of_Data]" & _
            "FROM example.txt "

注意TXT_CONN中称为Dbq的部分。这是存储数据文件的目录。实际上,您将在 SQL 字符串的 WHERE 子句中定义您使用的特定文件。SQL常量包含您的查询字符串。在这种情况下,我们只选择名称、DoB 和平均三个数据值。完成所有这些后,您就可以实际执行查询了:

Sub QueryText()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim i As Integer

    'Define/open connection
    With cn
        .ConnectionString = TXT_CONN
        .Open

        'Query text file
        With rs
            .Open SQL, cn
            .MoveFirst

            'Loop through/print column names to Immediate Window
            For i = 0 To .Fields.Count - 1
                Debug.Print .Fields(i).Name
            Next i

            'Loop through recordset
            While Not (.EOF Or .BOF)

                'Loop through/print each column value to Immediate Window
                For i = 0 To .Fields.Count - 1
                    Debug.Print .Fields(i)
                Next i

                .MoveNext
            Wend

            .Close 'Close recordset
        End With

        .Close 'Close connection to file
    End With

    Set rs = Nothing
    Set cn = Nothing
End Sub

我知道我在上面的评论中说过这样做非常简单,而且这看起来需要做很多工作,但我向你保证不是。您可以只使用 QueryText() 方法并最终得到类似的结果。但是,我已经包含了所有其他内容,以尝试为您提供一些想法,让您了解可以将其用于您的项目的地方,并向您展示如果您没有得到预期的结果,如何解决您可能遇到的问题.

这是我最初学习的指南:http: //msdn.microsoft.com/en-us/library/ms974559.aspx

这是对实际 Excel 文件执行相同操作的指南:http: //support.microsoft.com/kb/257819

最后,这里有更多关于 Schema.ini 文件的信息:http: //msdn.microsoft.com/en-us/library/windows/desktop/ms709353 (v=vs.85).aspx

希望您能够找到一种方法在您的工作中利用所有这些信息!学习所有这些的一个附带好处是您可以使用 ADO 来查询实际的数据库,例如 Access、SQL Server 和 Oracle。该代码与此处打印的代码几乎相同。只需换掉连接字符串、sql 字符串,然后忽略有关 Schema.ini 文件的全部内容。

于 2012-09-19T23:08:18.133 回答
1

以下是在单个单元格上工作的 2 个示例 VBA UDF:输入公式为

=AVERAGE(len_text(SPLIT_TEXT(A1,"|")))

请注意,在这种特殊情况下,您实际上并不需要 len_text 函数,您可以改用 Excel 的 LEN(),但是您必须输入 AVERAGE(..) 作为数组公式。

Option Explicit

Public Function Split_Text(theText As Variant, Delimiter As Variant) As Variant
    Dim var As Variant
    var = Split(theText, Delimiter)
    Split_Text = Application.WorksheetFunction.Transpose(var)
End Function
Public Function Len_Text(something As Variant) As Variant
    Dim j As Long
    Dim k As Long
    Dim var() As Variant

    If IsObject(something) Then
        something = something.Value2
    End If

    ReDim var(LBound(something) To UBound(something), LBound(something, 2) To UBound(something, 2))
    For j = LBound(something) To UBound(something)
        For k = LBound(something, 2) To UBound(something, 2)
            var(j, k) = Len(something(j, k))
        Next k
    Next j
    Len_Text = var

End Function
于 2012-09-20T10:16:47.220 回答