Чтение большого текстового файла очень медленно - PullRequest
0 голосов
/ 28 ноября 2018

, поэтому мне дали задание написать программу vb, в которой я читаю в большом текстовом файле (где-то от 500 МБ до 2 ГБ), и этот файл обычно начинается с 13-значного числа, а затем загружается другая информация после каждой строки.(например, «1578597500548 info info info info и т. д.») Я должен позволить пользователю ввести 13-значное число, а затем моя программа выполнит поиск большого файла с этим номером в начале каждой строки, и, если он найден, запишите полную строку в новую.текстовый файл!

Моя текущая программа работает отлично, но я замечаю, что мое добавление в список list / streamreader занимает около 90% времени процесса.В среднем около 27 сек за пробег.Есть идеи как ускорить?Вот что я написал.

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim wtr As IO.StreamWriter
    Dim listy As New List(Of String)
    Dim i = 0

    stpw.Reset()
    stpw.Start()

    'reading in file of large data 700mb and larger
    Using Reader As New StreamReader("G:\USER\FOLDER\tester.txt")
        While Reader.EndOfStream = False
            listy.Add(Reader.ReadLine)
        End While
    End Using

    'have a textbox which finds user query number
    Dim result = From n In listy
                 Where n.StartsWith(TextBox1.Text)
                 Select n

    'writes results found into new file
    wtr = New StreamWriter("G:\USER\searched-number.txt")
    For Each word As String In result
        wtr.WriteLine(word)
    Next
    wtr.Close()

    stpw.Stop()
    Debug.WriteLine(stpw.Elapsed.TotalMilliseconds)

    Application.Exit()
End Sub

ОБНОВЛЕНИЕ Я принял предложение не помещать его в список первым и просто искать в памяти, времяпримерно на 5 секунд быстрее, все еще требуется 23 секунды для завершения, а также выписывает строку над цифрой, которую я ищу, поэтому, если вы могли бы сказать мне, где я иду не так.Спасибо, ребята!

wtr = New StreamWriter("G:\Karl\searchednumber.txt")
        Using Reader As New StreamReader("G:\Karl\AC\tester.txt")
            While Reader.EndOfStream = False
                lineIn = Reader.ReadLine
                If Reader.ReadLine.StartsWith(TextBox1.Text) Then
                    wtr.WriteLine(lineIn)

                Else

                    Continue While
                End If
            End While
            wtr.Close()
        End Using

1 Ответ

0 голосов
/ 28 ноября 2018

Индексируйте файл при загрузке программы.

Создайте Dictionary(Of ULong, Long), а когда программа загружается, прочитайте файл.Для каждой строки добавьте запись в словарь, показывающую 13-значное значение в начале каждой строки в качестве ключа ULong и позицию в потоке файла в качестве значения Long.

Затем, когда пользователь вводит ключ, вы можете проверить словарь, который будет почти экземпляром, и затем искать точное местоположение на диске, который вам нужен.

Создание индекса файла при запуске программы может занять несколько минут , но вам когда-либо придется это делать один раз .Прямо сейчас вам нужно либо искать по всему объекту каждый раз, когда пользователь хочет выполнить поиск, либо хранить в памяти несколько сотен мегабайт данных текстового файла.Когда у вас есть индекс, поиск значения в словаре и последующий поиск по нему должны казаться почти мгновенными.


Я только что увидел этот комментарий:

может быть более 1 вхождения из 13-значного числа, поэтому необходимо выполнить поиск по всему файлу.

Исходя из этого, индекс должен быть Dictionary(Of ULong, List(Of Long)), где при добавлении значения к записи сначала создается экземпляр списка, если он еще не существует, а затем добавляется новое значение в список.

Вот основная попытка, набранная непосредственно в окне ответа без помощи данных тестирования или Visual Studio, которая, вероятно, поэтому все еще содержит несколько ошибок:

Public Class MyFileIndexer
    Private initialCapacity As Integer = 1
    Private Property FilePath As String
    Private Index As Dictionary(Of ULong, List(Of Long))

    Public Sub New(filePath As String)
        Me.FilePath = filePath
        RebuildIndex()
    End Sub

    Public Sub RebuildIndex()
        Index = New Dictionary(Of ULong, List(Of Long))()

        Using sr As New StreamReader(FilePath)
            Dim Line As String = sr.ReadLine()
            Dim position As Long = 0
            While Line IsNot Nothing

                'Process this line
                If Line.Length > 13 Then
                   Dim key As ULong = ULong.Parse(Line.SubString(0, 13))
                   Dim item As List(Of Long)
                   If Not Index.TryGetValue(key, item) Then
                       item = New List(Of Long)(initialCapacity)
                       Index.Add(key, item)
                   End If

                   item.Add(position)
                End If

                'Prep for next line
                position = sr.BaseStream.Position
                Line = sr.ReadLine()
            End While
        End Using   
    End Sub

    'Expect key to be a 13-character numeric string
    Public Function Search(key As String) As List(Of String)
        'Will throw an exception if parsing fails. Be prepared for that.
        Dim realKey As ULong = ULong.Parse(key)
        Return Search(realKey)
    End Function

    Public Function Search(key As ULong) As List(Of String)
        Dim lines As List(Of Long)
        If Not Index.TryGetValue(key, lines) Then Return Nothing

        Dim result As New List(Of String)()
        Using sr As New StreamReader(FilePath)
            For Each position As Long In lines
                sr.BaseStream.Seek(position, SeekOrigin.Begin)
                result.Add(sr.ReadLine())
            Next position
        End Using
        Return Result
    End Function
End Class

'Somewhere public, when your application starts up:
Public Index As New MyFileIndexer("G:\USER\FOLDER\tester.txt")

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim lines As List(Of String) = Nothing
    Try
        lines = Index.Search(TextBox1.Text)
    Catch
        'Do something here
    End Try

    If lines IsNot Nothing Then
        Using sw As New StreamWriter($"G:\USER\{TextBox1.Text}.txt")
            For Each line As String in lines
                 sw.WriteLine(line)
            Next 
        End Using
    End If
End Sub

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

Public Class MyFileIndexer(Of TKey)
    Private initialCapacity As Integer = 1
    Private Property FilePath As String
    Private Index As Dictionary(Of TKey, List(Of Long))
    Private GetKey As Func(Of String, TKey) 

    Public Sub New(filePath As String, Func(Of String, TKey) keySelector)
        Me.FilePath = filePath
        Me.GetKey = keySelector
        RebuildIndex()
    End Sub

    Public Sub RebuildIndex()
        Index = New Dictionary(Of TKey, List(Of Long))()

        Using sr As New StreamReader(FilePath)
            Dim Line As String = sr.ReadLine()
            Dim position As Long = 0
            While Line IsNot Nothing

               Dim key As TKey = GetKey(Line)
               Dim item As List(Of Long)
               If Not Index.TryGetValue(key, item) Then
                   item = New List(Of Long)(initialCapacity)
                   Index.Add(key, item)
               End If   
               item.Add(position)

                'Prep for next line
                position = sr.BaseStream.Position
                Line = sr.ReadLine()
            End While
        End Using   
    End Sub

    Public Function Search(key As TKey) As List(Of String)
        Dim lines As List(Of Long)
        If Not Index.TryGetValue(key, lines) Then Return Nothing

        Dim result As New List(Of String)()
        Using sr As New StreamReader(FilePath)
            For Each position As Long In lines
                sr.BaseStream.Seek(position, SeekOrigin.Begin)
                result.Add(sr.ReadLine())
            Next position
        End Using
        Return Result
    End Function
End Class
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...