С помощью запускаемого в файле "Тест подхватывания номеров.xlsm" единственного макроса я пытаюсь найти в выбираемой в ходе исполнения макроса папке среди .xls файлов находить значения, как-либо совпадающие с каждым значением из второго столбца файла с макросом. Шерстятся все листы и все задействованные диапазоны листов. Найденные значения вставляются справа от искомого значения в файле с макросом. Вот код макроса:
Код
Sub Найти_полный_sn()
Dim Путь As String, Файл As String, sn As Range, Совпадений As Integer, i%, rng As Range
Dim sh As Worksheet
Dim Книга As Excel.Workbook
'выбираем папку с файлами-источниками информации
With Application.FileDialog(msoFileDialogFolderPicker)
.ButtonName = "OK"
.Title = "Выберите папку, содержащую файлы Excel"
If .Show = 0 Then
Exit Sub
End If
Путь = .SelectedItems(1)
End With
Файл = Dir(PathName:=Путь + "\*.xls")
i = 2
Set sn = ActiveSheet.Cells(i, 2)
Do Until sn.Value = "" 'пока не закончатся серийные номера в столбце
Совпадений = 0
Do Until Файл = "" 'пока не закончатся файлы в выбранной папке
Set Книга = Workbooks.Open(Путь + "\" + Файл)
For Each sh In Книга.Sheets 'поиск по листам
sh.Activate
Set rng = sh.UsedRange.Find(What:=sn.Value, LookIn:=xlValues, LookAt:=xlPart)
If Not (rng Is Nothing) Then
Совпадений = 1
sn.Offset(0, Совпадений).Value = rng.Value
Do Until rng Is Nothing
Set rng = sh.UsedRange.FindNext(rng)
If Not (rng Is Nothing) Then
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).Value = rng.Value
End If
Loop
End If
Next sh
Книга.Close
Файл = Dir
Loop
i = i + 1
sn = ActiveSheet.Cells(i, 2).Value 'следующий серийник
Loop
Set Книга = Nothing
End Sub
Возникли следующие неточности работы алгоритма, прошу помочь исправить: 1. Как остановить бесконечное зацикливание, возникающее в самом вложенном Do Loop(это поиск значения на отдельном листе)? 2. Почему-то для первого номера первым находится не короткое значение 60802491, а сразу 23160802491. Почему пропускается первое имеющееся значение
Тестировать макрос можно на втором файле с названием "База с полными номерами.xls", который надо положить в выбираемую в ходе отработки макроса папку.
borro написал: Почему пропускается первое имеющееся значение
Потому что для метода Find "первое" — не самое похожее, а первое, заданное направлением поиска.
Цитата
borro написал: Как остановить бесконечное зацикливание
Код
Set FirstCell=rng
Do
Set rng = sh.UsedRange.FindNext(rng)
If Not (rng Is FirstCell) Then
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).Value = rng.Value
Else: Exit Do
End If
Loop
Там же есть и второе условие выхода из цикла - ненахождение искомого значения на листе при следующей итерации FindNext. Попробовал это воплотить все в коде:
Код
Sub Найти_полный_sn()
Dim Путь As String, Файл As String, sn As Range, FirstCell As Range, Совпадений As Integer
Dim sh As Worksheet
Dim Книга As Excel.Workbook
'выбираем папку с файлами-источниками информации
With Application.FileDialog(msoFileDialogFolderPicker)
.ButtonName = "OK"
.Title = "Выберите папку, содержащую файлы Excel"
If .Show = 0 Then
Exit Sub
End If
Путь = .SelectedItems(1)
End With
Файл = Dir(PathName:=Путь + "\*.xls")
i = 2
Set sn = ActiveSheet.Cells(i, 2)
Do Until sn.Value = "" 'пока не закончатся серийные номера в столбце
Совпадений = 0
Do Until Файл = "" 'пока не закончатся файлы в выбранной папке
Set Книга = Workbooks.Open(Путь + "\" + Файл)
For Each sh In Книга.Sheets 'поиск по листам
sh.Activate
Set rng = sh.UsedRange.Find(What:=sn.Value, LookIn:=xlValues, LookAt:=xlPart)
If Not (rng Is Nothing) Then
Set FirstCell = rng
If Совпадений = 0 Then
Совпадений = 1
Else
Совпадений = Совпадений + 1
End If
sn.Offset(0, Совпадений).Value = rng.Value
Do Until rng Is Nothing Or rng Is FirstCell
Set rng = sh.UsedRange.FindNext(rng)
If Not (rng Is Nothing) Then
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).Value = rng.Value
End If
Loop
End If
Next sh
Книга.Close
Файл = Dir
Loop
i = i + 1
Set sn = ActiveSheet.Cells(i, 2) 'следующий серийник
Loop
Set Книга = Nothing
End Sub
некорректно работает. Для первого значения только одно значение находится из файла-источника. А должно быть три - 60802491, 23160802491, 6080249112321
If Совпадений = 0 Then
Совпадений = 1
Else
Совпадений = Совпадений + 1
End If
интересный подход. Но если вдуматься - когда совпадений 0, то если просто прибавить 1, то будет так же 1. Поэтому If здесь вообще не нужен и достаточно оставить только одну строку:
Код
Совпадений = Совпадений + 1
sh.Activate - тоже лишнее, т.к. далее обращаетесь все равно к конкретному листу. И в общем код должен выглядеть так:
Код
Sub Найти_полный_sn()
Dim Путь As String, Файл As String, sn As Range, Совпадений As Integer, i%, rng As Range
Dim sh As Worksheet, shAct As Worksheet
Dim Книга As Excel.Workbook
Dim s As String
Set shAct = ActiveSheet
'выбираем папку с файлами-источниками информации
With Application.FileDialog(msoFileDialogFolderPicker)
.ButtonName = "OK"
.Title = "Выберите папку, содержащую файлы Excel"
If .Show = 0 Then
Exit Sub
End If
Путь = .SelectedItems(1)
End With
Файл = Dir(PathName:=Путь + "\*.xls")
i = 2
Set sn = shAct.Cells(i, 2)
Do Until sn.Value = "" 'пока не закончатся серийные номера в столбце
Do Until Файл = "" 'пока не закончатся файлы в выбранной папке
Set Книга = Workbooks.Open(Путь + "\" + Файл)
For Each sh In Книга.Sheets 'поиск по листам
' sh.Activate
Set rng = sh.UsedRange.Find(What:=sn.Value, LookIn:=xlValues, LookAt:=xlPart)
If Not (rng Is Nothing) Then
s = rng.Address
Do
Set rng = sh.UsedRange.FindNext(rng)
If Not (rng Is Nothing) Then
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).Value = rng.Value
End If
Loop Until rng Is Nothing Or rng.Address = s
End If
Next sh
Книга.Close
Файл = Dir
Loop
i = i + 1
Совпадений = 0
Set sn = shAct.Cells(i, 2) 'берем следующий серийник
Loop
Set Книга = Nothing
End Sub
Sub br()
' Set sh = ActiveSheet
Set Rng = sh.UsedRange.Find(What:="a", LookIn:=xlValues, LookAt:=xlPart)
If Not (Rng Is Nothing) Then
s = Rng.Address
Совпадений = Совпадений + 1
Do
Set Rng = sh.UsedRange.FindNext(Rng)
If Rng.Address = s Then s = "": Set Rng = Nothing: Exit Do
Совпадений = Совпадений + 1
Loop
End If
End Sub
Всем спасибо! Дмитрий, да, я сам улыбался своей логике сегодня С учетом ваших предложений получилось следующая окончательная работающая версия:
Код
Sub Найти_полный_sn()
Dim Путь As String, Файл As String, sn As Range, FirstCell As Range, Совпадений As Integer, cell As Range, s$
Dim sh As Worksheet
Dim Книга As Excel.Workbook
'выбираем папку с файлами-источниками информации
With Application.FileDialog(msoFileDialogFolderPicker)
.ButtonName = "OK"
.Title = "Выберите папку, содержащую файлы Excel"
If .Show = 0 Then
Exit Sub
End If
Путь = .SelectedItems(1)
End With
Файл = Dir(PathName:=Путь + "\*.xls")
i = 2
Set sn = ActiveSheet.Cells(i, 2)
Do Until sn.Value = "" 'пока не закончатся серийные номера в столбце
Совпадений = 0
Do Until Файл = "" 'пока не закончатся файлы в выбранной папке
Set Книга = Workbooks.Open(Путь + "\" + Файл)
For Each sh In Книга.Sheets 'поиск по листам
Set rng = sh.UsedRange.Find(What:=sn.Value, LookIn:=xlValues, LookAt:=xlPart)
If Not (rng Is Nothing) Then
s = rng.Address
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).Value = rng.Value & " из " & Файл
Do
Set rng = sh.UsedRange.FindNext(rng)
If Not (rng Is Nothing) And rng.Address <> s Then
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).Value = rng.Value & " из " & Файл
End If
Loop Until rng Is Nothing Or rng.Address = s
End If
Next sh
Книга.Close
Файл = Dir
Loop
i = i + 1
Set sn = ActiveSheet.Cells(i, 2) 'следующий серийник
Файл = Dir(PathName:=Путь + "\*.xls")
Loop
Set Книга = Nothing
End Sub
Скажите, пожалуйста, как можно адаптировать предыдущий мною выложенный код так, чтобы поиск слова(строки) шел рекурсивно и по всем подпапкам? Сейчас он только по файлам выбранной папки ищет.
Я попробовал пойти вторым путем - доработать пример, найденный в интернете, но получилось громоздко с двумя процедурами, и я пока не знаю, как заставить программу автоматом отвечать "Не обновлять ссылки" в открываемом файле, плюс автоматом предварительно восстанавливать поврежденные эксель-файлы при их открытии. Вот что пока получилось:
Код
Dim objFSO As Object, objFolder As Object, objFile As Object, Совпадений As Integer
Sub Get_All_from_SubFolders()
Dim sFolder As String
With Application.FileDialog(msoFileDialogFolderPicker)
If .Show = False Then Exit Sub
sFolder = .SelectedItems(1)
End With
sFolder = sFolder & IIf(Right(sFolder, 1) = Application.PathSeparator, "", Application.PathSeparator)
Application.ScreenUpdating = False
Set objFSO = CreateObject("Scripting.FileSystemObject")
i = 2
Set sn = ActiveSheet.Cells(i, 1)
Do Until sn.value = "" 'пока не закончатся серийные номера в столбце
Совпадений = 0
GetSubFolders sFolder, sn, Совпадений
Set objFolder = Nothing
Set objFSO = Nothing
i = i + 1
Set sn = ActiveSheet.Cells(i, 1) 'следующий серийник
Loop
Application.ScreenUpdating = True
End Sub
Private Sub GetSubFolders(sPath, sn, Совпадений)
Dim sPathSeparator As String, sObjName As String, rng As Range
Dim sh As Worksheet
Set objFolder = objFSO.GetFolder(sPath)
For Each objFile In objFolder.Files
If Replace(objFile.Name, objFSO.GetBaseName(objFile), "") Like ".xls*" Then
'открываем книгу
Workbooks.Open sPath & objFile.Name
For Each sh In ActiveWorkbook.Sheets 'поиск по листам
Set rng = sh.UsedRange.Find(What:=sn.value, LookIn:=xlValues, LookAt:=xlPart)
If Not (rng Is Nothing) Then
s = rng.Address
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).value = rng.value & " " & rng.Offset(0, -1).value & " в " & sPath & objFile.Name
Do
Set rng = sh.UsedRange.FindNext(rng)
If Not (rng Is Nothing) And rng.Address <> s Then
Совпадений = Совпадений + 1
sn.Offset(0, Совпадений).value = rng.value & " " & rng.Offset(0, -1).value & " в " & sPath & objFile.Name
End If
Loop Until rng Is Nothing Or rng.Address = s
End If
Next sh
ActiveWorkbook.Close True
End If
Next
For Each objFolder In objFolder.SubFolders
GetSubFolders objFolder.Path & Application.PathSeparator, sn, Совпадений
Next
End Sub
Буду благодарен, если подскажите, каким путем идти