Jump to content

Parsing text format and creating custom


Recommended Posts

Can someone help me create this base script? I am trying to write an app that converts from one format to a custom one. The 1st line is the program version. The 2nd line is the name of the file. The 3rd line is the author of the file. The 4th line is the font of the file. I can discard the 1st and 4th lines of the file. Where I need help is the next section of the file. So far the file will look like this:

v1.0
File name
John Doe
Comic Sans

Then, there are questions, in a similar format.

Am I awesome? 'question
Yes 'answer 1
No 'answer 2
Maybe 'answer 3
So 'answer 4
0 'correct answer.  can be 0 thru 4 (4 is none of the above).  0 thru 3 can be listed, so the answer can be 01 or 03 or 0123 or 12, etc.  whenever it is 4, no other number appears
C 'randomize or not
A hint can go here 'obviously a hint
Explanation of answer 'self explanatory

I commented using apostrophies above, they are not really there in the file. The text repeats so a sample will look like this:

v1.0
Example file
josheee12
Comic Sans
Question 1 here
Q1A1
Q1A2
Q1A3
Q1A4
01
C
Answer text example Q1
Hint here Q1
Explanation here Q1
Question 2 here
q2A1
Q2A2
Q2A3
Q2A4
4

Answer text example Q2
Hint here Q2
Explanation here Q2

I want to create an XML-style format out of this, so I get something along the lines of:

<info version="v1.0">
<name>Example file</name>
<author>josheee12</author>
<font>Comic Sans</font>
<num>2</num>
</info>

<question id="1">

<ask>Question 1 here</ask>
<answer id="a">Q1A1</answer>
<answer id="b">Q1A2</answer>
<answer id="c">Q1A3</answer>
<answer id="d">Q1A4</answer>

<correct>ab</correct>

<answertext>Answer text example Q1</answertext>
<hint>Hint here Q1</hint>
<explanation>Explanation here Q1</explanation>

</question>


<question id="2">

<ask>Question 2 here</ask>
<answer id="a">Q2A1</answer>
<answer id="b">Q2A2</answer>
<answer id="c">Q2A3</answer>
<answer id="d">Q2A4</answer>

<correct>n</correct>

<answertext>Answer text example Q2</answertext>
<hint>Hint here Q2</hint>
<explanation>Explanation here Q2</explanation>

</question>

and so on.

For question #2 the value of "correct" is n for none. I know this is a large task, and any help is appreciated.

Link to comment
Share on other sites

This is the first part, parsing out the data, I leave the second part to you (hint: it's been mostly answered on the forums already):

#region Constants
Global $HEADER_Names [4] = [ _
"version", _ 
"file", _ 
"author", _
"font"]

Global $BODY_Names [9] = [ _ 
"question", _
"answer0", _
"answer1", _
"answer2", _
"answer3", _
"solution", _
"random", _
"hint", _
"explanation"]

Local $fileName = @ScriptDir & "\DataFile.txt"
#endregion Constants

#region Program Body
Local $handle = FileOpen ($fileName, 0) ;ReadMode
If $handle = -1 Then
    ErrorBox ("Unable to open file")
    Exit
EndIf

; Extract the Header File
Local $header = ExtractHeader ($handle)
If $header = -1 then ErrorBox ("Premature end of file reached, check file formatting")
If $header = 1 then ErrorBox ("Error reading file")

; Debug the Header Extraction
DebugHeader ($header)

; Extract the Body
Local $body = ExtractData ($handle)
If $body = -1 Then 
    ErrorBox ("Permature end of file reached, check file formatting")
ElseIf $body = 1 Then 
    ErrorBox ("Error reading file")
ElseIf $body = 0 Then 
    ErrorBox ("No Data Extracted")
EndIf

; Debug the Body Extraction
DebugBody ($body)


#endregion Program Body

#region Parsing Functions
;Returns an array of the header values or -1 if a premature end of file was 
;reached, or 1 if unable to read from the file
Func ExtractHeader ($FileHandle)
    Local $HeaderArray [4]
    $HeaderArray [0] = FileReadLine ($FileHandle)
    If @error Then Return @error
        
    $HeaderArray [1] = FileReadLine ($FileHandle)
    If @error Then Return @error
        
    $HeaderArray [2] = FileReadLine ($FileHandle)
    If @error Then Return @error
    
    $HeaderArray [3] = FileReadLine ($FileHandle)
    If @error Then Return @error
    
    Return $HeaderArray
EndFunc

;Returns an array of arrays:
;[ [data, from, question1], [data, from, question2] ... ]
Func ExtractData ($FileHandle)
    Local $dataArray
    Local $question, $a0, $a1, $a2, $a3, $sol, $solText, $rand, $hint, $exp
    Local $state = 0
    ; We are going to use a simple state based machine to keep track of where we are in the file
    ; 0 = Looking for question
    ; 1-4 = Looking for answers 1-4
    ; 5 = Looking for correct answer
    ; 6 = Checking for randomization
    ; 7 = Looking for answer text
    ; 8 = Looking for a hint
    ; 9 = Looking for an explication
    ; 10 = Found all, store
    ; 11 = EOF reached
    ; 12 = Early EOF reached
    While $state < 11
        If $state = 0 Then
            $question = FileReadLine ($FileHandle)
            If $question or @error Then ;Skip blanks between records
                AdvanceState ($state, @error)
            EndIf
        ElseIf $state = 1 Then
            $a0 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 2 Then
            $a1 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 3 Then
            $a2 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 4 Then
            $a3 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 5 Then
            $sol = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 6 Then
            If FileReadLine ($FileHandle) then
                $rand = "yes"
            Else
                $rand = "no"
            EndIf
            AdvanceState ($state, @error)
        ElseIf $state = 7 Then
            $solText = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 8 Then
            $hint = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 9 Then
            $exp = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 10 Then
            Add_Data ($dataArray, $question, $a0, $a1, $a2, $a3, $sol, $rand, $hint, $exp)
            $question = $a0 = $a1 = $a2 = $a3 = $sol = $rand = $hint = $exp = ""
            AdvanceState ($state)
        EndIf
    WEnd
    
    If $state = 12 then return -1 ; Premature EOF
    If Not IsArray ($dataArray) Then Return 0
    
    Return $dataArray
EndFunc

; Advances the state counter, setting it to the error state if EOF was reached
Func AdvanceState (ByRef $state, $error = 0)
    ConsoleWrite ("(" & $state & " => ")
    If $error = -1 Then
        If $state = 0 Then 
            $state = 11 ; Only permissible spot for an EOF is between records
        Else
            $state = 12 ; Early EOF
        EndIf
    Else
        $state = mod ($state + 1, 11)
    EndIf
    ConsoleWrite ($state & ")" & @LF)
EndFunc
    
Func Add_Data (ByRef $Array, $question, $a0, $a1, $a2, $a3, $sol, $rand, $hint, $exp)
    ConsoleWrite ("Saving data" & @LF)
    
    If Not IsArray ($Array) Then
        ; If not an array, make it an array
        Dim $Array [1][9]
    Else    
        ; Resize the array
        ReDim $Array[Ubound ($Array, 1) + 1][9] 
    EndIf
    
    Local $last = Ubound ($Array, 1) - 1
    $Array[$last][0] = $question
    $Array[$last][1] = $a0
    $Array[$last][2] = $a1
    $Array[$last][3] = $a2
    $Array[$last][4] = $a3
    $Array[$last][5] = $sol
    $Array[$last][6] = $rand
    $Array[$last][7] = $hint
    $Array[$last][8] = $exp
EndFunc
        
#endregion Parsing Functions

#region Debug Functions
Func DebugHeader ($HeaderArray)
    Local $testString
    For $i = 0 to 3
        $testString &= $i & ": " & $HEADER_Names[$i] & " = " & $HeaderArray[$i] & @LF
    Next
    debug ($testString)
EndFunc

Func DebugBody ($BodyArray)
    Local $testString = ""
    For $question = 0 to Ubound ($BodyArray, 1) -1 
        $testString &= "Question " & $question & @LF
        For $line = 0 to Ubound ($BodyArray, 2) -1 
            $testString &= @TAB & $BODY_Names [$line] & " = " & $BodyArray[$question][$line] & @LF
        Next
    Next
    debug ($testString)
EndFunc

Func ErrorBox ($string)
    MsgBox (0, "ERROR", $string)
EndFunc

Func debug ($data)
    If IsArray ($data) Then 
        debugArray ($data)
    Else
        MsgBox (0, "DEBUG", $data)
    EndIf
EndFunc

Func debugArray ($data)
    Local $string = ""
    Local $counter = 0
    For $a in $data
        $string &= $counter & @TAB & $a & @LF
        $counter += 1
    Next
    debug($string)
EndFunc#endregion Debug Functions

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

Well, nothing really, it's only 1/2 a program.

At the end of the program you will have:

1: an array named $header containing 4 elements, each containing a line of the header

2: an array named $body containing x elements, one for each question, each element will be an array containing the parts of the question.

The order of each is in the Global Constants $HEADER_Names and $BODY_Names. Check the debug functions for an example of how to use those.

What you have to do next is to figure out how to dump this data to XML.

Happy Scripting :mellow:

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

I got it without using any form of external code. For the DebugHeader, it became:

Func DebugHeader($HeaderArray)
    Local $testString
    $testString &= "<header>" & @LF
    For $i = 0 To 3
            $testString&= "<" & $HEADER_Names[$i] & ">" & $HeaderArray[$i] & "</" & $HEADER_Names[$i] & ">" & @LF
        Next
    $testString &= "</header>"
    debug($testString)
EndFunc   ;==>DebugHeader
Link to comment
Share on other sites

::smiles:: I was hoping you'd catch that.

I was looking at the code again and realized, you'll want to put

FileClose ($handle)
at the end of the 'Extract Body' section.

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

Close, right before:

; Debug the Body Extraction

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

With some tweaking, I got the following output:

<header>

<version>v2.0</version>
<file>TestQuiz</file>
<author>josheee12</author>
<font>MS Sans Serif</font>

</header>

<body>

<question id="0">
<questiontext>Does the script work?</questiontext>
<answer0>Yes</answer0>
<answer1>No</answer1>
<answer2>Maybe</answer2>
<answer3>So</answer3>
<solution>02</solution>
<random>no</random>
<hint>Your hints are nice</hint>
<explanation>I am awesome</explanation>
</question>

<question id="1">
<questiontext>Does the script work well?</questiontext>
<answer0>Yes and very well</answer0>
<answer1>No</answer1>
<answer2>Maybe</answer2>
<answer3>So</answer3>
<solution>0</solution>
<random>yes</random>
<hint>Your hints are nice</hint>
<explanation>I am awesome</explanation>
</question>

</body>
Link to comment
Share on other sites

Well done :mellow:

You could sneak a @Tab in there if you wanted to make it more readable, but you got it Posted Image

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

Well done :mellow:

You could sneak a @Tab in there if you wanted to make it more readable, but you got it Posted Image

Yeah, I know. This will be a 2-part program. There will be a web-based client for actually taking these quizzes. Until I write a quiz creation frontend in JS/PHP/AJAX, I want to use another program to generate the quizzes. How can I trim spaces trailing off the beginning/end of a string? Edited by josheee12
Link to comment
Share on other sites

It looks like you just need to test if the answer has more than 1 character, which should be easy with something like StringLen. Then just process it during the extraction like happened with the 'randomize' entry.

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

It looks like you just need to test if the answer has more than 1 character, which should be easy with something like StringLen. Then just process it during the extraction like happened with the 'randomize' entry.

Where can I insert this code? I know the method, but where?
Link to comment
Share on other sites

Func ExtractData ($FileHandle)
    Local $dataArray
    Local $question, $a0, $a1, $a2, $a3, $sol, $solText, $rand, $hint, $exp ;<- You'll have to add a variable here for temporary storage
    Local $state = 0
    ; We are going to use a simple state based machine to keep track of where we are in the file
    ; 0 = Looking for question
    ; 1-4 = Looking for answers 1-4
    ; 5 = Looking for correct answer
    ; 6 = Checking for randomization
    ; 7 = Looking for answer text
    ; 8 = Looking for a hint
    ; 9 = Looking for an explication
    ; 10 = Found all, store
    ; 11 = EOF reached
    ; 12 = Early EOF reached
    While $state < 11
        If $state = 0 Then
            $question = FileReadLine ($FileHandle)
            If $question or @error Then ;Skip blanks between records
                AdvanceState ($state, @error)
            EndIf
        ElseIf $state = 1 Then
            $a0 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 2 Then
            $a1 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 3 Then
            $a2 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 4 Then
            $a3 = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 5 Then; <- This part deals with the correct answer, so here's where you'd want to test for multiple answers
            $sol = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 6 Then
            If FileReadLine ($FileHandle) then
                $rand = "yes"
            Else
                $rand = "no"
            EndIf
            AdvanceState ($state, @error)
        ElseIf $state = 7 Then
            $solText = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 8 Then
            $hint = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 9 Then
            $exp = FileReadLine ($FileHandle)
            AdvanceState ($state, @error)
        ElseIf $state = 10 Then
    ;<- whatever you named your temporary variable, it'll need to be added to these lines:
            Add_Data ($dataArray, $question, $a0, $a1, $a2, $a3, $sol, $rand, $hint, $exp)
            $question = $a0 = $a1 = $a2 = $a3 = $sol = $rand = $hint = $exp = ""
            AdvanceState ($state)
        EndIf
    WEnd
    
    If $state = 12 then return -1 ; Premature EOF
    If Not IsArray ($dataArray) Then Return 0
    
    Return $dataArray
EndFunc

;This function will also have to be modified, I think I made the rest not count on any particular array length, but checking would be good, as
;I could be mistaken
Func Add_Data (ByRef $Array, $question, $a0, $a1, $a2, $a3, $sol, $rand, $hint, $exp)
    ConsoleWrite ("Saving data" & @LF)
    
    If Not IsArray ($Array) Then
        ; If not an array, make it an array
        Dim $Array [1][9]
    Else    
        ; Resize the array
        ReDim $Array[Ubound ($Array, 1) + 1][9] 
    EndIf
    
    Local $last = Ubound ($Array, 1) - 1
    $Array[$last][0] = $question
    $Array[$last][1] = $a0
    $Array[$last][2] = $a1
    $Array[$last][3] = $a2
    $Array[$last][4] = $a3
    $Array[$last][5] = $sol
    $Array[$last][6] = $rand
    $Array[$last][7] = $hint
    $Array[$last][8] = $exp
EndFunc

Oh, and you'll also want to add something as a tag name to the $BODY_Names, also because the script will break if the size of the $BODY_Names array and the second dimension of the $body array are of different sizes.

#fgpkerw4kcmnq2mns1ax7ilndopen (Q, $0); while ($l = <Q>){if ($l =~ m/^#.*/){$l =~ tr/a-z1-9#/Huh, Junketeer's Alternate Pro Ace /; print $l;}}close (Q);[code] tag ninja!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...