Страница: 1 |
Задача: есть текстовый файл размером 65мгб. Он состоит из упорядоченных строк. Нужно огранизовать по ним поиск. Число строк как писалось ранее можно определить так Dim FileToRead As String Но чтобы организовать бинарный поиск нужно знать среднею строку файла ну это понятно будет LineCount/2 Для доступа к среднею строке я так понял можно в цикле до средней строки использовать функцию SkipLine Ну вот не задача. Когда нашел середину файла и например оказвается нужно искать строку в области от начала файла до его середины, придется открывать файл снова(что бы перейти к началу) Т.е. по файлу можно предвигаться только снизу вверх, но не наоборот. Т.е. достигнув следующей записи с помощью ReadLine нельзя вернуться назад на предыдущую запись? У кого есть какие соображения по улучшению кода?
Страница: 1 |
Вопрос: Сложный вопросик: Быстрый поиск в текстовом файле
Добавлено: 09.06.04 13:43
Автор вопроса: Dima34
Dim Position As Long
Dim Free_hFile As Long
Dim BufferSize As Long
Dim Buffer As String
Dim t1 As Single, t2 As Single
Dim Count_CrLf As Long
FileToRead = "C:\prim.txt"
Position = 1
Free_hFile = FreeFile
BufferSize = 570000
Buffer = Space$(BufferSize)
Dim FileLen As Long
Open FileToRead For Binary As #Free_hFile
FileLen = LOF(1)
t1 = Timer
For Position = 1 To FileLen Step BufferSize
Get #Free_hFile, Position, Buffer
Count_CrLf = Count_CrLf + UBound(Split(Buffer, vbCrLf))
Next Position
t2 = Timer
Close #Free_hFile
MsgBox "Complete at " & t2 - t1 & " sec", , "LineCount=" & Count_CrLf
т.к. файл открыт с последовательным доступом.
Ответы
Всего ответов: 14
Номер ответа: 1
Автор ответа:
Pashenko
ICQ: 176176951
Вопросов: 14
Ответов: 655
Профиль | | #1
Добавлено: 09.06.04 14:19
А что, если?
1. загнать в память по нескольку первых символов каждой строки (скажем, в массив).
2. найти интервал (номера первой и последней строк, которые могут совпадать с искомой). это в памяти.
3. последовательно перебрать строки из найденного интервала (это уже в файле).
Номер ответа: 2
Автор ответа:
Sanchez
ICQ: 330837479
Вопросов: 1
Ответов: 13
Web-сайт:
Профиль | | #2
Добавлено: 09.06.04 14:29
А почему ты используешь бинарный доступ? Разве нельзя читать обычным input'ом, и по строкам? Записать все строки в массив, и потом уже после прочтения всего файла искать то что тебе надо.
Номер ответа: 3
Автор ответа:
Pashenko
ICQ: 176176951
Вопросов: 14
Ответов: 655
Профиль | | #3
Добавлено: 09.06.04 14:36
2 Sanchez: Не очень хорошо загонять в память 65МБ.
2 Dima34: А вообще, Sanchez дело говорит - для такого файла, пожалуй, самым оптимальным будет метод прямого перебора - прочитал строку - сравнил, прочитал следующую и т. д. Всех делов - один проход. Кстати, совершенно до лампочки, сортирован файл или нет...
Номер ответа: 4
Автор ответа:
@CyRax PTR
ICQ: 204447456
Вопросов: 28
Ответов: 664
Web-сайт:
Профиль | | #4
Добавлено: 09.06.04 15:05
Попробуй поколдовать в сторону SetFilePointer (WIN API).
Могу даже пример с API-GUIDE скинуть:
===
Const MOVEFILE_REPLACE_EXISTING = &H1
Const FILE_ATTRIBUTE_TEMPORARY = &H100
Const FILE_BEGIN = 0
Const FILE_SHARE_READ = &H1
Const FILE_SHARE_WRITE = &H2
Const CREATE_NEW = 1
Const OPEN_EXISTING = 3
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000
Private Declare Function SetVolumeLabel Lib "kernel32" Alias "SetVolumeLabelA" (ByVal lpRootPathName As String, ByVal lpVolumeName As String) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function SetFileAttributes Lib "kernel32" Alias "SetFileAttributesA" (ByVal lpFileName As String, ByVal dwFileAttributes As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA" (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, ByVal lpTempFileName As String) As Long
Private Declare Function MoveFileEx Lib "kernel32" Alias "MoveFileExA" (ByVal lpExistingFileName As String, ByVal lpNewFileName As String, ByVal dwFlags As Long) As Long
Private Declare Function DeleteFile Lib "kernel32" Alias "eleteFileA" (ByVal lpFileName As String) As Long
Private Sub Form_Load()
'KPD-Team 1998
'URL: http://www.allapi.net/
'E-Mail: KPDTeam@Allapi.net
 im sSave As String, hOrgFile As Long, hNewFile As Long, bBytes() As Byte
 im sTemp As String, nSize As Long, Ret As Long
'Ask for a new volume label
sSave = InputBox("Please enter a new volume label for drive C:\" + vbCrLf + " (if you don't want to change it, leave the textbox blank)"
If sSave <> "" Then
SetVolumeLabel "C:\", sSave
End If
'Create a buffer
sTemp = String(260, 0)
'Get a temporary filename
GetTempFileName "C:\", "KPD", 0, sTemp
'Remove all the unnecessary chr$(0)'s
sTemp = Left$(sTemp, InStr(1, sTemp, Chr$(0)) - 1)
'Set the file attributes
SetFileAttributes sTemp, FILE_ATTRIBUTE_TEMPORARY
'Open the files
hNewFile = CreateFile(sTemp, GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)
hOrgFile = CreateFile("c:\config.sys", GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)
'Get the file size
nSize = GetFileSize(hOrgFile, 0)
'Set the file pointer
SetFilePointer hOrgFile, Int(nSize / 2), 0, FILE_BEGIN
'Create an array of bytes
ReDim bBytes(1 To nSize - Int(nSize / 2)) As Byte
'Read from the file
ReadFile hOrgFile, bBytes(1), UBound(bBytes), Ret, ByVal 0&
'Check for errors
If Ret <> UBound(bBytes) Then MsgBox "Error reading file ..."
'Write to the file
WriteFile hNewFile, bBytes(1), UBound(bBytes), Ret, ByVal 0&
'Check for errors
If Ret <> UBound(bBytes) Then MsgBox "Error writing file ..."
'Close the files
CloseHandle hOrgFile
CloseHandle hNewFile
'Move the file
MoveFileEx sTemp, "C:\KPDTEST.TST", MOVEFILE_REPLACE_EXISTING
'Delete the file
 eleteFile "C:\KPDTEST.TST"
Unload Me
End Sub
===
Построчно не советую считывать такой большой файл. Это займет очень много времени. Хотя... делай как знаешь.
Номер ответа: 5
Автор ответа:
Fallout
Вопросов: 10
Ответов: 387
Web-сайт:
Профиль | | #5
Добавлено: 09.06.04 15:21
То что ты Dima34 создаёшь буфер в 570кб это не гуд... читать и писать лучше Апишками.... это раз во вторых файл большой нужно использовать ФайМапинг... а не ивзрщаться с буферами....
Номер ответа: 6
Автор ответа:
@CyRax PTR
ICQ: 204447456
Вопросов: 28
Ответов: 664
Web-сайт:
Профиль | | #6
Добавлено: 09.06.04 15:58
Вообще то это я ему написал (единственный кто предоставил работающий пример). С файлами на API долго возиться. Ты вот толдычишь API-API, а примера так и не предоставил. Покажи как делать маппинг. В PB буфера можно было бы в потоках параллельно запустить. Но это так - просто в голову пришло.
Номер ответа: 7
Автор ответа:
Dima34
Вопросов: 4
Ответов: 4
Профиль | | #7
Добавлено: 09.06.04 16:11
Это всё не совсем подходит. Это всё я делаю из под Asp. Поэтому в Api я сомневаюсь. А простым перебором я сейчас и пользуюсь. Поиск по последнюю запись занимает 27 сек. Всего строк около 620 000 поэтому идею с масивом я сразу отмел. Хотя может и напрасно.
Номер ответа: 8
Автор ответа:
Pashenko
ICQ: 176176951
Вопросов: 14
Ответов: 655
Профиль | | #8
Добавлено: 09.06.04 16:34
Не мучайся, всё равно, хотя бы один раз, файл придётся прочитать целиком (или, как минимум, до первого вхождения искомой строки).
Номер ответа: 9
Автор ответа:
Fallout
Вопросов: 10
Ответов: 387
Web-сайт:
Профиль | | #9
Добавлено: 09.06.04 16:45
Я тут один способ счас придумал поиска слова... в файле
1) Поиск регистро зависимый или нет?
2) Достаточно сказать что слово в файле есть или ещё и предложение вывести?
Номер ответа: 10
Автор ответа:
Dima34
Вопросов: 4
Ответов: 4
Профиль | | #10
Добавлено: 09.06.04 17:44
Fallout
1.Регистр не зависимый
2.нужно вывести строку в которой присутствует это слово
Пока поиск у меня идет так:
Dim Pth, priz, arrTxT
Pth = "C:\price.txt"
Set fso = CreateObject("Scripting.FileSystemObject"
Set TF = fso.OpenTextFile(Pth, 1, False, -2)
priz = False
'слова для поиска в строке
numMKTPT = "zm7338"
t1 = Timer
Do While (Not TF.AtEndOfStream)
Buffer = TF.ReadLine
If InStr(1, Buffer, numMKTPT, vbTextCompare) >= 1 Then priz = True: Exit Do
Loop
arrTxT = Split(CStr(Buffer), ";", 17)
t2 = Timer
MsgBox "Complete at " & t2 - t1 & " sec"
Есть ли возможность ускорить работу кода?
Может использовать другие ф-ции для чтения файла
Номер ответа: 11
Автор ответа:
Dima34
Вопросов: 4
Ответов: 4
Профиль | | #11
Добавлено: 09.06.04 17:46
кстати пока время работы цикла до последней записи 15 сек.
Номер ответа: 12
Автор ответа:
Fallout
Вопросов: 10
Ответов: 387
Web-сайт:
Профиль | | #12
Добавлено: 09.06.04 23:41
Моя дурацкая мысль заключалась в том чтобы ... проверять на строку следующим образом...
наша стока... для поиска предположим 12121авпавпва1357
мы берём первый байт и последний... этой строки и соотвенно проверяем... +) то бишь если ... в буфере первый байт ... равен первому байту нашей строки то мы не лезем проверять все байты... строки .. а проверяем последний байт ... нашей строки и если он тоже совпал тогда проверяем уже всю строку +)
Номер ответа: 13
Автор ответа:
Sharp
Лидер форума
ICQ: 216865379
Вопросов: 106
Ответов: 9979
Web-сайт:
Профиль | | #13
Добавлено: 11.06.04 00:37
> Ты вот толдычишь API-API, а примера так и не предоставил. Покажи как делать маппинг.
В Iczelion Tutorial очень ясно и доходчиво написано, как делать маппинг. С ним должно быть заметно быстрее.
Номер ответа: 14
Автор ответа:
@CyRax PTR
ICQ: 204447456
Вопросов: 28
Ответов: 664
Web-сайт:
Профиль | | #14
Добавлено: 12.06.04 00:38
>FileLen = LOF(1)
Тут ошибка
Нужно LOF(Free_hFile)
У меня ещё была идея, но к сожалению почасовка кончилась.
Идея была такова:
Заменить UBound(Split(Buffer, vbCrLf))
На функцию, которая находит Instr'ом все VbCrLf в буфере. Этим ты убиваещь сразу 2-х зайцев: считаешь количество строк и находишь их позиции в файле. Занеся все позиции в массив, ты потом за доли секунды можешь прочесть любую строку в файле.