Что нового

Найти нужные строки во множестве документов

Frg777

Новичок
Сообщения
3
Репутация
0
Товарищи, подскажите

Имеется несколько сотен гигабайт текстовых документов .txt лежащих в папках и подпапках. В этих документах в столбик записаны имя и фамилия различных людей типа

Василий Иванов
Николай Петров
Федор Зорин
Максим Котов
Николай Белов
Максим Матов
...

Помогите составить команду которая найдет во всех вложеных папках во всех этих сотнях гигабайт текстовых документов только строки содержащие нужные имена, например, только строки с "Василий", "Николай", "Максим" и составит из этих строк новый файл .txt

ПС. Может есть лучший способ как выполнить эту задачу максимально быстро с учетом многогигабайтности данных?
 

IMStrelcov

CTPEJIbLLOB
Сообщения
171
Репутация
30
Код:
;если файлы большого размера (один файл 100 и более мегабайт),
;то потребуется много оперативной памяти
;и много времени на обработку
;имена как Александр и Александра считаются разными

;настройка работы скрипта
;0=нет    1=да
$iWriteFile = 1        ;записывать в новый файл, путь к файлу в котором найдены имена, перед найденными именами
$iOpenFolder = 1    ;открыть по окончанию, папку с новым файлом
$iClearFile = 1        ;очищать новый файл, если он уже есть на диске

;где создать новый  файл
$sFileOut = 'D:\System\Desktop\Новая папка (2)\result.txt'

;искомые имена, через любой из следующих знаков |.,/\ или пробел
$sNames = 'Максим Федор'

;папка в которой лежат файлы которые нужно просмотреть
$sPathIn = 'D:\System\Desktop\Новая папка (2)'




$iCount = 0
$iTime = TimerInit()
$sNames = StringRegExpReplace($sNames, '[\h\\/\.,\|]+', '|')
SearchFiles_($sPathIn)
SaveFile_()
If $iOpenFolder Then Run('explorer.exe /select,"' & $sFileOut & '"')
;If $iOpenFolder Then ShellExecute('"' & $sFileOut & '"')
$iTime = TickToTime_(TimerDiff($iTime))
MsgBox(4096 +64, 'Поиск имен', 'Обработка файлов завершена.' & @CRLF & 'Просмотрено файлов: ' & $iCount & @CRLF & 'Затрачено времени: ' & $iTime)


Func SearchFiles_($_sPathIn)
   Local $_sFile, $_sFileOut, $_sFileIn
   Local $_hSearch = FileFindFirstFile($_sPathIn & '\*.*')
   If @error Then Return SetError(1)
   While 1
      $_sFile = FileFindNextFile($_hSearch)
      If @error Then ExitLoop
      If @extended Then
         SearchFiles_($_sPathIn & '\' & $_sFile)
      ElseIf ($_sPathIn & '\' & $_sFile) <> $sFileOut Then
         SearchText_($_sPathIn & '\' & $_sFile)
         If @error Then ExitLoop
         $iCount += 1
      EndIf
   WEnd
   FileClose($_hSearch)
EndFunc

Func SearchText_($_sFileIn)
   Local $_sData = FileRead($_sFileIn)
   If @error Then Return SetError(1)
   $_sData = StringRegExp($_sData, '(?smix)((?:' & $sNames & ')(?:\h+?[^\r\n]*|$))', 3)
   If Not @error Then
      Local $_sResult = $iWriteFile ? $_sFileIn & @CRLF : ''
      For $_i=0 To UBound($_sData) -1
         $_sResult &= $_sData[$_i] & @CRLF
      Next
      $_sResult &= $iWriteFile ? @CRLF : ''
      SaveFile_($_sResult)
      If @error Then Return SetError(1)
   EndIf
EndFunc

Func SaveFile_($_sData = '')
   Local Static $_hFile = FileOpen($sFileOut, 8 + 1 +($iClearFile <> 0))
   If $_hFile Then
      If $_sData = '' Then
         FileClose($_hFile)
      Else
         FileWrite($_hFile, $_sData)
      EndIf
   Else
      Return SetError(1)
   EndIf
EndFunc

Func TickToTime_($_iTick)
   $_iTick = Ceiling($_iTick / 1000)
   If $_iTick > 359999 Then Return '99:99:99'
   Local $_iHour = Int($_iTick / 3600)
   $_iTick = Mod($_iTick, 3600)
   Local $_iMin = Int($_iTick / 60)
   Local $_iSec = Mod($_iTick, 60)
   $_iTick = StringFormat('%02s:%02s:%02s', $_iHour, $_iMin, $_iSec)
   Return $_iTick
EndFunc
 
Последнее редактирование:
Автор
F

Frg777

Новичок
Сообщения
3
Репутация
0
Спасибо!
Подскажите два вопроса:
1. Поиск рекурсивен? Если в Новая папка 2 содержит еще подпапки и подподпапки в которых тоже файлы txt в которых тоже надо искать
2. Как максимально эффективно выполнить задачу? Может быть используя grep в линуксе или еще какой способ есть?
 

IMStrelcov

CTPEJIbLLOB
Сообщения
171
Репутация
30
1. Да рекурсивен.
2. Не подскажу, так как опыта в этом не имею.

Запустите мой пример и утилиту в линукс, если есть возможность, и засеките время и сравните.
Сообщение автоматически объединено:

1. Да рекурсивен.
для рекурсии исправте строку
Код:
Local $_hSearch = FileFindFirstFile($_sPathIn & '\*.txt')

на
Код:
Local $_hSearch = FileFindFirstFile($_sPathIn & '\*.*')

для справки, запустил скрипт у себя, сработал за 6 секунд, с 26000 файлов, общим размером 10 мегабайт
 
Последнее редактирование:
Автор
F

Frg777

Новичок
Сообщения
3
Репутация
0
1. Да рекурсивен.
2. Не подскажу, так как опыта в этом не имею.

Запустите мой пример и утилиту в линукс, если есть возможность, и засеките время и сравните.
Сообщение автоматически объединено:


для рекурсии исправте строку
Код:
Local $_hSearch = FileFindFirstFile($_sPathIn & '\*.txt')

на
Код:
Local $_hSearch = FileFindFirstFile($_sPathIn & '\*.*')

для справки, запустил скрипт у себя, сработал за 6 секунд, с 26000 файлов, общим размером 10 мегабайт
Спасибо!
А есть ли предельный рвзмер для файлов? У меня файлы txt по несколько гигов. На какого размера файлы их разделить? По 1ГБ? Или еще меньше?
 

IMStrelcov

CTPEJIbLLOB
Сообщения
171
Репутация
30
Тут все сложно, я просто не знаю какого размера данные может обработать регулярное выражение, из-за этого посоветовал бы файлы поделить по мегабайт 10, можно конечно скрипт переписать в таком случае, чтобы читал файл частями, но пока времени нет, возможно ближе к вечеру попробую изменить, если очень нужно.
 
Верх