Excel VBA递归函数未返回预期结果

时间:2019-11-03 07:59:50

标签: excel vba recursion filesystemobject

我有以下函数正在调用自身(递归)。目标是返回格式为filename(1).ext,filename(2).ext等的唯一文件名。

Function CreateUniqueFileName(strPath As String, strFileName, orderId As Integer) As String
Dim extPos As Integer
Dim extension As String
Dim fileName As String

fileName = ""

extPos = InStrRev(strFileName, ".")

If (extPos > 0) Then
    fileName = Left(strFileName, extPos - 1)
    extension = Right(strFileName, Len(strFileName) - extPos)

    If (orderId = 0) Then
        fileName = strFileName
        CreateUniqueFileName = fileName
    Else
        fileName = fileName & " (" & CStr(orderId) & ")." & extension
    End If

    If (DoesFileExist(strPath & fileName)) Then
        Call CreateUniqueFileName(strPath, fileName, orderId + 1)
    Else
        CreateUniqueFileName = fileName
        Exit Function
    End If
End If
End Function

如果是第一次调用,并且orderId值为0,则始终是第一个,因此是唯一的。因此,在这种情况下,该函数仅被调用一次。但是,当执行递归并且DosFileExists返回false时,返回值应该返回生成的文件名并退出。但是,当我调试该函数时,没有错误,但是它总是返回原始值,而不是原始迭代的结果。

例如,如果我这样调用此函数:CreateUniqueFileName(“ C:\ Temp \”,“” 1010-40-800.jpg“,1) 它会在 C:\ temp 中检查是否已经存在一个名为1010-40-800(1).jpg的文件,如果是的话,将调用相同的函数并将orderId更新为1(在这种情况下为CreateUniqueFileName) (“ C:\ Temp \”,“” 1010-40-800.jpg“,2)。重复相同的过程(回溯)。现在假设 1010-40-800(2).jpg 是唯一的(找不到文件)。我希望函数以字符串结果的形式返回 1010-40-800(2).jpg ,但是它将返回值

1010-40-800(1).jpg 。这实际上是第一次调用该函数的值。

我在这里想念什么?

2 个答案:

答案 0 :(得分:1)

当递归调用函数时,您的代码中只有一个小缺陷。试试这个

d = d.astype({'b': 'category'}).groupby('a').agg({'b':lambda x: x.iat[0], 'c':['mean', 'max']})
print(d)
         b    c    
  <lambda> mean max
a                  
1        1  1.5   2
2        2  3.5   4

这仍然不能满足您的要求,因为它会附加每个orderID,但您应该会发现该漏洞,并希望能够解决其余问题。

我使用以下功能检查文件是否存在

Function CreateUniqueFileName(strPath As String, strFileName, orderId As Integer) As String
    Dim extPos As Integer
    Dim extension As String
    Dim fileName As String

    fileName = ""

    extPos = InStrRev(strFileName, ".")

    If (extPos > 0) Then
        fileName = Left(strFileName, extPos - 1)
        extension = Right(strFileName, Len(strFileName) - extPos)

        If (orderId = 0) Then
            fileName = strFileName
            CreateUniqueFileName = fileName
        Else
            fileName = fileName & " (" & CStr(orderId) & ")." & extension
        End If

        If (DoesFileExist(strPath & fileName)) Then
            CreateUniqueFileName = CreateUniqueFileName(strPath, fileName, orderId + 1)
        Else
            CreateUniqueFileName = fileName
            'Exit Function
        End If
    End If
End Function

但是在这种情况下,IMO会更适合获得唯一文件名的循环。

更新:查找附带的用于递归调用的完全固定版本和“循环”版本

Function DoesFileExist(fullFileName As String) As Boolean

    Dim TestStr As String
    TestStr = ""
    On Error Resume Next
    TestStr = Dir(fullFileName)
    On Error GoTo 0
    If TestStr = "" Then
        DoesFileExist = False
    Else
        DoesFileExist = True
    End If

End Function

以及带有循环的版本

 Function CreateUniqueFileName(strPath As String, strFileName, orderID As Integer) As String
    Dim extPos As Integer
    Dim extension As String
    Dim fileName As String
    Dim resFilename As String

    extPos = InStrRev(strFileName, ".")

    If (extPos > 0) Then
        fileName = Left(strFileName, extPos - 1)
        extension = Right(strFileName, Len(strFileName) - extPos)

        If (orderID = 0) Then
            resFilename = strFileName
        Else
            resFilename = fileName & " (" & CStr(orderID) & ")." & extension
        End If

        If (DoesFileExist(strPath & resFilename)) Then
            CreateUniqueFileName = CreateUniqueFileName(strPath, strFileName, orderID + 1)
        Else
            CreateUniqueFileName = resFilename
        End If

    End If
End Function

答案 1 :(得分:1)

您的代码存在结构,逻辑和假设方面的问题。

结构问题是,用于拆分扩展名的代码包含您的递归调用,因此,如果文件名不包含扩展名,则将永远不会发生递归。如果这是一个故意的决定,那么最好尽早退出该功能,而不是将其他所有内容包含在if结束条件中。

您的逻辑错误是您没有正确使用函数的递归调用

Call CreateUniqueFileName(strPath, fileName, orderId + 1)

应该是

CreateUniqueFileName = CreateUniqueFileName(strPath, fileName, orderId + 1)

您的假设问题是函数的参数是值。他们不是。默认情况下,VBA通过引用传递参数,因此每次调用函数时,在代码“文件名”中都是相同的变量,而不是成为新副本。

因此一行

fileName = fileName & " (" & CStr(orderId) & ")." & extension

在使用文件名而不是strFilename进行递归时,只会导致文件名问题。

我已经对您的代码进行了重组,以使递归部分更整洁(尽管其他人认为最好使用循环)

Function CreateUniqueFileName(ByVal StrPath As String, ByVal strFileName, ByRef orderId As Integer) As String

Dim FileNameArray                                As Variant

    FileNameArray = Split(strFileName, ".")

    If Len(FileNameArray(1)) = 0 Then

        Debug.Print ("CreateUniqueFilename says strFilename has no extension")
        CreateUniqueFileName = vbNullString
        Exit Function

    End If

    If orderId = 0 Then

       CreateUniqueFileName = FileNameArray(0) & Format(orderId, "0000") & FileNameArray(1)
       Exit Function

    End If

    CreateUniqueFileName = GetUniqueName(StrPath, FileNameArray, orderId)

End Function


Public Function GetUniqueName(ByRef StrPath As String, ByRef FileNameArray As Variant, ByVal orderId As Integer) As String
' StrPath and FIlenamearray are passed by reference as they don't change during the recursion
' orderid is passed by value so that we don't change the value of orderid in the calling code.
' If this side effect is desired, change the ByVal to ByRef

Dim myFilename                                     As String

    myFilename = FileNameArray(0) & Format(orderId, "0000") & FileNameArray(1)

    If (DoesFileExist(StrPath & myFilename)) Then

        GetUniqueName = GetUniqueName(StrPath, FileNameArray, orderId + 1)

    Else

        GetUniqueName = myFilename

    End If

End Function

请注意,我没有运行上面的代码,但可以正常编译。