在 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

上面已经给出了代码