Оптимизация в реализации TSV парсера в ListView

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

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

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Оптимизация в реализации TSV парсера в ListView

Сообщение Admiralisimys » 03.06.2009 (Ср) 8:14

Доброго времени суток. Сам алгоритм TSV парсера в VB.NET я реализовал следующим кодом
Код: Выделить всё
Dim Line As String
Dim Tab As Integer = 0
Dim ColumItem As String
While (r.Peek() > -1)
    Line = r.ReadLine
    While Line.Length > 0
Tab = InStr(Line, vbTab)
If Tab <> 0 Then
    ColumItem = Microsoft.VisualBasic.Left(Line, Tab - 1)
    Line = Mid(Line, Tab + 1)
Else
    ColumItem = Line
    Line = Mid(Line, Line.Length + 1)
End If
Console.Write(Line & " ")
    End While
    Console.Writeline()
End While
r.Close()

Его результат решил собирать\выводить в ListView, вот тут и сказалась особенность последнего, так как в шапки колонок добавляются одним оператором, первые ячейки строк другим, ну и остальные ячейки - третим. В результате
Код: Выделить всё
Private Sub ReadTSV_(ByVal Path As String)

        Dim r As IO.StreamReader
        r = New IO.StreamReader(Path, System.Text.Encoding.Default)

        Dim Line As String = r.ReadLine
        Dim Tab As Integer = 0
        Dim ColumItem As String = ""

        While Line.Length > 0
            Tab = InStr(Line, vbTab)
            If Tab <> 0 Then
                ColumItem = Microsoft.VisualBasic.Left(Line, Tab - 1)
                Line = Mid(Line, Tab + 1)
            Else
                ColumItem = Line
                Line = Mid(Line, Line.Length + 1)
            End If
            ListView1.Columns.Add(ColumItem)
        End While

        Dim item1 As ListViewItem

        While (r.Peek() > -1)


            Line = r.ReadLine

            Tab = InStr(Line, vbTab)
            If Tab <> 0 Then
                ColumItem = Microsoft.VisualBasic.Left(Line, Tab - 1)
                Line = Mid(Line, Tab + 1)           'отбрасываем добавленный элемент
            Else
                ColumItem = Line
                Line = Mid(Line, Line.Length + 1)   'обнуляем длину строчки
            End If

            item1 = New ListViewItem(New String() {ColumItem})

            Dim Columns As Integer = 1

            If ListView1.Columns.Count < Columns Then
                ListView1.Columns.Add(vbNullChar)
            End If

            While Line.Length > 0

                Tab = InStr(Line, vbTab)
                If Tab <> 0 Then
                    ColumItem = Microsoft.VisualBasic.Left(Line, Tab - 1)
                    Line = Mid(Line, Tab + 1)           'отбрасываем добавленный элемент
                Else
                    ColumItem = Line
                    Line = Mid(Line, Line.Length + 1)   'обнуляем длину строчки
                End If

                item1.SubItems.Add(ColumItem)

                Columns = Columns + 1

                If ListView1.Columns.Count < Columns Then
                    ListView1.Columns.Add(vbNullChar)
                End If

            End While

            ListView1.Items.AddRange(New ListViewItem() {item1})
        End While

        r.Close()

    End Sub
трёх кратная избыточность. Вопрос заключается в следующем: как можно оформить сам парсер в виде отдельной подпрограммы, которая бы абстрагировалась от особенностей компонента ListView?

Спасибо за внимание.

MIT
Мега гуру
Мега гуру
Аватара пользователя
 
Сообщения: 2211
Зарегистрирован: 17.09.2006 (Вс) 22:46

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение MIT » 03.06.2009 (Ср) 8:37

Admiralisimys писал(а):Сам алгоритм TSV парсера в VB.NET я реализовал следующим кодом
Про String.Split слышал?

З.Ы. Выкинь фтопку Microsoft.VisualBasic.Left, Mid и прочий древний хлам. В .NET среде есть куда более хороший способ работы со строками - String.
Изображение
You can change your face, but can`t change your mind. No matter what you do.
Создайте еще более понятный интерфейс и мир создаст еще более тупого юзера. (с) Баш

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Williams » 03.06.2009 (Ср) 10:39

Есть Microsoft.VisualBasic.FileIO.TextFieldParser
И вы думаете, что вас оставят в живых после прочтения этого поста?

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Admiralisimys » 03.06.2009 (Ср) 11:12

MIT буду разбираться, как это поможет уменьшить избыточность с заполнением элемента ListView.
З.Ы. Я просто использовал знакомое ещё по VB6
Williams это я так понял совет по оптимизации самого парсера? Спасибо, попробую его использовать.

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Williams » 03.06.2009 (Ср) 11:43

Admiralisimys писал(а):Williams это я так понял совет по оптимизации самого парсера? Спасибо, попробую его использовать.

Не по оптимизации, а по замене. TextFieldParser работает несколько быстрее, чем ручная обработка.
И вы думаете, что вас оставят в живых после прочтения этого поста?

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Admiralisimys » 04.06.2009 (Чт) 8:57

Вот что получилось после использования FileIO.TextFieldParser рекомендованного Williams'ом и опираясь на статью How to: Read From Comma-Delimited Text Files in Visual Basic
Код: Выделить всё
    Private Sub ReadTSV__(ByVal Path As String)

        Dim MyReader As New FileIO.TextFieldParser(Path, System.Text.Encoding.Default)
        MyReader.TextFieldType = FileIO.FieldType.Delimited
        MyReader.SetDelimiters(vbTab)

        Dim currentRow As String() = MyReader.ReadFields()
        Dim currentField As String
        For Each currentField In currentRow
            ListView1.Columns.Add(currentField)
        Next

        While Not MyReader.EndOfData
            'Try
            currentRow = MyReader.ReadFields()
            Dim item1 As ListViewItem
            Dim First As Boolean = True
            Dim Columns As Integer = 0
            For Each currentField In currentRow
                If First Then
                    item1 = New ListViewItem(New String() {currentField})
                    First = False
                Else
                    item1.SubItems.Add(currentField)
                End If
                Columns += 1
                If ListView1.Columns.Count < Columns Then
                    ListView1.Columns.Add(vbNullChar)
                End If
            Next
            'Catch ex As  _
            'Microsoft.VisualBasic.FileIO.MalformedLineException()
            'MsgBox("Line " & ex.Message & _
            '"is not valid and will be skipped.")
            'End Try
            ListView1.Items.AddRange(New ListViewItem() {item1})
        End While

        MyReader.Close()

    End Sub

Конструкцию Try\Catch ex As\End Try пока закомментировал, что-то она мне напоминает VB6 On Error Resum Next, хоть она и нужна будет, на случай если попытаются открыть бинарный файл.
Данный код на одном и том же файле добавляет на одну колонку больше, нежели тот, что приведён ранее. Всё дело в том, что если табуляция последняя в строчки она не пройдёт проверку
Код: Выделить всё
Tab = InStr(Line, vbTab)
If Tab <> 0
впрочем, сохраняется в файл как раз с табуляцией перед концом строчки
Код: Выделить всё
    Private Sub SaveToFile()
        Dim w As IO.StreamWriter
        w = New IO.StreamWriter("test.txt")

        For Each Column As ColumnHeader In ListView1.Columns
            w.Write(Column.Text & vbTab)
        Next
        w.WriteLine()

        For Each Item As ListViewItem In ListView1.Items
            For Each SubItem As ListViewItem.ListViewSubItem In Item.SubItems
                w.Write(SubItem.Text & vbTab)
            Next
            w.WriteLine()
        Next
        w.Close()
    End Sub
и трагедии в этом не вижу.
Насчёт скорости то с помощью кода
Код: Выделить всё
Debug.Print(TimeOfDay.Second & " " & TimeOfDay.Millisecond.ToString)
ReadTSV__(OpenFileDialog1.FileName)
Debug.Print(TimeOfDay.Second & " " & TimeOfDay.Millisecond.ToString)
установил, что обе реализации выполняются за одно и тоже время, а именно за секунду (почему то миллисекунды не фиксировались и всё время показывали 0) на файлах с 585 строк X 7 табуляций и 180 строк Х 14 табуляций. Правда в случаи первого файла раз было, что ручной способ выполнился за 0 секунд, но это скорей всего погрешность (почему же миллисекунды не показались). Так что ручной способ тоже не промах, ещё и с иллюстрацией процесса, может сгодится для Vb6 варианта.
Народ, а как всё таки красиво заполнять ListView? Ведь сохранится с него вон как неплохо получается (см. код выше).
Кстати текущий вариант вернул меня к тому случаю, когда в коде
Код: Выделить всё
                If First Then
                    item1 = New ListViewItem(New String() {currentField})
                    First = False
                Else
                    item1.SubItems.Add(currentField)
                End If
подчёркивающий Warning после Else на item1, так как компилятору не ведомо, что по замыслу первый раз по любому выполнится If, после которого инициализируется item1, о чём он меня предупреждает Warning 1 Variable 'item1' is used before it has been assigned a value. A null reference exception could result at runtime.
P.S. Прогнал ещё раз тесты, похоже что ручной всё же быстрее, возможно правда по тому, что код ещё можно оптимизировать.

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Nord777 » 04.06.2009 (Чт) 15:58

подчёркивающий Warning после Else на item1,
А ты обмани.
Перед If First Then:
Dim item1 As ListViewItem = Nothing
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Admiralisimys » 05.06.2009 (Пт) 8:58

Nord777 точно! Сразу после currentRow = MyReader.ReadFields() объявлять с инициализацией Dim item1 As ListViewItem = Nothing
Спасибо.
Williams писал(а):TextFieldParser работает несколько быстрее, чем ручная обработка.

Williams тесты показали противоположное (см. вложение)
Tsv.zip
FileIO.TextFieldParser vs IO.StreamReader - Tsv
(7.3 Кб) Скачиваний: 45

В качестве TSV файл может выступать лог программы FileMon: или создать свой лог или взять для теста, например, найденный в Интернете http://www.badongo.com/cfile/4317024 (лог не мой)
.
Но все равно спасибо за TextFieldParser, получается более краткий код.

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Admiralisimys » 10.08.2009 (Пн) 13:08

Применил и совет MIT, результаты впечатляют
Код: Выделить всё
    Private Sub ReadTSV_Split(ByVal path As String)
        Dim r As New IO.StreamReader(path, System.Text.Encoding.Default)

        'Прописываем шапку
        Dim Line As String = r.ReadLine
        Dim ListViewColumnItem() As String = Line.Split(vbTab)
        For count As Integer = 0 To ListViewColumnItem.Length - 1
            ListView1.Columns.Add(ListViewColumnItem(count))
        Next

        'Прописываем строчки: сначала задаём первый элемент, а потом все остальные, если они существуют
        While (r.Peek() > -1)
            Line = r.ReadLine
            ListViewColumnItem = Line.Split(vbTab)

            Dim item1 As ListViewItem = New ListViewItem(New String() {ListViewColumnItem(0)})
            'Выравниваем шапку пустым элементом, если количество элементов в строке превысило количество в шапке
            Dim Columns As Integer = 1
            If ListView1.Columns.Count < Columns Then
                ListView1.Columns.Add(vbNullChar)
            End If

            If ListViewColumnItem.Length > 1 Then
                For count As Integer = 1 To ListViewColumnItem.Length - 1
                    item1.SubItems.Add(ListViewColumnItem(count))
                    Columns = Columns + 1
                    If ListView1.Columns.Count < Columns Then
                        ListView1.Columns.Add(vbNullChar)
                    End If
                Next
            End If
            ListView1.Items.AddRange(New ListViewItem() {item1})
        End While
        r.Close()
    End Sub

По времени выполнения (скорости) обходит два предыдущих кода IO.StreamReader + цикл где выискивались табуляций и FileIO.TextFieldParser.
Спасибо MIT, Williams, Nord777 я узнал много нового.

А как насчёт заполнения элемента ListView? Данный код оптимален? Или есть ещё что-то более оптимальное в заполнении?
Может посоветуете иной визуальный элемент?
Программа должна быть неким подобием ЕКСЕЛя - просмотрищик (при возможности и редактор) для табулированных ТХТ файлов, с поиском и выделением по ячейкам.

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Nord777 » 10.08.2009 (Пн) 17:04

Может посоветуете иной визуальный элемент?
Например DataGridView
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

iGrok
Артефакт VBStreets
Артефакт VBStreets
 
Сообщения: 4272
Зарегистрирован: 10.05.2007 (Чт) 16:11
Откуда: Сетевое сознание

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение iGrok » 10.08.2009 (Пн) 17:05

Admiralisimys писал(а):Программа должна быть неким подобием ЕКСЕЛя - просмотрищик (при возможности и редактор) для табулированных ТХТ файлов, с поиском и выделением по ячейкам.


Ммм.. Ну в теории, можно текстовый таблулированный файл вообще в рекордсет поднимать, и работать с ним, как с БД. Отображать при этом хоть вообще в DataGrid'е. Не знаю, правда, эффективнее ли это будет и насколько.
label:
cli
jmp label

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Оптимизация в реализации TSV парсера в ListView

Сообщение Nord777 » 10.08.2009 (Пн) 17:17

Ммм.. Ну в теории, можно текстовый таблулированный файл вообще в рекордсет поднимать, и работать с ним, как с БД
Несколько проще...
Код: Выделить всё
Public Class Form1
   Private TabbedStriings As List(Of OneString)


   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      Dim DGV As New DataGridView
      DGV.SetBounds(10, 10, 300, 100, BoundsSpecified.All)
      Me.Controls.Add(DGV)

      TabbedStriings = New List(Of OneString)
      TabbedStriings.Add(New OneString("Row1_Col1", "Row1_Col2"))
      TabbedStriings.Add(New OneString("Row2_Col1", "Row2_Col2"))
      TabbedStriings.Add(New OneString("Row3_Col1", "Row3_Col2"))

      DGV.DataSource = TabbedStriings
   End Sub

   Private Class OneString
      Private _Column1 As String
      Private _Column2 As String

      Public Sub New(ByVal Col1 As String, ByVal Col2 As String)
         _Column1 = Col1
         _Column2 = Col2
      End Sub

      Public Property Column1() As String
         Get
            Return _Column1
         End Get

         Set(ByVal Value As String)
            _Column1 = Value
         End Set
      End Property

      Public Property Column2() As String
         Get
            Return _Column2
         End Get
         Set(ByVal Value As String)
            _Column2 = Value
         End Set
      End Property

   End Class

End Class
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5


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

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

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

    TopList