Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Общий форум

Страница: 1 |

 

  Вопрос: Сложный вопросик: Быстрый поиск в текстовом файле Добавлено: 09.06.04 13:43  

Автор вопроса:  Dima34

Задача: есть текстовый файл размером 65мгб. Он состоит из упорядоченных строк. Нужно огранизовать по ним поиск.

Число строк как писалось ранее можно определить так

    Dim FileToRead As String
    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

Но чтобы организовать бинарный поиск нужно знать среднею строку файла ну это понятно будет LineCount/2

Для доступа к среднею строке я так понял можно в цикле до средней строки использовать функцию SkipLine
т.к. файл открыт с последовательным доступом.

Ну вот не задача. Когда нашел середину файла и например оказвается нужно искать строку в области от начала файла до его середины, придется открывать файл снова(что бы перейти к началу)

Т.е. по файлу можно предвигаться только снизу вверх, но не наоборот. Т.е. достигнув следующей записи с помощью ReadLine нельзя вернуться назад на предыдущую запись?

У кого есть какие соображения по улучшению кода?

 

Ответить

  Ответы Всего ответов: 14  

Номер ответа: 1
Автор ответа:
 Pashenko



ICQ: 176176951 

Вопросов: 14
Ответов: 655
 Профиль | | #1 Добавлено: 09.06.04 14:19
А что, если?
1. загнать в память по нескольку первых символов каждой строки (скажем, в массив).
2. найти интервал (номера первой и последней строк, которые могут совпадать с искомой). это в памяти.
3. последовательно перебрать строки из найденного интервала (это уже в файле).

Ответить

Номер ответа: 2
Автор ответа:
 Sanchez



ICQ: 330837479 

Вопросов: 1
Ответов: 13
 Web-сайт: fasco.nm.ru
 Профиль | | #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-сайт: basicproduction.nm.ru/
 Профиль | | #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 ";DeleteFileA" (ByVal lpFileName As String) As Long
Private Sub Form_Load()
    'KPD-Team 1998
    'URL: http://www.allapi.net/
    'E-Mail: KPDTeam@Allapi.net
    ;Dim sSave As String, hOrgFile As Long, hNewFile As Long, bBytes() As Byte
    ;Dim 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
    ;DeleteFile "C:\KPDTEST.TST"
    Unload Me
End Sub
===

 Построчно не советую считывать такой большой файл. Это займет очень много времени. Хотя... делай как знаешь.

Ответить

Номер ответа: 5
Автор ответа:
 Fallout



Вопросов: 10
Ответов: 387
 Web-сайт: mudator.by.ru
 Профиль | | #5
Добавлено: 09.06.04 15:21
То что ты Dima34 создаёшь буфер в 570кб это не гуд... читать и писать лучше Апишками.... это раз во вторых файл большой нужно использовать ФайМапинг... а не ивзрщаться с буферами....

Ответить

Номер ответа: 6
Автор ответа:
 @CyRax PTR



ICQ: 204447456 

Вопросов: 28
Ответов: 664
 Web-сайт: basicproduction.nm.ru/
 Профиль | | #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-сайт: mudator.by.ru
 Профиль | | #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-сайт: mudator.by.ru
 Профиль | | #12
Добавлено: 09.06.04 23:41
Моя дурацкая мысль заключалась в том чтобы ... проверять на строку следующим образом...

наша стока... для поиска предположим 12121авпавпва1357

мы берём первый байт и последний... этой строки и соотвенно проверяем... +) то бишь если ... в буфере первый байт ... равен первому байту нашей строки то мы не лезем проверять все байты... строки .. а проверяем последний байт ... нашей строки и если он тоже совпал тогда проверяем уже всю строку +)

Ответить

Номер ответа: 13
Автор ответа:
 Sharp


Лидер форума

ICQ: 216865379 

Вопросов: 106
Ответов: 9979
 Web-сайт: sharpc.livejournal.com
 Профиль | | #13
Добавлено: 11.06.04 00:37
> Ты вот толдычишь API-API, а примера так и не предоставил. Покажи как делать маппинг.
В Iczelion Tutorial очень ясно и доходчиво написано, как делать маппинг. С ним должно быть заметно быстрее.

Ответить

Номер ответа: 14
Автор ответа:
 @CyRax PTR



ICQ: 204447456 

Вопросов: 28
Ответов: 664
 Web-сайт: basicproduction.nm.ru/
 Профиль | | #14
Добавлено: 12.06.04 00:38
>FileLen = LOF(1)
 Тут ошибка
Нужно LOF(Free_hFile)

У меня ещё была идея, но к сожалению почасовка кончилась.
Идея была такова:
 Заменить UBound(Split(Buffer, vbCrLf))
 На функцию, которая находит Instr'ом все VbCrLf в буфере. Этим ты убиваещь сразу 2-х зайцев: считаешь количество строк и находишь их позиции в файле. Занеся все позиции в массив, ты потом за доли секунды можешь прочесть любую строку в файле.

Ответить

Страница: 1 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам