使用ArrayList将文本文件分割并存储到数组中

时间:2022-01-18 15:40:23

I have been developing a quiz application that uses a textfile to store questions.

我一直在开发一个测试应用程序,它使用一个文本文件来存储问题。

The questions are formatted in this format "QUESTION##CHOICE_A##CHOICE_B##CHOICE_C##CHOICE_D##ANSWER" I want it to read each line splits it into 6 different parts using "##" as the split string and store it into arrays such as Questions, CHOICE_A,CHOICE_B,CHOICE_C,CHOICE_D

这些问题的格式都是这样的:“我想让它读每一行,把它分成6个不同的部分,用“##”作为分裂的字符串,然后把它存储到数组中,比如问题,CHOICE_A,CHOICE_B,CHOICE_C,CHOICE_D。

My code does not loop. it just stores the first line

我的代码不循环。它只存储第一行

A graphical illustration is shown below of the questions

问题的图示如下

使用ArrayList将文本文件分割并存储到数组中使用ArrayList将文本文件分割并存储到数组中

Here is my code

这是我的代码

Dim sr As StringReader = New StringReader(My.Resources.ResourceManager.GetObject(globalVariables.currSubject))

    Dim questions As String

    Dim splitquestions(6) As String

    Dim Unsplitquestions(6) As String




    Dim i As Integer = 0

    Do Until sr.Peek = -1

        questions = sr.ReadLine

        Unsplitquestions(i) = questions

        splitquestions = Unsplitquestions(i).Split(New String() {"##"}, StringSplitOptions.RemoveEmptyEntries)

        ' Splits and Stores Into Various 
        '
        '


        globalVariables.ArrayQuestions.Add(splitquestions(0))

        globalVariables.optionA.Add(splitquestions(1))

        globalVariables.optionB.Add(splitquestions(2))

        globalVariables.optionC.Add(splitquestions(3))

        globalVariables.optionD.Add(splitquestions(4))

        globalVariables.Answer.Add(splitquestions(5))

    Loop

2 个解决方案

#1


3  

No, do not use an ArrayList for manipulating a set of objects like this. You should try to think in an Object Oriented way. A QuestionEntry is an entity that contains a QuestionText, 4 possible question answers and one QuestionAnswer.

不,不要使用ArrayList操作这样的一组对象。你应该试着以面向对象的方式思考。一个问题条目是包含一个问题文本、四个可能的问题答案和一个问题答案的实体。

So let's try this code

让我们试试这个代码

Public Class QuestionEntry
   Public QuestionText as String
   Public ChoiceA as String
   Public ChoiceB as String
   Public ChoiceC as String
   Public ChoiceD as String
   Public QuestionAnswer as String
End Class

Dim questions = new List(Of QuestionEntry)()
Dim line As String
Do While sr.Peek() >= 0
    line = sr.ReadLine
    Console.WriteLine(line)
    Dim parts = line.Split(New String() {"##"}, StringSplitOptions.RemoveEmptyEntries)
    Dim q = new QuestionEntry()
    With q

            .QuestionText = parts(0)
            .ChoiceA = parts(1)
            .ChoiceB = parts(2)
            .ChoiceC = parts(3)
            .ChoiceD = parts(4)
            .QuestionAnswer = parts(5)

    End With
    questions.Add(q)
Loop

Of course this is just an example and a bit of error checking will be required to make the code more safe. For example before creating the new question entry you should check if the array returned by the split has effectively 6 elements. (parts.Length = 6)

当然,这只是一个示例,为了使代码更安全,需要进行一些错误检查。例如,在创建新的问题条目之前,您应该检查split返回的数组是否具有有效的6个元素。(部分。长度= 6)

Now all of your text is handled by an instance of a List(Of QuestionEntry) and you could use it like a normal array

现在,所有文本都由一个列表(QuestionEntry)的实例处理,您可以像使用普通数组一样使用它

Dim qe = questions(0)
Console.WriteLine("Question: " & qe.QuestionText)
Console.WriteLine("Choice A: " & qe.ChoiceA)
Console.WriteLine("Choice B: " & qe.ChoiceB)
Console.WriteLine("Choice C: " & qe.ChoiceC)
Console.WriteLine("Choice D: " & qe.ChoiceD)
Console.ReadLine("Enter your answer:")

#2


1  

The best way to do this is to rely on an existing delimited data parser. The .Split() method is very often horrible for this: performance is sub-par, and there are all kings of edge cases (more than you'd think) where it just doesn't work well. There is even a parser already built into .Net: Microsoft.VisualBasic.FileIO.TextFieldParser.

最好的方法是依赖现有的分隔数据解析器。split()方法在这一点上通常很糟糕:性能低于平均水平,并且有很多边缘情况(比您想象的要多),在这些情况下它不能很好地工作。net中甚至已经内置了一个解析器:Microsoft.VisualBasic.FileIO.TextFieldParser。

Additionally, ArrayLists really only exist for pre-.Net 2.0 compatibility. There's is no good reason to ever use one any more. At very least, use a generic List(Of String). In this case, though, your best option is to build a quick class:

此外,ArrayLists真的只存在于pre-。Net 2.0兼容性。没有理由再使用它了。至少,使用一个泛型列表(字符串)。在这种情况下,你最好的选择是创建一个快速类:

Public Class Question
    Public Property QuestionText As String
    Public Property OptionA As String
    Public Property OptionB As String
    Public Property OptionC As String
    Public Property OptionD As String
    Public Property Answer As String
End Class

Now you read your file like this:

现在你这样阅读你的文件:

Dim results As New List(Of Question)()
Using rdr As New TextFieldParser(My.Resources.ResourceManager.GetObject(globalVariables.currSubject))
    rdr.Delimiters = new String() {"##"}
    Dim row() As String

    While Not rdr.EndOfData
        row = rdr.ReadFields()
        results.Add(New Question() {
             QuestionText = row(0), 
             OptionA = row(1), 
             OptionB = row(2), 
             OptionC = row(3),
             OptionD = row(4), 
             Answer = row(5)
         })
    End While
End Using

Even with the class definition, that's a whole let less code than the original, and it's much easier to maintain, as well.

即使有了类定义,也比原来的代码要少,而且维护起来也要容易得多。

I'd also be tempted to write this as an Iterator:

我也想把它写成迭代器:

Public Iterator Function ReadQuestions(ByVal FileName As String) As IEnumerable(Of Question)
    Using rdr As New TextFieldParser(FileName)
        rdr.Delimiters = new String() {"##"}
        Dim row() As String

        While Not rdr.EndOfData
            row = rdr.ReadFields()

            Yield New Question() {
                QuestionText = row(0), 
                OptionA = row(1), 
                OptionB = row(2), 
                OptionC = row(3),
                OptionD = row(4), 
                Answer = row(5)
            }
        End While
    End Using
End Function

I have two final changes to suggest. The first to add a constructor to the Question type that accepts a string array. This would remove one bit of advanced syntax (the object initializer) from the code, and simplify reading through the portion of the code that actually reads the data. The second isto make the ReadQuestions() method a shared member of the Question type. The final result is this:

我有两个建议。第一个向接受字符串数组的问题类型添加构造函数。这将从代码中删除一点高级语法(对象初始化器),并简化对实际读取数据的代码部分的读取。第二个是使ReadQuestions()方法成为问题类型的共享成员。最后的结果是:

Public Class Question
    Public Property QuestionText As String
    Public Property OptionA As String
    Public Property OptionB As String
    Public Property OptionC As String
    Public Property OptionD As String
    Public Property Answer As String

    Public Sub New(ByVal data() As String)
        'Add error handling here
        QuestionText = data(0), 
        OptionA = data(1), 
        OptionB = data(2), 
        OptionC = data(3),
        OptionD = data(4), 
        Answer = data(5)
    End Sub

    Public Shared Iterator Function ReadFromFile(ByVal FileName As String) As IEnumerable(Of Question)
        Using rdr As New TextFieldParser(FileName)
            rdr.Delimiters = new String() {"##"}

            While Not rdr.EndOfData                                
                Yield New Question(rdr.ReadFields()) 
            End While
        End Using
    End Function
End Class

And you call all this from your existing code like so:

你从现有代码中调用所有这些

Dim Questions = Question.ReadFromFile(My.Resources.ResourceManager.GetObject(globalVariables.currSubject))
For Each q As Question in Questions
    '...
Next 

#1


3  

No, do not use an ArrayList for manipulating a set of objects like this. You should try to think in an Object Oriented way. A QuestionEntry is an entity that contains a QuestionText, 4 possible question answers and one QuestionAnswer.

不,不要使用ArrayList操作这样的一组对象。你应该试着以面向对象的方式思考。一个问题条目是包含一个问题文本、四个可能的问题答案和一个问题答案的实体。

So let's try this code

让我们试试这个代码

Public Class QuestionEntry
   Public QuestionText as String
   Public ChoiceA as String
   Public ChoiceB as String
   Public ChoiceC as String
   Public ChoiceD as String
   Public QuestionAnswer as String
End Class

Dim questions = new List(Of QuestionEntry)()
Dim line As String
Do While sr.Peek() >= 0
    line = sr.ReadLine
    Console.WriteLine(line)
    Dim parts = line.Split(New String() {"##"}, StringSplitOptions.RemoveEmptyEntries)
    Dim q = new QuestionEntry()
    With q

            .QuestionText = parts(0)
            .ChoiceA = parts(1)
            .ChoiceB = parts(2)
            .ChoiceC = parts(3)
            .ChoiceD = parts(4)
            .QuestionAnswer = parts(5)

    End With
    questions.Add(q)
Loop

Of course this is just an example and a bit of error checking will be required to make the code more safe. For example before creating the new question entry you should check if the array returned by the split has effectively 6 elements. (parts.Length = 6)

当然,这只是一个示例,为了使代码更安全,需要进行一些错误检查。例如,在创建新的问题条目之前,您应该检查split返回的数组是否具有有效的6个元素。(部分。长度= 6)

Now all of your text is handled by an instance of a List(Of QuestionEntry) and you could use it like a normal array

现在,所有文本都由一个列表(QuestionEntry)的实例处理,您可以像使用普通数组一样使用它

Dim qe = questions(0)
Console.WriteLine("Question: " & qe.QuestionText)
Console.WriteLine("Choice A: " & qe.ChoiceA)
Console.WriteLine("Choice B: " & qe.ChoiceB)
Console.WriteLine("Choice C: " & qe.ChoiceC)
Console.WriteLine("Choice D: " & qe.ChoiceD)
Console.ReadLine("Enter your answer:")

#2


1  

The best way to do this is to rely on an existing delimited data parser. The .Split() method is very often horrible for this: performance is sub-par, and there are all kings of edge cases (more than you'd think) where it just doesn't work well. There is even a parser already built into .Net: Microsoft.VisualBasic.FileIO.TextFieldParser.

最好的方法是依赖现有的分隔数据解析器。split()方法在这一点上通常很糟糕:性能低于平均水平,并且有很多边缘情况(比您想象的要多),在这些情况下它不能很好地工作。net中甚至已经内置了一个解析器:Microsoft.VisualBasic.FileIO.TextFieldParser。

Additionally, ArrayLists really only exist for pre-.Net 2.0 compatibility. There's is no good reason to ever use one any more. At very least, use a generic List(Of String). In this case, though, your best option is to build a quick class:

此外,ArrayLists真的只存在于pre-。Net 2.0兼容性。没有理由再使用它了。至少,使用一个泛型列表(字符串)。在这种情况下,你最好的选择是创建一个快速类:

Public Class Question
    Public Property QuestionText As String
    Public Property OptionA As String
    Public Property OptionB As String
    Public Property OptionC As String
    Public Property OptionD As String
    Public Property Answer As String
End Class

Now you read your file like this:

现在你这样阅读你的文件:

Dim results As New List(Of Question)()
Using rdr As New TextFieldParser(My.Resources.ResourceManager.GetObject(globalVariables.currSubject))
    rdr.Delimiters = new String() {"##"}
    Dim row() As String

    While Not rdr.EndOfData
        row = rdr.ReadFields()
        results.Add(New Question() {
             QuestionText = row(0), 
             OptionA = row(1), 
             OptionB = row(2), 
             OptionC = row(3),
             OptionD = row(4), 
             Answer = row(5)
         })
    End While
End Using

Even with the class definition, that's a whole let less code than the original, and it's much easier to maintain, as well.

即使有了类定义,也比原来的代码要少,而且维护起来也要容易得多。

I'd also be tempted to write this as an Iterator:

我也想把它写成迭代器:

Public Iterator Function ReadQuestions(ByVal FileName As String) As IEnumerable(Of Question)
    Using rdr As New TextFieldParser(FileName)
        rdr.Delimiters = new String() {"##"}
        Dim row() As String

        While Not rdr.EndOfData
            row = rdr.ReadFields()

            Yield New Question() {
                QuestionText = row(0), 
                OptionA = row(1), 
                OptionB = row(2), 
                OptionC = row(3),
                OptionD = row(4), 
                Answer = row(5)
            }
        End While
    End Using
End Function

I have two final changes to suggest. The first to add a constructor to the Question type that accepts a string array. This would remove one bit of advanced syntax (the object initializer) from the code, and simplify reading through the portion of the code that actually reads the data. The second isto make the ReadQuestions() method a shared member of the Question type. The final result is this:

我有两个建议。第一个向接受字符串数组的问题类型添加构造函数。这将从代码中删除一点高级语法(对象初始化器),并简化对实际读取数据的代码部分的读取。第二个是使ReadQuestions()方法成为问题类型的共享成员。最后的结果是:

Public Class Question
    Public Property QuestionText As String
    Public Property OptionA As String
    Public Property OptionB As String
    Public Property OptionC As String
    Public Property OptionD As String
    Public Property Answer As String

    Public Sub New(ByVal data() As String)
        'Add error handling here
        QuestionText = data(0), 
        OptionA = data(1), 
        OptionB = data(2), 
        OptionC = data(3),
        OptionD = data(4), 
        Answer = data(5)
    End Sub

    Public Shared Iterator Function ReadFromFile(ByVal FileName As String) As IEnumerable(Of Question)
        Using rdr As New TextFieldParser(FileName)
            rdr.Delimiters = new String() {"##"}

            While Not rdr.EndOfData                                
                Yield New Question(rdr.ReadFields()) 
            End While
        End Using
    End Function
End Class

And you call all this from your existing code like so:

你从现有代码中调用所有这些

Dim Questions = Question.ReadFromFile(My.Resources.ResourceManager.GetObject(globalVariables.currSubject))
For Each q As Question in Questions
    '...
Next