vba - 使用 VBA 分配给 .Value 时保留空字符串

标签 vba excel

(编辑:为解决方案增加了 2 个限制)

我有一个表(listobject wise),我需要在其中复制某些行。出于效率原因,我使用 SourceListRow.Range.Value2 = DestListRow.Range.Value2 (一次性复制整个范围)。复制的单元格范围之后的所有公式列都会自动完美地扩展到新行并作用于复制的数据。我在 Windows 上使用 Excel 2010。

但是,尽管我已经在使用这种代码进行了 eons,但我现在才在使用 Range.Value/Range.Value2 时偶然发现了一个奇怪的现象:如果您为其分配一个空字符串,则最终的单元格值不会是一个空字符串,但它将是空的。那就是:数据没有被忠实地复制,并且副本可能与源不同,特别是如果连续的公式在其上使用 ISBLANK 等。因此,在处理副本与源时,相同的公式将产生不同的结果。

请参阅下面的测试代码。打开一个新的空白 Excel 工作簿,转到 VBA,添加一个新模块并添加以下代码:

Sub Test()
  ActiveSheet.Range("a1").Formula = "="""""
  ActiveSheet.Range("b1").Formula = "=isblank(a1)"
  ActiveSheet.Range("c1").Value2 = TypeName(ActiveSheet.Range("a1").Value2)

  ActiveSheet.Range("a2").Value2 = ActiveSheet.Range("a1").Value2
  ActiveSheet.Range("b2").Formula = "=isblank(a2)"
  ActiveSheet.Range("c2").Value2 = TypeName(ActiveSheet.Range("a2").Value2)

  ActiveSheet.Range("a3").Value2 = ""
  ActiveSheet.Range("b3").Formula = "=isblank(a3)"
  ActiveSheet.Range("c3").Value2 = TypeName(ActiveSheet.Range("a3").Value2)

  ActiveSheet.Range("a4").Formula = ActiveSheet.Range("a1").Formula
  ActiveSheet.Range("b4").Formula = "=isblank(a4)"
  ActiveSheet.Range("c4").Value2 = TypeName(ActiveSheet.Range("a4").Value2)

  Call ActiveSheet.Range("a1").Copy
  Call ActiveSheet.Range("a5").PasteSpecial(xlPasteValues)
  ActiveSheet.Range("b5").Formula = "=isblank(a5)"
  ActiveSheet.Range("c5").Value2 = TypeName(ActiveSheet.Range("a5").Value2)
End Sub

然后运行它并查看工作表;
  • B1 告诉 FALSE(它应该 - 单元格不为空),C1 告诉“String”(单元格值确实是一个空字符串);
  • B2 告诉 TRUE,即使我们只是按原样复制了 A1 的值; C2 告诉“Empty”,而如果值被忠实复制,它应该告诉“String”;
  • 作为测试,B3 告诉 TRUE,即使我们只是将其显式设置为空字符串; C3 再次告诉“Empty”确认数据修改;
  • 将 Range.Formula 分配给 OtherRange.Formula 有效(字符串数据类型保留在 B4 中),但我不想复制公式,我只想复制值!
  • 作为测试,我模仿 GUI 在 A5 中产生的结果,果然,复制/粘贴为值确实保留了 is-an-empty-string 状态...

  • 该怎么办?
  • 使用 Range.Copy/Range.PasteSpecial(xlPasteValues) 是 Not Acceptable ,性能方面和剪贴板方面;
  • 使用 .Formula 也不是一个选项,因为我想复制这些值;
  • 我可以遍历数组中的所有值,测试它们是否为空字符串,然后将这些单元格的公式设置为 =“”,但我想一次性复制整个多单元格范围以提高效率......
  • 我不能使用自动过滤器等,因为我的数据存在于一个表中(又名 ListObject);我可以使用表自己的自动过滤器,但人们可能已经在使用它,所以我必须在之后恢复它,这太过分了。
  • 使用 Range.Find 和 Range.Replace 也不是一个选项,因为这会更改用户的查找/替换对话框设置。

  • 所有想法表示赞赏!

    最佳答案

    SpecialCells(xlCellTypeBlank) 和 AutoFilter 以不同的方式处理空字符串。您的源数据是否可过滤?如果是这样,您能否使用占位符值,然后将单元格更改为 .Formula = "="""""复制后?

    我获取了您的示例代码并生成了工作簿,然后在其上方添加了一行作为过滤器标题。下面的代码会将所有空字符串更改为“ChangeMe”;然后,您可以复制这些值并使用 Range.Replace 将目标中的所有“ChangeMe”实例替换为“=”“”“”。

    Sub Test2()
    
    ActiveSheet.Range("A2:A6").SpecialCells(xlCellTypeBlanks).Interior.Color = 255 'Just to prove that xlCellTypeBlanks only selects actual blanks
    
    ActiveSheet.Range("A1:A6").AutoFilter Field:=1, Criteria1:="=" 'Shows both actual blanks and ZLS/null strings
    ActiveSheet.Range("A1:A6").SpecialCells(xlCellTypeBlanks).EntireRow.Hidden = True 'Be able to use SpecialCells(xlCellTypeVisible) to reference all ZLS
    ActiveSheet.Range("A2:A6").SpecialCells(xlCellTypeVisible).Value2 = "ChangeMe"
    'Now unfilter and unhide your data, copy using .Value2, then .Replace "ChangeMe" with "="""""
    
    End Sub
    

    我认为这可以完成您正在尝试做的事情-让我知道这是否是解决方案的正确方向。

    编辑:误解了原始问题。下面的旧答案是不正确的。

    虽然不如原始代码高效,但使用 .AutoFilter 比循环遍历许多单元格要高效得多。此外, .AutoFilter 将零长度字符串和空单元格视为相同。

    像这样的东西:
    Sub CopyAndClearFakeBlanks()
    
    Dim WSSource As Worksheet
    Dim WSDest As Worksheet
    Dim LRow As Long
    Dim LPasteRow As Long
    
    Set WSSource = Sheets("Source Data")
    Set WSDest = Sheets("Paste Here")
    
    LRow = WSSource.Range("A:A").Find(what:="*", searchdirection:=xlPrevious).Row 'Note that this will ignore blanks, so you may want to use a different method for finding your last row. Depends on how your users would need the data if there are trailing blanks.
    
    On Error Resume Next
    LPasteRow = 2 'Always need at least one row before the data for filtering properly; you can delete after if necessary
    LPasteRow = WSDest.Range("A:A").Find(what:="*", searchdirection:=xlPrevious).Row + 1
    WSDest.AutoFilterMode = False
    On Error GoTo 0 'ofc use proper error handling in your actual code
    
    WSDest.Range("A" & LPasteRow & ":A" & LPasteRow + LRow - 1).Value2 = WSSource.Range("A1:A" & LRow).Value2
    WSDest.Range("A" & LPasteRow - 1 & ":A" & LPasteRow + LRow).AutoFilter field:=1, Criteria1:="=" 'Show blank or ZLS only
    
    On Error Resume Next
    WSDest.Range("A" & LPasteRow & ":A" & LPasteRow + LRow).SpecialCells(xlCellTypeVisible).Clear 'Turn them into true blanks
    WSDest.ShowAllData
    WSDest.AutoFilterMode = False
    On Error GoTo 0
    
    Set WSDest = Nothing
    Set WSSource = Nothing
    
    End Sub
    

    关于vba - 使用 VBA 分配给 .Value 时保留空字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35599770/

    相关文章:

    excel - 如何*快速*将许多 .txt 文件转换为 .xls 文件

    vba - Word VBA 查找并突出显示匹配项

    vba - VBA 中的函数可选参数不起作用

    vba - 复制行内容和格式(到另一张纸)

    vba - Excel Vba 单元格的异常行为。在合并的单元格上查找

    excel - 通过使用宏防止 ThisWorkbook 中的隐藏工作表可见并从另一个工作簿运行

    vba - 在不使用 OLE 应用程序的情况下提取 Microsoft Office 中的 OLE 对象数据

    excel - 显示带有输入的公式表达式而不是其结果

    Excel公式引用 'CELL TO THE LEFT'

    python - 代码在 PyCharm 上运行,但不在 Jupyter 上运行