2013-06-03 48 views
8

我正在创建一个快速子文件来对电子邮件进行有效性检查。我想删除'E'列中不包含'@'的整行联系人数据。我使用了下面的宏,但是它的运行速度太慢,因为Excel在删除后移动了所有的行。如果单元格不包含'@',则删除整行的有效方法

我试过另一种技术:set rng = union(rng,c.EntireRow),然后删除整个范围,但我无法防止错误消息。

我也尝试过只添加每行到一个选择,并在所有内容被选中后(如在Ctrl + Select中),随后删除它,但我找不到适当的语法。

任何想法?

Sub Deleteit() 
    Application.ScreenUpdating = False 

    Dim pos As Integer 
    Dim c As Range 

    For Each c In Range("E:E") 

     pos = InStr(c.Value, "@") 
     If pos = 0 Then 
      c.EntireRow.Delete 
     End If 
    Next 

    Application.ScreenUpdating = True 
End Sub 
+0

首先,限制了细胞的数量来遍历。即,而不是'范围(E:E)',使用其中包含数据的范围 – shahkalpesh

+0

我一直想知道如何做到这一点 - 如何选择包含第一个单元格的范围,直到包含数据的最后一个单元格? – Parseltongue

+1

http://www.rondebruin.nl/win/s4/win001.htm - 看看这个。我相信,它会为你回答。回想你的问题,假设你在包含数据的单元格A1中,现在按Ctrl +向下箭头。这将选择所有从A1开始的单元格,直到包含数据的最后一个单元格为止(注意:中间不应该有空单元格)。使用VBA,你可以'lastCell = Range(“A1”)。End(xlDown)' – shahkalpesh

回答

16

你不需要循环来做到这一点。自动过滤器效率更高。 (类似于光标对其中的SQL子句)

自动过滤不包含 “@”,然后所有的行删除它们像这样:

Sub KeepOnlyAtSymbolRows() 
    Dim ws As Worksheet 
    Dim rng As Range 
    Dim lastRow As Long 

    Set ws = ActiveWorkbook.Sheets("Sheet1") 

    lastRow = ws.Range("E" & ws.Rows.Count).End(xlUp).Row 

    Set rng = ws.Range("E1:E" & lastRow) 

    ' filter and delete all but header row 
    With rng 
     .AutoFilter Field:=1, Criteria1:="<>*@*" 
     .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete 
    End With 

    ' turn off the filters 
    ws.AutoFilterMode = False 
End Sub 

注:

  • .Offset(1,0)阻止我们删除标题行
  • .SpecialCells(xlCellTypeVisible)指定自动筛选器应用后剩余的行
  • .EntireRow.Delete删除所有可见的行标题以外的行

步执行代码,你可以看到每行做什么。在VBA编辑器中使用F8。

+0

我得到'下标超出范围'的错误。你能解释两件事吗? 'Set rng = ws.Range(“A1:A”&lastRow)是什么?为什么“A1:A”?以及“.Offset(1,0).SpecialCells(xlCellTypeVisible).EntireRow.Delete”是做什么的? – Parseltongue

+0

我刚刚意识到您正在使用的列是E.错误是因为我在搜索错误的列。将“A”改为“E”,它应该起作用。设置范围指定我们要自动过滤的范围(A1:A以及最后一行的值)。 .Offset(1,0)使我们不会删除标题行。 –

+2

立即尝试 - 我编辑了专栏。 –

2

使用用户shahkalpesh提供的示例,我成功创建了以下宏。我仍然对学习其他技术感到好奇(比如Fnostro引用的一个技术,您可以在其中清除内容,排序,然后删除)。我是VBA新手,所以任何示例都会非常有帮助。

Sub Delete_It() 
    Dim Firstrow As Long 
    Dim Lastrow As Long 
    Dim Lrow As Long 
    Dim CalcMode As Long 
    Dim ViewMode As Long 

    With Application 
     CalcMode = .Calculation 
     .Calculation = xlCalculationManual 
     .ScreenUpdating = False 
    End With 

    With ActiveSheet 
     .Select 
     ViewMode = ActiveWindow.View 
     ActiveWindow.View = xlNormalView 
     .DisplayPageBreaks = False 

     'Firstrow = .UsedRange.Cells(1).Row 
     Firstrow = 2 
     Lastrow = .Cells(.Rows.Count, "E").End(xlUp).Row 

     For Lrow = Lastrow To Firstrow Step -1 
      With .Cells(Lrow, "E") 
       If Not IsError(.Value) Then 
        If InStr(.Value, "@") = 0 Then .EntireRow.Delete 
       End If 
      End With 
     Next Lrow 
     End With 

    ActiveWindow.View = ViewMode 
    With Application 
     .ScreenUpdating = True 
     .Calculation = CalcMode 
    End With 

End Sub 
+0

完成代码工作,但尽可能避免范围循环 - 它们在较大的数据集上可能非常缓慢。如有可能,请使用“AutoFilter”,“SpecialCells”或变体阵列。 – brettdj

3

您是否尝试过使用“@”为准则,然后使用

specialcells(xlcelltypevisible).entirerow.delete 

注意到一个简单的自动过滤:之前和之后@有星号,但我不知道该怎么阻止他们被解析出来!

+0

道歉 - 当我最初发布时,你的回答不在那里。尽管我确实搞乱了标准! – JosieP

1

当你与许多行和许多条件下工作,你最好不要使用行删除的这种方法

Option Explicit 

Sub DeleteEmptyRows() 
    Application.ScreenUpdating = False 

    Dim ws As Worksheet 
    Dim i&, lr&, rowsToDelete$, lookFor$ 

    '*!!!* set the condition for row deletion 
    lookFor = "@" 

    Set ws = ThisWorkbook.Sheets("Sheet1") 
    lr = ws.Range("E" & Rows.Count).End(xlUp).Row 

    ReDim arr(0) 

    For i = 1 To lr 
    If StrComp(CStr(ws.Range("E" & i).Text), lookFor, vbTextCompare) = 0 then 
     ' nothing 
    Else 
     ReDim Preserve arr(UBound(arr) + 1) 
     arr(UBound(arr) - 1) = i 
    End If 
    Next i 

    If UBound(arr) > 0 Then 
     ReDim Preserve arr(UBound(arr) - 1) 
     For i = LBound(arr) To UBound(arr) 
      rowsToDelete = rowsToDelete & arr(i) & ":" & arr(i) & "," 
     Next i 

     ws.Range(Left(rowsToDelete, Len(rowsToDelete) - 1)).Delete Shift:=xlUp 
    Else 
     Application.ScreenUpdating = True 
     MsgBox "No more rows contain: " & lookFor & "or" & lookFor2 & ", therefore exiting" 
     Exit Sub 
    End If 

    If Not Application.ScreenUpdating Then Application.ScreenUpdating = True 
    Set ws = Nothing 
End Sub 
+0

'Select'减慢任何代码,应始终避免。我怀疑这可能会影响过滤效率。 – brettdj

0

相反的循环和1个参考各小区1,抓住一切,把它变成一个变种阵列;然后循环变体数组。

入门:

Sub Sample() 
    ' Look in Column D, starting at row 2 
    DeleteRowsWithValue "@", 4, 2 
End Sub 

真正的工人:

Sub DeleteRowsWithValue(Value As String, Column As Long, StartingRow As Long, Optional Sheet) 
Dim i As Long, LastRow As Long 
Dim vData() As Variant 
Dim DeleteAddress As String 

    ' Sheet is a Variant, so we test if it was passed or not. 
    If IsMissing(Sheet) Then Set Sheet = ActiveSheet 
    ' Get the last row 
    LastRow = Sheet.Cells(Sheet.Rows.Count, Column).End(xlUp).Row 
    ' Make sure that there is work to be done 
    If LastRow < StartingRow Then Exit Sub 

    ' The Key to speeding up the function is only reading the cells once 
    ' and dumping the values to a variant array, vData 
    vData = Sheet.Cells(StartingRow, Column) _ 
       .Resize(LastRow - StartingRow + 1, 1).Value 
    ' vData will look like vData(1 to nRows, 1 to 1) 
    For i = LBound(vData) To UBound(vData) 
     ' Find the value inside of the cell 
     If InStr(vData(i, 1), Value) > 0 Then 
      ' Adding the StartingRow so that everything lines up properly 
      DeleteAddress = DeleteAddress & ",A" & (StartingRow + i - 1) 
     End If 
    Next 
    If DeleteAddress <> vbNullString Then 
     ' remove the first "," 
     DeleteAddress = Mid(DeleteAddress, 2) 
     ' Delete all the Rows 
     Sheet.Range(DeleteAddress).EntireRow.Delete 
    End If 
End Sub 
相关问题