VB.NET Исключение индекса вне диапазона, связанное с текстовым файлом

#vb.net #indexoutofrangeexception

Вопрос:

У меня есть код, который я использовал много раз, и который всегда отлично работал для меня. Однако последнее использование вызывает исключение при определенных обстоятельствах, которое я, похоже, не могу разрешить. Вот он:

Я читаю из текстового файла в массив, использую его в качестве источника привязки для некоторых моих элементов управления (он автоматически заполняет 3 элемента управления на основе выбора одного элемента управления). Я создал класс учащихся с 4 свойствами (имя, идентификатор, DOB и DOE). Вот код, который я использую:

 Private Sub autoFill()
        Dim rost As String = "Roster.txt"
        Dim lines As List(Of String) = File.ReadAllLines(rost).ToList
        Dim list As List(Of Student) = New List(Of Student)
        For i As Integer = 0 To lines.Count - 1
            Dim data As String() = lines(i).Split(":")
            list.Add(New Student() With {
                     .StudentName = data(0),
                     .StudentID = data(1),
                     .StudentDOB = data(2),
                     .StudentDOE = data(3)
                                        })
        Next
        StudentBindingSource.DataSource = list


    End Sub
 

Теперь вот в чем проблема. В цикле «Для», когда я устанавливаю значение i от 0 до строк.количество -1, возникает эта ошибка:

VB>ЧИСТОЕ ИСКЛЮЧЕНИЕ

Однако…Если я изменю i на 1 вместо 0, это сработает, ИЛИ если я уберу данные(2) и данные(3), это сработает с i = 0. Я бы предпочел использовать 0, чтобы в выпадающем списке была пустая строка или «—выберите—» и т. Д. Единственное, что я подумал, что может быть полезно, — это то, что моей первой строке в текстовом файле нечего делить. Вот формат строки текстового файла:

 Student Name         ID#        DOB          DOE      <-----This header row is NOT in the text file
Last Name, First Name : 0000000 : 01/01/2021 : 01/01/2021
 

Я собираюсь предположить, что мне здесь не хватает чего-то действительно простого. Любые рекомендации будут весьма признательны! Спасибо.

Комментарии:

1. Тогда не анализируйте эту строку, так как она не дает ожидаемых результатов (почему это не так, по — видимому, загадка -у вас есть заголовок? Не имеет значения). Если вам нужно добавить пустой или специальный объект в первый индекс, просто сделайте это. Вы также можете показать баннер Cue в своем выпадающем списке вместо добавления нулевого элемента ( <choose one> материала). — Вы думали об использовании базы данных для этого?

2. If data.Length = 4 Then list.Add(New Student() With {.... End If проверяет, есть ли ровно четыре элемента.

3. Я бы предположил, что там есть пустая строка. В любом случае попробуйте изучить/распечатать строки до их анализа (или после сбоя с помощью отладчика). Что-то не так с линией, которая выходит из строя, из-за недостаточного количества двоеточий.

4. Спасибо всем за помощь. Это всего лишь небольшое приложение, которое я использую только на работе, чтобы ускорить свой рабочий процесс. Текстовый файл никогда не будет содержать более 100 строк, и он не сильно изменится, поэтому текстовый файл отлично подходит для моей цели. как оказалось, добавление 4 элементов в первую строку позволило мне начать с индекса 0 и по-прежнему использовать только позицию 0 в строке).

Ответ №1:

Прежде чем мы перейдем к реальной проблеме, давайте кое-что переделаем.

Лучший способ структурировать код, особенно при работе с загрузкой данных, — это иметь метод, который принимает входные данные и возвращает результат. Кроме того, вызов ToList() или ToArray() является очень дорогостоящей операцией для производительности. Очень часто вы можете значительно повысить производительность, работая с более низким уровнем IEnumerable как можно дольше.

Имея в виду эти принципы, рассмотрите этот кодекс:

 Private Function ReadStudentData(fileName As String) As IEnumerable(Of Student)
    Dim lines As IEnumerable(Of String) = File.ReadLines(fileName)

    Return lines.
        Select(Function(line) line.Split(":")).
        Select(Function(data)
            Return New Student() With {
                .StudentName = data(0),
                .StudentID = data(1),
                .StudentDOB = data(2),
                .StudentDOE = data(3)
            }
        End Function)
End Function

Private Sub autoFill()
    StudentBindingSource.DataSource = ReadStudentData("Roster.txt")
End Sub
 

Теперь перейдем к актуальному вопросу. Проблема заключалась не в циклическом переборе list переменной. Проблема в data массиве. В какой-то момент у вас есть строка, в которой недостаточно элементов. Это часто встречается, например, как последняя строка в файле.

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

 Private Function ReadStudentData(fileName As String) As IEnumerable(Of Student)
    Return File.ReadLines(fileName).
        Select(Function(line) line.Split(":")).
        Where(Function(data) data.Length = 4).
        Select(Function(data) 
            Return New Student() With {
                .StudentName = data(0),
                .StudentID = data(1),
                .StudentDOB = data(2),
                .StudentDOE = data(3)
            }
        End Function)
End Function

Private Sub autoFill()
    StudentBindingSource.DataSource = ReadStudentData("Roster.txt")
End Sub
 

Комментарии:

1. Это было не только отличным решением для меня, но и исчерпывающим объяснением, которое позволило мне кое-чему научиться! Спасибо!

2. С другой стороны, запуск этого маленького приложения был ПОЛНОЙ потерей ресурсов, и теперь я знаю, почему!

Ответ №2:

Проблема в том, что вы не проверили «данные» на наличие достаточного количества элементов для создания «Ученика». Простая проверка должна это исправить.

 Private Sub autoFill()
    Dim rost As String = "Roster.txt"
    Dim lines As List(Of String) = File.ReadAllLines(rost).ToList
    Dim list As List(Of Student) = New List(Of Student)
    For i As Integer = 0 To lines.Count - 1
        Dim data As String() = lines(i).Split(":"c)
        'Check data
        If data.Length >= 4 Then '
            list.Add(New Student() With {
                     .StudentName = data(0),
                     .StudentID = data(1),
                     .StudentDOB = data(2),
                     .StudentDOE = data(3)
                                        })
        End If
    Next
    StudentBindingSource.DataSource = list
End Sub
 

Комментарии:

1. И в этом-то как раз и была проблема, спасибо!

Ответ №3:

попробуйте этот код:

 Dim list As List(Of Student) = New List(Of Student)(100)
 

в основном инициализируйте список учащихся с помощью емкости. Это емкость списка, а не количество/длина.