2013-06-03 2 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

Во-первых, ограничить количество клеток, чтобы пройти. т.е. вместо 'range (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 удаляет все видимые строки для заголовка строки

Шаг через код, за исключением, и вы можете увидеть, что делает каждая строка. Используйте F8 в редакторе VBA.

+0

Я получаю ошибку «индекс вне диапазона». Не могли бы вы объяснить две вещи? Что означает «Установить rng = ws.Range (« A1: A »и lastRow)? Почему «А1: А»? и что такое «.Offset (1, 0) .SpecialCells (xlCellTypeVisible) .EntireRow.Delete« do? – 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 
Смежные вопросы