Скорость чтения из файла и разбор строк

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

twinzco
Начинающий
Начинающий
 
Сообщения: 23
Зарегистрирован: 22.05.2006 (Пн) 23:36

Скорость чтения из файла и разбор строк

Сообщение twinzco » 04.06.2006 (Вс) 1:18

Пишу проект на VB.NET, встала следующая задача: мне нужно в массив прочитать значения из большого файла (~16 Mb). Этот код работает примерно в 2 раза быстрее:
Код: Выделить всё
Dim objStreamReader As StreamReader
        Dim str, line() As String
        Dim enter As String = Chr(13) + Chr(10)
        Dim probel As String = " "
        Dim sps() As Char = {" "}
        objStreamReader = New StreamReader(dlg.FileName)
        str = objStreamReader.ReadToEnd
        objStreamReader.Close()
        str = str.Replace(enter, " ")
        str = str.Replace(".", ",")
        line = str.Split(sps)
        str = Nothing
        nx = CInt(line(1))
        ny = CInt(line(2))
        xmin = CDbl(line(3))
        xmax = CDbl(line(4))
        ymin = CDbl(line(5))
        ymax = CDbl(line(6))
        zmin = CDbl(line(7))
        zmax = CDbl(line(8))
        k = 9
        ReDim data(nx, ny)
        For j = 0 To ny - 1
            For i = 0 To nx - 1
                If line(k) <> "" Then
                    data(i, j) = CDbl(line(k))
                    k = k + 1
                Else
                    k = k + 1
                    data(i, j) = CDbl(line(k))
                    k = k + 1
                End If
            Next i
            Pbar.PerformStep()
        Next j
        line = Nothing

чем следующий код:
Код: Выделить всё

FileOpen(1, dlg.FileName, OpenMode.Input, OpenAccess.Read, OpenShare.Default)
        Xtxt.Text = Now
        s = vb.LineInput(1)
        vb.Input(1, nx)
        vb.Input(1, ny)
        Pbar.Maximum = ny - 1
        Pbar.Step = 1
        lblout.Text = "Открываю файл..."
        vb.Input(1, xmin)
        vb.Input(1, xmax)
        vb.Input(1, ymin)
        vb.Input(1, ymax)
        vb.Input(1, zmin)
        vb.Input(1, zmax)
        ReDim data(nx, ny)
        For j = 0 To ny - 1
            For i = 0 To nx - 1
                vb.Input(1, data(i, j))
            Next i
            Pbar.PerformStep()
        Next j
        FileClose(1)


Как еще можно ускорить этот процесс, подскажите пожалуйста. Если делать разбор строк, там у меня получается еще хуже. А файл, который нужно считать может быть еще большего размера (до 1 Gb).

twinzco
Начинающий
Начинающий
 
Сообщения: 23
Зарегистрирован: 22.05.2006 (Пн) 23:36

Сообщение twinzco » 04.06.2006 (Вс) 17:10

Как я полагал с разбором строк, вышло быстрее. Проблема была с функцией val(), слишком медленная. Этот вариант работает еще быстрее:
Код: Выделить всё

Dim objStreamReader As StreamReader
        Dim str As String
        Dim pos, oldpos As Integer
        Dim enter As String = Chr(13) + Chr(10)
        Dim chars() As Char = {ControlChars.Cr, ControlChars.CrLf, ControlChars.Lf}
        Dim probel As String = " "
        objStreamReader = New StreamReader(dlg.FileName)
        str = objStreamReader.ReadToEnd
        objStreamReader.Close()
        pos = InStr(str, enter) ' нашли первый enter
        s = Mid(str, 1, pos - 1)   'Input(1, s)
        str = str.Remove(0, pos + 1)
        oldpos = pos ' сохранили позицию первого ентера
        pos = InStr(1, str, enter) ' нашли следующий
        s = Mid(str, 1, pos - 1) ' вырезали
        oldpos = pos ' сохранили позицию второго ентера
        pos = InStr(s, probel)
        nx = CInt(Mid(s, 1, pos)) 'Input(1, nx)
        ny = CInt(Mid(s, pos + 1)) 'Input(1, ny)
        Pbar.Maximum = ny - 1
        Pbar.Step = 1
        lblout.Text = "Открываю файл..."
        pos = oldpos
        str = str.Remove(0, pos + 1)
        pos = InStr(1, str, enter) ' нашли следующий
        s = Mid(str, 1, pos - 1) ' вырезали
        oldpos = pos ' сохранили позицию второго ентера
        pos = InStr(s, probel)
        xmin = Val(Mid(s, 1, pos)) 'Input(1, xmin)
        xmax = Val(Mid(s, pos + 1)) 'Input(1, xmax)
        pos = oldpos
        str = str.Remove(0, pos + 1)
        pos = InStr(1, str, enter) ' нашли следующий
        s = Mid(str, 1, pos - 1) ' вырезали
        oldpos = pos ' сохранили позицию второго ентера
        pos = InStr(s, probel)
        ymin = Val(Mid(s, 1, pos)) 'Input(1, ymin)
        ymax = Val(Mid(s, pos + 1)) 'Input(1, ymax)
        pos = oldpos
        str = str.Remove(0, pos + 1)
        pos = InStr(1, str, enter) ' нашли следующий
        s = Mid(str, 1, pos - 1) ' вырезали
        oldpos = pos ' сохранили позицию второго ентера
        pos = InStr(s, probel)
        zmin = Val(Mid(s, 1, pos)) 'Input(1, zmin)
        zmax = Val(Mid(s, pos + 1)) 'Input(1, zmax)
        pos = oldpos
        str = str.Remove(0, pos + 1)
        str = str.Replace(enter, " ")
        str = str.Replace(".", ",")
        str = str.Trim
        oldpos = 1
        ReDim data(nx, ny)
        For j = 0 To ny - 1
            For i = 0 To nx - 1
                pos = InStr(oldpos, str, probel)
                If pos <> 0 Then
                    s = (Mid(str, oldpos, pos - oldpos))
                    data(i, j) = CDbl(s)
                    oldpos = pos + 1
                Else
                    s = (Mid(str, oldpos))
                    data(i, j) = CDbl(s)
                End If
                ' Input(1, data(i, j))
            Next i
            Pbar.PerformStep()
        Next j

Но все равно, любые идеи по оптимизации - welcome. Кроме того остается вопрос про разделители десятичной части, как бы сделать быстро и независимо от установок (запятая или точка).
Последний раз редактировалось twinzco 04.06.2006 (Вс) 19:17, всего редактировалось 1 раз.

GAGArin
Неистовый флудер
Неистовый флудер
 
Сообщения: 1777
Зарегистрирован: 23.12.2002 (Пн) 12:46
Откуда: я тут взялся, не знаю...

Сообщение GAGArin » 04.06.2006 (Вс) 17:29

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

Функции преобразования типов в MSDN очень подробно расписаны. Про скорость работы там тоже есть (помню что читал что-то такое, не помню где) Это на счет Val

twinzco
Начинающий
Начинающий
 
Сообщения: 23
Зарегистрирован: 22.05.2006 (Пн) 23:36

Сообщение twinzco » 04.06.2006 (Вс) 19:15

Фишка в том, что если использовать CDbl, то вызов превращается в инлайн функцию, а val - переопределена, поэтому CDbl быстрее. Но есть тонкое место, которое мне и не понятно, CDbl не понимает значение вида "321.567", ему нужно - "321,567", поэтому приходится делать затратную операцию по замене точек на запятые. Есть идеи как это обойти?

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 04.06.2006 (Вс) 22:35

Вообще-то это зависит от региональных настроек пользователя. Если стоит в качестве десятичного разделителя ",", то будет восприниматься ",".
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

twinzco
Начинающий
Начинающий
 
Сообщения: 23
Зарегистрирован: 22.05.2006 (Пн) 23:36

Сообщение twinzco » 05.06.2006 (Пн) 5:37

Это понятно, я и спрашиваю: как решить эту проблему?

twinzco
Начинающий
Начинающий
 
Сообщения: 23
Зарегистрирован: 22.05.2006 (Пн) 23:36

Сообщение twinzco » 05.06.2006 (Пн) 23:34

После сообщения многоуважаемого GAGArin, я вернулся обратно к сплиту :D и получилось следующее, если не грузить весь файл в память (хотя я где-то читал, что это самый быстрый способ, м.б. codeguru.com), то все получается в лет! Для сравнения в предыдущем посте код считывает и парсит файл в ~60 Mb примерно за 1 мин. 10 сек. А вот этот код всего за 17 сек:
Код: Выделить всё
Dim objStreamReader As StreamReader
        Dim str, line() As String
        Dim sps As Char = " "
        objStreamReader = New StreamReader(dlg.FileName)
        Pbar.Value = 0
        str = objStreamReader.ReadLine
        str = objStreamReader.ReadLine
        line = str.Split(sps)
        line(0) = line(0).Replace(".", ",")
        line(1) = line(1).Replace(".", ",")
        nx = line(0)
        ny = line(1)
        str = objStreamReader.ReadLine
        line = str.Split(sps)
        line(0) = line(0).Replace(".", ",")
        line(1) = line(1).Replace(".", ",")
        xmin = line(0)
        xmax = line(1)
        str = objStreamReader.ReadLine
        line = str.Split(sps)
        line(0) = line(0).Replace(".", ",")
        line(1) = line(1).Replace(".", ",")
        ymin = line(0)
        ymax = line(1)
        str = objStreamReader.ReadLine
        line = str.Split(sps)
        line(0) = line(0).Replace(".", ",")
        line(1) = line(1).Replace(".", ",")
        zmin = line(0)
        zmax = line(1)
        Pbar.Maximum = ny
        ReDim data(nx, ny)
        For j = 0 To ny - 1
            str = objStreamReader.ReadLine
            line = str.Split(sps)
            For i = 0 To nx - 1
                line(i) = line(i).Replace(".", ",")
                data(i, j) = CDbl(line(i))
            Next i
            Pbar.PerformStep()
        Next j
        objStreamReader.Close()

Но пока остается вопрос о запятой в качестве разделителя, я же не знаю какие установки на компе у конечного пользователя. Пытался прикрутить возможности globalization, но пока не вышло.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Сообщение Viper » 06.06.2006 (Вт) 7:40

До настроек пользователя добраться весьма просто:

Dim sDecimalPoint As String = Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator

И ты увидишь то что юзает юзер вместо десятичной точки.
Весь мир матрица, а мы в нем потоки байтов!

twinzco
Начинающий
Начинающий
 
Сообщения: 23
Зарегистрирован: 22.05.2006 (Пн) 23:36

Сообщение twinzco » 06.06.2006 (Вт) 10:34

!Viper!, согласен, добраться просто. Мне пока не ясно, как это использовать практически, значение там readonly. Или без всяких затратных преобразований из строки прочесть double, неважно точка или запятая является разделителем. Если с помощью val, то слишком долго получается. Если CDbl - он зависит от региональных настроек, а работает очень быстро. Поэтому приходится менять в строке шило на мыло, в смысле точку на запятую, а для файла например в 300 Mb это уже чувствительное время. :(


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 109

    TopList