在 excel 单元格中显示 >255 个字符
displaying >255 chars in excel cell
Excel 当我的 UDF returns 超过 255 个字符的字符串时显示 #VALUE!
。
xlwings 是 0.7.1,excel 是 2007,根据 Microsoft 的说法,一个单元格最多可以包含 32767 个字符。
问题出在哪里?
据我所知,Py.CallUDF(由 xlwings udfs 使用)returns 一个 2D Variant 数组。
似乎出于某种原因,从纯 VBA UDF 返回字符串长度大于 255 的 Variant 数组在 excel 中调用时会导致 #VALUE 错误。在 VBA 编辑器的数组上放置一个手表显示数据是完整的,只是没有正确传递给 excel。在 VBA 中稍作搜索就返回了几个关于最大字符串长度的问题,但没有任何内容专门解决这个问题。
Returning 字符串数组或大于 255 个字符的单个字符串似乎工作正常。
这里有几个纯粹的 VBA 例子来说明问题:
Return 变体数组:
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、returns 调用(N > 255 时失败):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 #VALUE!
Return 变体数组的元素:
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、returns 调用(N > 255 时成功):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
Return 字符串数组:
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、returns 调用(N > 255 时成功):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
解决方法
如果您的 python UDF 只有 returns 单个字符串值,像这样:
@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
允许长度 >255 的字符串成功达到 Excel。您可以对数组结果做类似的事情,您只需要通过 looping/reassigning 从 temp 到结果的所有值将 Variant 数组转换为 String 数组。
根据@schoolie上面关于将2D Variant数组转换为2D String数组的建议,我修改了我本地xlwings中VB一个函数生成逻辑的源码:
在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)
另一种选择是在执行 'Import Python UDFs' 后在 VB 编辑器中修补生成的 VB 宏。但是,如果您重新导入,此更改将会丢失。 @schoolie
上面已经给出了代码
Excel 当我的 UDF returns 超过 255 个字符的字符串时显示 #VALUE!
。
xlwings 是 0.7.1,excel 是 2007,根据 Microsoft 的说法,一个单元格最多可以包含 32767 个字符。
问题出在哪里?
据我所知,Py.CallUDF(由 xlwings udfs 使用)returns 一个 2D Variant 数组。
似乎出于某种原因,从纯 VBA UDF 返回字符串长度大于 255 的 Variant 数组在 excel 中调用时会导致 #VALUE 错误。在 VBA 编辑器的数组上放置一个手表显示数据是完整的,只是没有正确传递给 excel。在 VBA 中稍作搜索就返回了几个关于最大字符串长度的问题,但没有任何内容专门解决这个问题。
Returning 字符串数组或大于 255 个字符的单个字符串似乎工作正常。
这里有几个纯粹的 VBA 例子来说明问题:
Return 变体数组:
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、returns 调用(N > 255 时失败):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 #VALUE!
Return 变体数组的元素:
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、returns 调用(N > 255 时成功):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
Return 字符串数组:
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、returns 调用(N > 255 时成功):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
解决方法
如果您的 python UDF 只有 returns 单个字符串值,像这样:
@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
允许长度 >255 的字符串成功达到 Excel。您可以对数组结果做类似的事情,您只需要通过 looping/reassigning 从 temp 到结果的所有值将 Variant 数组转换为 String 数组。
根据@schoolie上面关于将2D Variant数组转换为2D String数组的建议,我修改了我本地xlwings中VB一个函数生成逻辑的源码:
在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)
另一种选择是在执行 'Import Python UDFs' 后在 VB 编辑器中修补生成的 VB 宏。但是,如果您重新导入,此更改将会丢失。 @schoolie
上面已经给出了代码