使用VBA分配.Value时保留空string

(编辑:添加2个解决scheme的更多限制)

我有一个表(明智的listobject),我需要复制特定的行。 我使用SourceListRow.Range.Value2 = DestListRow.Range.Value2出于效率的原因(一次复制整个范围)。 复制的单元格范围之后的所有公式列自动完全扩展到新行并对复制的数据执行操作。 我在Windows上使用Excel 2010。

不过,虽然我已经使用这种代码的年龄,我只是现在偶然发现了一个奇怪的时候使用Range.Value / Range.Value2:如果你指定一个空string,最后单元格值不会是一个空string,但它将是空的。 那就是:数据不会被忠实地复制,而且副本可能与源代码有所不同,特别是如果连续的公式使用ISBLANK等。 因此,相同的公式将有不同的结果时,在复制与源的工作。

见下面的testing代码。 打开一个新的空白的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”(单元格值确实是一个空string)。
  • B2告诉TRUE,即使我们刚刚复制A1的值, C2告诉“空”,而它应该告诉“string”如果值被忠实地复制;
  • 作为一个testing,B3告诉TRUE,尽pipe我们只是明确地将其设置为一个空string; C3再次告诉“空”来确认数据改变;
  • 将Range.Formula赋值给OtherRange.Formula works(String数据types保存在B4中),但是我不想复制公式的,我只想复制这个值!
  • 作为一个testing,我模仿了GUI将在A5中产生的效果,果然,Copy / Paste-As-Values确实保留了is-an-empty-string状态。

该怎么办?

  • 使用Range.Copy / Range.PasteSpecial(xlPasteValues)是不可接受的,性能明智的和剪贴板明智的;
  • 使用.Formula也不是一个选项,因为我想复制这些值;
  • 我可以遍历数组中的所有值,testing它们是否为空string,然后将这些单元格的公式设置为=“”,但是我想一次性复制整个多单元格范围以获得效率…
  • 我不能使用自动filter等,因为我的数据住在一个表(又名ListObject); 我可以使用表自己的自动filter,但人们可能已经使用它,所以我不得不恢复它,这是一个kludge太多。
  • 使用Range.Find和Range.Replace也不是一个选项,因为这改变了用户的查找/replace对话框设置。

所有的想法赞赏!

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

我把你的示例代码和生成的工作簿,然后在它上面添加一行filter标题。 下面的代码会将所有的空string改为“ChangeMe”; 您可以复制这些值,并使用Range.Replacereplace目标中“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 

我认为完成了你正在做的事情 – 让我知道这是否是解决scheme的正确方向。

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

虽然效率不如原始代码高,但使用.AutoFilter比在许多单元中循环更有效率。 此外,.AutoFilter将零长度string和空单元格视为相同。

像这样的东西:

 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