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
问题的图示如下
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