Гость
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Очень долгий перебор строк в цикле / 25 сообщений из 62, страница 1 из 3
16.06.2021, 10:40
    #40077851
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Доброго времени суток, уважаемые форумчане! В макросах не силен, по статейкам в инете написал код. Смысл такой, что в таблицу сначала вставляется необходимое количество строк, затем из буфера обмена (ранее скопированного диапазона в excel) вставляется в диапазон ячеек таблицы word (выбирается таблица №3). Далее перебираются ячейки 3 столбца и при условии, что в ячейке 3 символа, копируется в буфер содержимое ячейки второго столбца, строка очищается, ячейки в строке объединяются и затем в строку вставляется ранее скопированное содержимое ячейки второго столбца, ну и для красоты ячейка затемняется. Проблема в том, что в случае, например, двухста строк код выполняется более менее быстро, а вот когда строк, например, две тысячи, код выполняется настолько долго, что через 15-20 минут, не дождавшись окончания, снимаю задачу диспетчером. Не могу понять, что можно предпринять для исправления.
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
Sub Import()
On Error Resume Next
Application.ScreenUpdating = False
Dim i As Long
    With ActiveDocument
    n = Val(InputBox("Сколько строк в таблице?"))
    If n = 0 Then Exit Sub
    k = n - 1 'корректировка количества строк
    r = n + 1 'корректировка количества строк
        With .Tables(3) 'обращаюсь к третьей таблице
        .Rows(2).Select 'выбираю позицию для добавления строк
        Selection.InsertRowsBelow k 'вставвка заданного количества строк
        .Rows(1).Borders(wdBorderBottom).LineStyle = wdLineStyleDouble 'красявости
        .Rows(r).Borders(wdBorderBottom).LineStyle = wdLineStyleDouble 'красявости
        rs = .Cell(2, 2).Range.Start 'начало дипазона для вставки
        re = .Cell(r, 6).Range.End 'конец дипазона для вставки
        ActiveDocument.Range(rs, re).Paste 'вставка из буфера в заданный диапазон
            For i = 2 To r
                If .Cell(i, 3).Range.Characters.Count = 3 Then 'определение строки для форматирования
                .Cell(i, 2).Range.Copy 'копирую в память содержимое ячейки
                .Rows(i).Range.Delete Unit:=wdCharacter, Count:=1 'очистка строки
                .Cell(i, 1).Merge MergeTo:=.Cell(i, 6) 'объединение ячеек
                .Rows(i).Range.Paste 'вставка ранее запомненного значения
                .Rows(i).Shading.BackgroundPatternColor = wdColorGray15 'красявости
                End If
            Next
        End With
    End With
Application.ScreenUpdating = True
End Sub



Подскажите, пожалуйста, в чем ошибка в коде, существует ли способ ли ускорить его выполнение? Заранее спасибо.
...
Рейтинг: 0 / 0
16.06.2021, 10:58
    #40077859
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Во-первых, тут очень много копирования в буфер. Это занимает очень много времени. По возможности, нужно избежать копирования в буфер, лучше копировать в переменную (если речь идет о значении, если, конечно, речь идет только о значении, а не о формулах, оформлении и прочем)

Во-вторых, любое взаимодействие с ячейками листа тоже достаточно медленное. Поэтому, лучше минимально задействовать такое взаимодействие в цикле и активно использовать массивы, копируя и вставляя значения разом из массива / в массив.
...
Рейтинг: 0 / 0
17.06.2021, 07:45
    #40078146
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, большое Вам спасибо за отзыв. По первому пункту понятно в каком направлении работать, а на счет второго пункта я не совсем понял, каким образом должна происходить работа с массивом, разом копировать и вставлять, если по выполнению условия в ячейке изменяется строка. Буду очень благодарен, если немного подробней объясните как это должно работать.
...
Рейтинг: 0 / 0
17.06.2021, 08:33
    #40078150
Akina
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Markovich21
каким образом должна происходить работа с массивом, разом копировать и вставлять, если по выполнению условия в ячейке изменяется строка.

Она изменяется, потому что там имеется зависимая формула?
...
Рейтинг: 0 / 0
17.06.2021, 09:05
    #40078156
vb_sub
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro,
столкнулся с таким кейсом-нужно обработать таблицу с листа Excel через предварительное загнание её в массив, но для обработки нет необходимости загонять всю таблицу в массив- нужно только 2 столбца из 20 к примеру. Но быстрым методом копирования диапазона в массив
Код: vbnet
1.
   SetTimingRange = Range(.Cells(2, 1), .Cells(rowCount, columnCount)).Value2


можно загнать только всю таблицу целиком в массив.
Получается, чтобы заполнить массив только 2 столбцами, а не всей таблицей, нужно делать цикл по ячейкам, который медленнее, чем Range.Value и вручную их копировать в массив. Соответственно, можно ли несколько Range склеить в один массив без цикла по ячейкам?
...
Рейтинг: 0 / 0
17.06.2021, 09:46
    #40078165
Akina
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
vb_sub
Но быстрым методом копирования диапазона в массив

можно загнать только всю таблицу целиком в массив.

А указать в Range только нужные столбцы идея не приходила? типа
Код: vbnet
1.
SetTimingRange = Range(.Cells(2, 1), .Cells(rowCount, 2)).Value2
...
Рейтинг: 0 / 0
17.06.2021, 09:51
    #40078168
vb_sub
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Akina,
Range это непрерывный диапазон, нельзя указать крайние столбцы и исключить некоторые промежуточные из них.
...
Рейтинг: 0 / 0
17.06.2021, 10:25
    #40078182
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Akina, я может что то не понимаю, но у меня word и формулы в строке нет. Изменяется строка i (объединяются ячейки и в них вставляется текстовое значение из ячейки второго столбца), если в ячейке третьего столбца 3 символа
...
Рейтинг: 0 / 0
17.06.2021, 10:27
    #40078183
big-duke
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
vb_sub
Akina,
Range это непрерывный диапазон

Это не так.
Код: vbnet
1.
Range("A1,B:C,f:f,16:17,20:20").Select
...
Рейтинг: 0 / 0
17.06.2021, 10:39
    #40078190
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Markovich21
у меня word
о боже *FACEPALM*!
Я не заметил слона!

Думаю, что все вышестоящие ответы можно вычеркнуть.

Автору - надо ЯВНО указывать среду разработки, но мы, конечно, тоже хороши!
...
Рейтинг: 0 / 0
17.06.2021, 11:27
    #40078207
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, виноват, каюсь, в будущем учту замечание. И все же для word существует решение данной проблемы?
...
Рейтинг: 0 / 0
17.06.2021, 14:19
    #40078291
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
С вордом всё сложнее, надо возиться и пробовать.

Попробуй для начала убрать работу с буфером внутри цикла. Может это решит проблему до приемлемой скорости.

ЗЫ: Лучше работать с ThisDocument вместо ActiveDocument
...
Рейтинг: 0 / 0
17.06.2021, 21:09
    #40078409
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, спасибо за ответы с наводками. Использовал переменную внутри цикла вместо буфера, скорость выполнения кода действительно возрасла. Теперь хотя бы на двух тысячах строках можно дождаться завершения работы макроса. Код выглядит так:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
Sub ImportMetallTest()
Application.ScreenUpdating = False
Dim i As Integer
Dim S As String
    With ActiveDocument
    N = Val(InputBox("Сколько строк в таблице?"))
    If N = 0 Then Exit Sub
    k = N - 1
    R = N + 1
        With .Tables(3)
        .Rows(2).Select
        Selection.InsertRowsBelow k
        rs = .Cell(2, 2).Range.Start
        re = .Cell(R, 6).Range.End
        ActiveDocument.Range(rs, re).Paste
           For i = 2 To R
               If .Cell(i, 3).Range.Characters.Count = 3 Then
               S = .Cell(i, 2).Range
               .Rows(i).Range.Delete Unit:=wdCharacter, Count:=1
               .Cell(i, 1).Merge MergeTo:=.Cell(i, 6)
               .Rows(i).Range = S
               End If
           Next
       End With
    End With
Application.ScreenUpdating = True
End Sub


Но возникла проблема, что после вставленной переменной отрабатывается Enter и ячейка становится как бы двустроковой. Можно этого как то избежать?
...
Рейтинг: 0 / 0
17.06.2021, 21:23
    #40078415
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Для начала отформатируем, чтобы хоть читать код можно было
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
Sub ImportMetallTest()

Application.ScreenUpdating = False
Dim i As Integer
Dim S As String
With ActiveDocument
    N = Val(InputBox("Сколько строк в таблице?"))
    If N = 0 Then Exit Sub
    k = N - 1
    R = N + 1
    With .Tables(3)
        .Rows(2).Select
        Selection.InsertRowsBelow k
        rs = .Cell(2, 2).Range.Start
        re = .Cell(R, 6).Range.End
        ActiveDocument.Range(rs, re).Paste
        For i = 2 To R
            If .Cell(i, 3).Range.Characters.Count = 3 Then
                S = .Cell(i, 2).Range
                .Rows(i).Range.Delete Unit:=wdCharacter, Count:=1
                .Cell(i, 1).Merge MergeTo:=.Cell(i, 6)
                .Rows(i).Range = S
            End If
        Next
    End With
End With
Application.ScreenUpdating = True

End Sub
...
Рейтинг: 0 / 0
17.06.2021, 21:24
    #40078416
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Попробуй копировать в переменную не .Range а .Range.Text
...
Рейтинг: 0 / 0
18.06.2021, 08:12
    #40078468
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, таким вот образом не помогло:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
        For i = 2 To R
            If .Cell(i, 3).Range.Characters.Count = 3 Then
                S = .Cell(i, 2).Range.Text
                .Rows(i).Range.Delete Unit:=wdCharacter, Count:=1
                .Cell(i, 1).Merge MergeTo:=.Cell(i, 6)
                .Rows(i).Range = S
            End If
        Next


Все-равно откуда то присутствует символ "конец абзаца"
...
Рейтинг: 0 / 0
18.06.2021, 09:46
    #40078486
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Ну значит надо его грохнуть прям в переменной
...
Рейтинг: 0 / 0
18.06.2021, 11:39
    #40078535
istrebitel
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
vb_sub
Range это непрерывный диапазон, нельзя указать крайние столбцы и исключить некоторые промежуточные из них.

Можно использовать несколько Range, по одному на каждую необходимую колонку.
...
Рейтинг: 0 / 0
19.06.2021, 01:38
    #40078813
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, несколько другим способом пытаюсь решить проблему скорости выполнения цикла. Как Вы и советовали, отказался от копирования в буфер внутри цикла. Получился код:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
Sub ImportTest1()

Application.ScreenUpdating = False
Dim i As Integer
Dim S As String
With ActiveDocument
    N = Val(InputBox("Сколько строк в таблице?"))
    If N = 0 Then Exit Sub
    k = N - 1
    R = N + 1
    With .Tables(3)
        .Rows(2).Select
        Selection.InsertRowsBelow k
        rs = .Cell(2, 2).Range.Start
        re = .Cell(R, 6).Range.End
        ActiveDocument.Range(rs, re).Paste
        For i = 2 To R
            If .Cell(i, 3).Range.Characters.Count = 3 Then
                .Cell(i, 1).Merge MergeTo:=.Cell(i, 6)
                .Rows(i).Range = Left(.Rows(i).Range, Len(.Rows(i).Range) - 13)
            End If
        Next
    End With
End With
Application.ScreenUpdating = True

End Sub


В нем в строке, найденной по условию, сначала объединяются ячейки; в объединенной ячейке остается содержимое второй ячейки с множеством скрытых символов, которые обрезаются; в итоге как плюс - не сбивается форматирование. Скорость выполнения кода заметно выше, но все-равно когда строк две тысячи выполняется он очень долго. Не могу разобраться как работать с ThisDocument. Если с ним работать тоже должен быть прирост скорости? Есть еще варианты над чем поработать? Спасибо.
...
Рейтинг: 0 / 0
19.06.2021, 07:22
    #40078817
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
можно еще ускорить, избавившись от двойного обращения к .Rows(i).Range

чтобы ускорять еще - скорее всего уже нужно менять подход в принципе, а может это уже будет невозможно. Без файла образца сложно что-то сказать
...
Рейтинг: 0 / 0
19.06.2021, 23:24
    #40078892
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, двойное обращение к .Rows(i).Range исключил. Вставил таймер, цикл по обработке двух тысяч строк выполняется 85 сек.
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
Sub ImportTest2()

Application.ScreenUpdating = False
Dim i As Integer
Dim S As String
With ActiveDocument
'    N = Val(InputBox("Сколько строк в таблице?"))
    N = 2000
'    If N = 0 Then Exit Sub
    k = N - 1
    R = N + 1
    With .Tables(3)
        .Rows(2).Select
        Selection.InsertRowsBelow k
        rs = .Cell(2, 2).Range.Start
        re = .Cell(R, 6).Range.End
        ActiveDocument.Range(rs, re).Paste
 tm = Timer
        For i = 2 To R
            If .Cell(i, 3).Range.Characters.Count = 3 Then
                .Cell(i, 1).Merge MergeTo:=.Cell(i, 6)
                S = .Rows(i).Range
                S = Left(S, Len(S) - 13)
                .Rows(i).Range = S
            End If
       Next
    End With
End With
Application.ScreenUpdating = True

MsgBox Timer - tm, 64

End Sub


Прилагаю файлы для примера. В таблице excel нажимаю кнопку "копировать в буфер", в word выполняю код. Т.к. два файла приложить не могу, поэтому выложил в архиве. Предполагаю, что можно сократить время, если передавать таблицу из excel в word напрямую, но как это сделать пока не знаю, да и все-таки основное время уходит на обработку строк в цикле, вот его бы ускорить
...
Рейтинг: 0 / 0
24.06.2021, 13:57
    #40079841
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro

чтобы ускорять еще - скорее всего уже нужно менять подход в принципе, а может это уже будет невозможно.

Shocker.Pro, в чем может заключаться подход с другой стороны, имеется ввиду отказаться от цикла перебора ячеек?
...
Рейтинг: 0 / 0
24.06.2021, 14:29
    #40079849
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Я поэкспериментировал с этим кодом. Даже если просто убрать все внутренности цикла, тормозит сама проверка
Код: vbnet
1.
.Cell(i, 3).Range.Characters.Count = 3


Попытки обращения к ячейке другими способами результата особо не дали.

Я не знаю, какие у тебя возможности есть при манипуляции экселем, кроме копипасты
По большому счету, нужно заранее вычислить номера строк для объединения ячеек
...
Рейтинг: 0 / 0
24.06.2021, 15:34
    #40079872
Markovich21
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Shocker.Pro, большое спасибо Вам за помощь и быстрые ответы. Может я делаю это криво, но в экселе, я специально ввожу в ячейку 3 символа, чтобы определить строку в которой нужно объединить ячейки, т.е. эти три символа в ячейке третьего столбца работают как маркер. Может есть какие то другие способы, более быстрые, по которым определить строку, в которой объединить ячейки? А про определение номеров строк для объединения в самом экселе... у меня в голове витала такая идея. Но она для меня какая то фантастическая. В самом экселе определить номера строк для определения сложностей нет (например, через те же три символа в ячейке), но фактически передать в ворд номера строк для объединения не представляю как реализовать. Количество строк и номера строк могут быть абсолютно разные. В экселе нужно выделить ячейку (диапазон) для номеров строк или как то через переменные эти номера передавать?
...
Рейтинг: 0 / 0
24.06.2021, 15:38
    #40079876
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Очень долгий перебор строк в цикле
Markovich21
я специально ввожу в ячейку 3 символа
это программно происходит или руками?
...
Рейтинг: 0 / 0
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Очень долгий перебор строк в цикле / 25 сообщений из 62, страница 1 из 3
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]