当我的UDF返回超过255个字符串时,Excel显示#VALUE!
。
xlwings是0.7.1而excel是2007年,根据微软的说法,单元格中最多可包含32767个字符。
哪里可能是问题?
答案 0 :(得分:2)
我可以说,Py.CallUDF(由xlwings udfs使用)返回一个2D Variant数组。
出于某种原因,似乎从纯VBA UDF返回字符串长度大于255的Variant数组会在excel中调用时导致#VALUE错误。在VBA编辑器中将监视放在阵列上会显示数据完好无损,而且没有正确传递给excel。一点点搜索返回了几个关于VBA中最大字符串长度的问题,但没有专门解决这个问题。
使用>返回字符串数组或单个字符串但是255个字符似乎工作正常。
以下是一些显示问题的纯VBA示例:
返回变体数组:
Function variant_long_string(n)
Dim temp(0 To 0, 0 To 0) As Variant
temp(0, 0) = String(n, "a")
variant_long_string = temp
End Function
从Excel调用,返回(N> 255时失败):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 #VALUE!
返回变体数组的元素:
Function variant_long_string_element(n)
Dim temp(0 To 0, 0 To 0) As Variant
temp(0, 0) = String(n, "a")
variant_long_string_element = temp(0, 0)
End Function
从Excel调用,返回(成功N> 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
返回字符串数组:
Function string_long_string(n)
Dim temp(0 To 0, 0 To 0) As String
temp(0, 0) = String(n, "a")
string_long_string = temp
End Function
从Excel调用,返回(成功N> 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
解决方法强>
如果你的python UDF只返回一个字符串值,如下所示:
@xw.func
def build_long_string(n):
res = 'a'*int(n)
return res
xlwings将在xlwings_udfs模块中自动生成以下VBA宏:
Function build_long_string(n)
If TypeOf Application.Caller Is Range Then On Error GoTo failed
build_long_string = Py.CallUDF(PyScriptPath, "build_long_string", Array(n), ThisWorkbook)
Exit Function
failed:
build_long_string = Err.Description
End Function
作为让您的UDF正常工作的快速补丁,请稍微更改该宏:
Function build_long_string(n)
If TypeOf Application.Caller Is Range Then On Error GoTo failed
temp = Py.CallUDF(PyScriptPath, "build_long_string", Array(n), ThisWorkbook)
build_long_string = temp(0, 0)
Exit Function
failed:
build_long_string = Err.Description
End Function
允许字符串> gt; 255成功使其成为Excel。你可以为数组结果做类似的事情,你只需要通过循环/重新分配从temp到结果的所有值来将Variant数组转换为String数组。
答案 1 :(得分:2)
根据@ schoolie关于将2D Variant数组转换为2D String数组的建议,我在本地xlwings中修改了VBA函数生成逻辑的来源:
在udfs.generate_vba_wrapper()
中取代:
vba.write('{fname} = Py.CallUDF("{module_name}", "{fname}", {args_vba}, ThisWorkbook)\n',
module_name=module_name,
fname=fname,
args_vba=args_vba,
)
使用:
vba.write('r = Py.CallUDF("{module_name}", "{fname}", {args_vba}, ThisWorkbook)\n',
module_name=module_name,
fname=fname,
args_vba=args_vba,
)
vba.write('ReDim strarray(UBound(r, 1), UBound(r, 2)) As String\n')
vba.write('For i = 0 To UBound(r, 1)\n')
vba.write(' For j = 0 To UBound(r, 2)\n')
vba.write(' strarray(i, j) = CStr(r(i, j))\n')
vba.write(' Next\n')
vba.write('Next\n')
vba.write('{fname} = strarray\n', fname=fname)
另一个选择是在执行'导入Python UDF后,在VB编辑器中修补生成的VB宏。但是,如果您重新导入,此更改将会丢失。上述代码已由@schoolie
提供