Excel VBA 递归函数 return 不是预期的结果
Excel VBA Recursive function return not the expected result
我有以下函数正在调用自身(递归)。目标是 return 一个唯一的文件名,格式为文件名 (1).ext、文件名 (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,则它始终是第一个,因此是唯一的。所以在那种情况下,函数只被调用一次。但是当执行递归并且 DoesFileExists returns 为 false 时,return 值应该 return 生成的文件名并退出。但是,当我调试该函数时,该函数正在无误地执行,但它总是 returns 原始值而不是原始迭代的结果。
例如,如果我这样调用这个函数:CreateUniqueFileName("C:\Temp\",""1010-40-800.jpg",1)
它会在 C:\temp 中检查是否已经存在名为 1010-40-800 (1).jpg 的文件,如果存在则调用相同的函数并将 orderId 更新为 1 in这种情况下 CreateUniqueFileName("C:\Temp\",""1010-40-800.jpg",2)。重复相同的过程(递归)。现在假设 1010-40-800 (2 ).jpg 是唯一的(找不到文件)。我希望函数 return 1010-40-800 (2).jpg 作为字符串结果。但它会 return 值 1010-40-800 (1).jpg。这实际上是第一次调用函数时的值。
我在这里错过了什么?
当您递归调用您的函数时,您的代码中有一个小缺陷。试试这个
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
这仍然没有给你想要的东西,因为它附加了每个 orderID,但你应该看到这个缺陷并希望能够解决剩下的问题。
我使用了以下函数来检查文件是否存在
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
但在这种情况下,IMO 循环会更好地获取唯一的文件名。
更新: 查找附加的递归调用的完全固定版本和 "loop" 版本
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
还有带循环的版本
Function CreateUniqueFileNameA(strPath As String, strFileName) As String
Dim extPos As Integer
Dim extension As String
Dim fileName As String
Dim resFilename As String
Dim orderID As Long
extPos = InStrRev(strFileName, ".")
If extPos > 0 Then
fileName = Left(strFileName, extPos - 1)
extension = Right(strFileName, Len(strFileName) - extPos)
orderID = 0
resFilename = strFileName
Do While DoesFileExist(strPath & resFilename)
orderID = orderID + 1
resFilename = fileName & " (" & CStr(orderID) & ")." & extension
Loop
End If
CreateUniqueFileNameA = resFilename
End Function
您的代码存在结构、逻辑和假设问题。
结构问题是用于拆分扩展名的代码包含您的递归调用,因此如果文件名不包含扩展名,您的递归将永远不会发生。如果这是一个深思熟虑的决定,那么最好早点退出函数,而不是在 if end if 中包含其他所有内容。
你的逻辑错误是你没有正确使用函数的递归调用
Call CreateUniqueFileName(strPath, fileName, orderId + 1)
应该是
CreateUniqueFileName = CreateUniqueFileName(strPath, fileName, orderId + 1)
您的假设问题是函数的参数是值。他们不是。默认情况下 VBA 通过引用传递参数,因此在您的代码中 'filename' 每次调用函数时都是相同的变量而不是新副本。
因此这一行
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
请注意,我没有运行上面的代码,但它编译得很好。
我有以下函数正在调用自身(递归)。目标是 return 一个唯一的文件名,格式为文件名 (1).ext、文件名 (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,则它始终是第一个,因此是唯一的。所以在那种情况下,函数只被调用一次。但是当执行递归并且 DoesFileExists returns 为 false 时,return 值应该 return 生成的文件名并退出。但是,当我调试该函数时,该函数正在无误地执行,但它总是 returns 原始值而不是原始迭代的结果。
例如,如果我这样调用这个函数:CreateUniqueFileName("C:\Temp\",""1010-40-800.jpg",1) 它会在 C:\temp 中检查是否已经存在名为 1010-40-800 (1).jpg 的文件,如果存在则调用相同的函数并将 orderId 更新为 1 in这种情况下 CreateUniqueFileName("C:\Temp\",""1010-40-800.jpg",2)。重复相同的过程(递归)。现在假设 1010-40-800 (2 ).jpg 是唯一的(找不到文件)。我希望函数 return 1010-40-800 (2).jpg 作为字符串结果。但它会 return 值 1010-40-800 (1).jpg。这实际上是第一次调用函数时的值。
我在这里错过了什么?
当您递归调用您的函数时,您的代码中有一个小缺陷。试试这个
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
这仍然没有给你想要的东西,因为它附加了每个 orderID,但你应该看到这个缺陷并希望能够解决剩下的问题。
我使用了以下函数来检查文件是否存在
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
但在这种情况下,IMO 循环会更好地获取唯一的文件名。
更新: 查找附加的递归调用的完全固定版本和 "loop" 版本
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
还有带循环的版本
Function CreateUniqueFileNameA(strPath As String, strFileName) As String
Dim extPos As Integer
Dim extension As String
Dim fileName As String
Dim resFilename As String
Dim orderID As Long
extPos = InStrRev(strFileName, ".")
If extPos > 0 Then
fileName = Left(strFileName, extPos - 1)
extension = Right(strFileName, Len(strFileName) - extPos)
orderID = 0
resFilename = strFileName
Do While DoesFileExist(strPath & resFilename)
orderID = orderID + 1
resFilename = fileName & " (" & CStr(orderID) & ")." & extension
Loop
End If
CreateUniqueFileNameA = resFilename
End Function
您的代码存在结构、逻辑和假设问题。
结构问题是用于拆分扩展名的代码包含您的递归调用,因此如果文件名不包含扩展名,您的递归将永远不会发生。如果这是一个深思熟虑的决定,那么最好早点退出函数,而不是在 if end if 中包含其他所有内容。
你的逻辑错误是你没有正确使用函数的递归调用
Call CreateUniqueFileName(strPath, fileName, orderId + 1)
应该是
CreateUniqueFileName = CreateUniqueFileName(strPath, fileName, orderId + 1)
您的假设问题是函数的参数是值。他们不是。默认情况下 VBA 通过引用传递参数,因此在您的代码中 'filename' 每次调用函数时都是相同的变量而不是新副本。
因此这一行
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
请注意,我没有运行上面的代码,但它编译得很好。