Hi, pbcdhs-ga:
The functionality which your program needs, in between reading the
text file into the array Open2() and ultimately writing it back out
again, seems to have three parts:
1) Parse the key names from the array and place them in a listbox.
2) Given a selected key name from listbox, search for the
corresponding value in Open2() and assign this string to a textbox
control.
3) Allow the user to edit the value in the textbox and then save the
revised value back into Open2().
The question you asked focuses on the middle step of this
functionality, namely searching for values once the key names are
given.
However the first and second steps are really quite closely related,
so I will present code for both of those parts. The third part is
much less related, and to design it correctly would require additional
clarifications. Therefore I will not attempt to address the third
part in my answer. If you would like help with that functionality,
I'll ask that you post a separate question about that.
In my VB.Net project I have the default form Form1, to which I've
added a textbox for the name of an input/output file (TextBox1), a
listbox for the key names (ListBox1), and a second textbox for the
corresponding string values (TextBox2). I tweaked the attributes of
these controls slightly, e.g. the presence of scrollbars, to make
their behavior and appearance appropriate for the application in mind.
To the code "behind" Form1, I've added a declaration of the global
string array:
Dim Open2() As String
just as you apparently did. I've inserted code there in the same
module for a subroutine that performs the first functionality
described above, parsing out the key names from Open2() after that
array is initialized according to your choice of input files:
Public Sub FindKeys()
REM find keys and insert them into listbox
Dim i As Integer
Dim keyDesc As Boolean
Dim keyStr As String
keyDesc = False
For i = 0 To Open2.Length - 1
If Not (keyDesc) Then
keyStr = Open2(i).Substring(0, Open2(i).IndexOf(" ") - 1)
ListBox1.Items.Add(keyStr)
If keyStr.Equals("description") Then
keyDesc = (Open2(i).IndexOf("""") > -1) And _
(Open2(i).IndexOf("""")=Open2(i).LastIndexOf(""""))
End If
Else
keyDesc = (Open2(i).IndexOf("""") < 0)
End If
Next i
End Sub
Let's review the ideas of this code before proceeding to the code for
the second part of the functionality, the actual search for values
procedure.
This first code is more complex than the tiny snippet you used to
insert all the contents of Open2() into ListBox1. This is due to our
need to distinguish the key name from the following value (they are
assumed to be separated by a space), and also to the machinery needed
to keep track of a value associated with the special "description" key
that uses quotation marks and _may_ extend over more than one line.
To the latter end we declare a local boolean variable keyDesc that
will help us by tracking whether we are in the midst of one of these
multiline descriptions.
With keyDesc initialized to False at the outset, we are prepared to
loop through the strings Open2() in the same order (presumably) as the
corresponding lines that appeared in the input file.
We anticipate finding a key name at the beginning of a line only when
keyDesc is False, ie. only when we are _not_ skipping through the
contents of a multiline "description". Hence this loop is structure
by the if...then...else logic shown.
Since you have asked for help with doing the searching in a VB.Net
compatible manner, I have consistently chosen the String class methods
over functions available in VB.Net which are more or less intended for
backward compatibility.
Thus I've used the String.Substring and String.IndexOf methods to
isolate the key name at the beginning of such lines in Open2(), rather
than Mid() and Instr() functions that might be familiar to you from
VB6 or before. Recall that a key name will be terminated by a space.
Once a key name is found, we immediately insert it into the listbox.
Then we check to see if perhaps the key name is "description". If so,
we make some further checks to see whether the quoted value that we
expect to follow extends beyond the current line. If it does extend
to the next lines, then we wind up setting keyDesc to True.
The additional tests referred to ask whether that line actually
contains a quotation mark (strangely enough four consecutive quotation
marks are needed in order to syntactically define a single quotation
mark in a string argument). But if so, that line may contain both the
opening and closing quotes (in which case the description value is
confined to just one line and we assign keyDesc to be False). If
there is only one quote mark (the first is the last), then we set
keyDesc to be True.
Finally for this loop, the "else" part of the logic simply needs to be
on the lookout for the closing quote mark, namely the next line which
contains a quote mark. This arrangement (unlike the VB syntax parser)
does not allow for nesting of quotation marks inside description
values. The way we formulate this is:
keyDesc = (Open2(i).IndexOf("""") < 0)
The IndexOf method returns -1 only when the search fails. In this
case that means when the line contains no quotation mark, keyDesc will
(continue to) be True.
In laying out this code I've tried to honor all the essential logical
tests but to keep the code simple enough to be understood. Some
tweaks that might have merit for the sake of robustness would check
for errors. For example, a quote mark is presumably not valid as part
of a key name, but I make no such checks. That's not a big deal here,
one way or the other, because I've only implemented the quotation mark
machinery for the single key name "description". If you had in mind
to allow other key names to assume multiline text values using the
same quotation marks to unify the lines, then the issue of checking
for the locations of quote marks within key names might be more
germane.
A second "robustness" tweak would be to test the value of keyDesc
after exiting the loop. The value of keyDesc should be False at that
point, unless a multiline quoted description was opened but never
closed in the input file. An obvious error, but arguably one that is
better handled by exposing it to the user rather than by concealing
it.
I'm going to take a "break" in the discussion at this point before
continuing with the "search" code. The code is similar enough that
I'd rather put it into a separate section ("clarification") to avoid
any confusion.
regards, mathtalk |
Clarification of Answer by
mathtalk-ga
on
12 Apr 2003 09:58 PDT
Hi, pbcdhs-ga:
Good catch on the shortened key names!
Here's the second patch of code; I was more parsimonious with
indentations to avoid problems with the code "wrapping around".
Discussion follows:
Public Function GetValue(ByVal keyGet As String) As String
REM given key name keyGet, return corresponding value string
Dim i As Integer
Dim keyFound As Boolean
Dim keyDesc As Boolean
Dim keyStr As String
keyFound = False
keyDesc = False
For i = 0 To Open2.Length - 1
If Not (keyDesc) Then
keyStr = Open2(i).Substring(0, Open2(i).IndexOf(" "))
If keyStr.Equals(keyGet) Then keyFound = True
If keyStr.Equals("description") Then
keyDesc = (Open2(i).IndexOf("""") > -1) And _
(Open2(i).IndexOf("""") = Open2(i).LastIndexOf(""""))
End If
If keyFound Then
GetValue = Open2(i).Substring(Open2(i).IndexOf(" ") + 1)
End If
Else
keyDesc = (Open2(i).IndexOf("""") < 0)
If keyFound Then
GetValue &= Chr(13) & Chr(10) & Open2(i)
End If
End If
If keyFound And Not (keyDesc) Then Exit For
Next i
If Not (keyFound) Then GetValue = String.Empty
End Function
The basic loop structure is the same, but our logic is complicated by
needing to extract information from more than one line in the
"description". To emphasize the similarity of structure, I put all
the logic within a single loop, as before. However I did add logic at
the loop's end to return an empty value string if the requested key is
not found.
I think the code is written in such a way that if the "description"
value is on a single line, the correct value is extracted regardless
of whether or not it is set off by quotation marks. This is a small
detail of the spec that was not clear to me, so I tried to make it
work either way.
Please let me know if anything needs clarification, esp. bugs or .Net
framework calls that should be explained in greater detail.
regards, mathtalk-ga
|
Clarification of Answer by
mathtalk-ga
on
12 Apr 2003 18:57 PDT
Hi, pbcdhs-ga:
To remove those two quotation marks from around a description string,
you could use the String.Substring method to drop the first and last
characters, by specifying:
myString.Substring(1,myString.Length - 2)
[.Net Substring Method]
http://msdn.microsoft.com/library/en-us/cpref/html/
frlrfsystemstringclasssubstringtopic.asp
Alternatively you could use the String.Replace method, which replaces
all occurrences of the first string argument with those of the second.
I have in mind:
myString.Replace("""","")
[.Net Replace Method]
http://msdn.microsoft.com/library/en-us/cpref/html/
frlrfsystemtextregularexpressionsregexclassreplacetopic.asp
which cryptically says to replace all the quotation marks with
nothing, i.e. to remove them.
As far as how to "parse" a string like the given example:
"<:87214:9999>"
into the two separate numbers, you will probably want to make use of
the method String.Split which "splits" a string instance into an array
of strings delimited by one or more of the characters passed in a
parameter array.
Specifically if myString = "<:87214:9999>", then:
Dim myStrArray as String()
myStrArray = myString.Split(":")
should produce:
myStrArray(0) = "<"
myStrArray(1) = "87214"
myStrArray(2) = "9999>"
If instead you used:
myStrArray = myString.Split("<",":",">")
you would produce:
myStrArray(0) = ""
myStrArray(1) = ""
myStrArray(2) = "87214"
myStrArray(3) = "9999"
myStrArray(4) = ""
This is a powerful tool for taking strings apart and would repay some
experimentation on your part to better understand how it can be
applied to your goals.
[.Net Split Method]
http://msdn.microsoft.com/library/en-us/cpref/html/
frlrfsystemstringclasssplittopic.asp
regards, mathtalk-ga
|