File handling.

Posted by Magnum on Thu 14 Mar 2002 11:34 PM — 14 posts, 46,815 views.

Canada #0
I would like to use script that both writes to, and reads from a file, but I have no idea on how it's done.

Perhaps it's just me, but I find Microsoft's home site for VBS very un-intuitive. It takes me a long time to track down the documentation for a particular command.

My intention is to save to a file, a list of values. For example:

crown,bluechest
gloves,bluechest
boots,bluechest
dagger,redchest
sword,redchest
poison,blackchest
poison,blackchest

The number of items in the list may vary, so the save and load script should be able to handle that.

The file does not need to remain open all the time.

I used to know how to do this kind of thing using turboPASCAL, but VBS seems completely different in it's file handling calls.

Your help is appreciated.
Australia Forum Administrator #1
I'm pretty sure that Krenath posted some examples of reading/writing files in VB. Look for him in the "users" list and check some of his recent posts.
Amended on Fri 15 Mar 2002 09:48 PM by Nick Gammon
Canada #2
Ok, I downloaded this help file, which is exactly what I've wanted for a while:

http://msdn.microsoft.com/scripting/vbscript/download/vbsdoc.exe

Shadowfyr provided the link a while ago, but for some reason I glossed over it.

I also reviewed that entire thread, which is here:
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=726

I'm starting to get a grasp on this FileObject thing, but I'm still having some trouble.

It seems to me, it would be wise to try and determine the path that the script file is in automatically. I can't seem to get that working.

Most of the properties\methods listed in the help file (for use with FileObjects) seem to need a path variable passed to them. For example, You can pass a long pathname and use the methods to break it into drive, directory path, filename, etc.

What I want to do is this:

- What drive/directory path is THIS script file in?
- Open/Create file(s) in this directory.

This would be far more preferable than storing the path name in a CONST or something like that.
Canada #3
'Dim ScriptPath
'ScriptPath = "E:\User\Magnum\MUSH Client\Worlds\Ages of Despair\"
'
'Function GetFileContents(sFileName)
' Dim FSO, ScriptFile
' Set FSO = CreateObject("Scripting.FileSystemObject")
' Set ScriptFile = FSO.OpenTextFile(sFilename,1)
' GetFileContents = ScriptFile.ReadAll
' Set ScriptFile = Nothing
' Set FSO = Nothing
'End Function
'
'World.Note "Including File 'AOD_EQ.vbs'"
'Execute GetFileContents(ScriptPath & "AOD_EQ.vbs")

That is code from my script. It's all commented out for the time being, because it's easier to debug the script while it's included in the main file. When it's done, I will move it into the 'library file', and uncomment this code.

I would like to automatically determine 'ScriptPath' rather than have it hardcoded. Can this be done?
Canada #4
A different problem:


Sub EQ_Load_File (thename, theoutput, arrWildcards)
  Dim arrItems
  Dim arrItemNames
  Dim EqItem
  Dim FSO, EqFileName
  Dim FileContents
  Dim F
  Dim x
  EqFileName = ScriptPath & "AOD_EQ_" & World.GetVariable("EQType") & ".txt"
  World.Note "EqFileName: " & EqFileName
  arrItems = Split(EQItemList, ", ")
  Set FSO = CreateObject("Scripting.FileSystemObject")
  If (FSO.FileExists(EqFileName)) Then
    Set F = FSO.OpenTextFile(EqFileName, 1)
    For x = LBound(arrItems) to UBound(arrItems)
      EqItem = "EQ_" & arrItems(x)
      World.Note F.ReadLine ' <<<<<---------- This just displays garbage ????
'     World.SetVariable EqItem, F.ReadLine
'     World.Note EqItem & ": " & World.GetVariable(EqItem)
    Next
    F.Close
  Else
    For x = LBound(arrItems) to UBound(arrItems)
      EqItem = "EQ_" & arrItems(x)
      World.SetVariable EqItem, "none"
    Next
    EQ_Save_File "EQ_Load_File", theoutput, arrWildcards
  End If
End Sub

Sub EQ_Save_File (thename, theoutput, arrWildcards)
  Dim arrItems
  Dim EqItem
  Dim FSO, EqFileName
  Dim F
  Dim x
  EqFileName = ScriptPath & "AOD_EQ_" & World.GetVariable("EQType") & ".txt"
  World.Note "EqFileName: " & EqFileName
  Set FSO = CreateObject("Scripting.FileSystemObject")
  Set F = FSO.CreateTextFile(EQFileName, True, True)
  arrItems = Split(EQItemList, ", ")
  For x = LBound(arrItems) to UBound(arrItems)
    EqItem = "EQ_" & arrItems(x)
    F.WriteLine World.GetVariable(EqItem)
  Next
  F.Close
End Sub


EQ_Save_File works. I can save a file, load it in NotePad, and it looks like it's in order. When I try and use "ReadLine" to read a line back, I get garbage. Any idea on why?

Edited by Nick to add [code] tags.
Amended on Sat 16 Mar 2002 09:47 PM by Nick Gammon
Australia Forum Administrator #5
I tried some lines in immediate mode and they worked, namely:


Set FSO = CreateObject("Scripting.FileSystemObject")
Set F = FSO.OpenTextFile("c:\test.txt", 1)
world.note F.readline
f.close
Set FSO = nothing


However I think your problem is your code structure. It isn't clear to me why you have this:


    For x = LBound(arrItems) to UBound(arrItems)
... read some lines here ...
    next
    F.close


If I read this correctly you are reading one line per file name, not per line in the file.

What you really want is something like this:


Do While Not F.AtEndOfStream
  myline = F.ReadLine
  world.note myline
Loop
F.Close


That will read the entire file until end of file.
Amended on Sat 16 Mar 2002 10:06 PM by Nick Gammon
Canada #6
The LBound to Ubound is counting through the number of items that are in the inventory list. Technically, if the text file were edited manually, this could cause an error with this routine. That's fine, I am not so concerned with that at the moment.

The script does not seem to be reading even a single line properly. The first value comes through as a wierd character followed by an n, the rest come back as null/empty.

This bug totally has me stumped, and I will not be able to complete this project until I can get past it.

Here is the complete code for my EQ project, thus far:


'------------------------------------------------------------
'
' This Script File created by "Magnum" for "Ages Of Despair".
'
'           EQUIPMENT HANDLING SCRIPT
' ------------------------------------------------------------

Dim EqItemList
EqItemList = "bweap, lweap, rweap, head, neck, arms, hands, cloak, torso, belt, legs, feet, lring, rring, shield, lhold, rhold"

Sub Set_EQType (thename, theoutput, arrWildcards)
	World.SetVariable "EQType", Trim(arrWildcards(1))
	World.SetVariable "EQTypeStatus", "EQType: " & CStr(World.GetVariable("EQType"))
	World.Note World.GetVariable("EQTypeStatus")
	Display_StatusLine()
	World.EchoInput = vbsTrue
	EQ_Load_File "Set_EQType", theoutput, arrWildcards
End Sub
' ------------------------------------------------------------
Sub EQ_Load_File (thename, theoutput, arrWildcards)
	Dim arrItems
	Dim EqItem
	Dim FSO, EqFileName
	Dim FileContents
	Dim F
	Dim x
	EqFileName = ScriptPath & "AOD_EQ_" & World.GetVariable("EQType") & ".txt"
	World.Note "EqFileName: " & EqFileName
	arrItems = Split(EQItemList, ", ")
	Set FSO = CreateObject("Scripting.FileSystemObject")
	If (FSO.FileExists(EqFileName)) Then
		Set F = FSO.OpenTextFile(EqFileName, 1)
		For x = LBound(arrItems) to UBound(arrItems)
			EqItem = "EQ_" & arrItems(x)
			World.Note F.ReadLine
'			World.SetVariable EqItem, F.ReadLine
'			World.Note EqItem & ": " & World.GetVariable(EqItem)
		Next
		F.Close
	Else
		For x = LBound(arrItems) to UBound(arrItems)
			EqItem = "EQ_" & arrItems(x)
			World.SetVariable EqItem, "none"
		Next
		EQ_Save_File "EQ_Load_File", theoutput, arrWildcards
	End If
End Sub

Sub EQ_Save_File (thename, theoutput, arrWildcards)
	Dim arrItems
	Dim EqItem
	Dim FSO, EqFileName
	Dim F
	Dim x
	EqFileName = ScriptPath & "AOD_EQ_" & World.GetVariable("EQType") & ".txt"
	World.Note "EqFileName: " & EqFileName
	Set FSO = CreateObject("Scripting.FileSystemObject")
	Set F = FSO.CreateTextFile(EQFileName, True, True)
	arrItems = Split(EQItemList, ", ")
	For x = LBound(arrItems) to UBound(arrItems)
		EqItem = "EQ_" & arrItems(x)
		F.WriteLine World.GetVariable(EqItem)
	Next
	F.Close
End Sub
' ------------------------------------------------------------
Sub EQ_Display_Inventory (thename, theoutput, arrWildcards)
	Dim arrItems
	Dim EqItem
	Dim x
	arrItems = Split(EQItemList, ", ")
	World.Note World.GetVariable("EqTypeStatus")
	World.Note " "
	For x = LBound(arrItems) to UBound(arrItems)
		EqItem = "EQ_" & arrItems(x)
		World.Note PadLeft(arrItems(x), 7) & ": " & World.GetVariable(EqItem)
	Next
End Sub


...continued in next message.
Canada #7

' ------------------------------------------------------------
Sub EQ_Set_Item (thename, theoutput, arrWildcards)
	Dim arrArguments
	Dim ItemType
	Dim ItemName
	Dim x
	Dim ValidType
	arrArguments = Split(arrWildcards(1), " ")
	ItemType = Trim(Lcase(arrArguments(0)))
	ItemName = Empty
	For x = (LBound(arrArguments) + 1) to UBound(arrArguments)
		ItemName = ItemName & " " & CStr(arrArguments(x))
	Next
	ItemName = Trim(Lcase(ItemName))
	If ItemName = Empty Then ItemName = "none"
	x = InStr(1, EQItemList, ItemType, vbTextCompare)
	If x <> 0 Then
		Select Case ItemType
			Case "bweap"
				If ItemName = "none" Then
					ValidType = True
				Else
					If (World.GetVariable("EQ_lweap") <> "None") AND _
						(World.GetVariable("EQ_rweap") <> "None") AND _
						(World.GetVariable("EQ_lhold") <> "None") AND _
						(World.GetVariable("EQ_rhold") <> "None") Then
						World.Note "You must set lweap, rweap, lhold and rhold to 'none' before you can set this item."
						ValidType = False
					Else
						ValidType = True
					End If
				End If
			Case "lweap"
				If ItemName = "none" Then
					ValidType = True
				Else
					If (World.GetVariable("EQ_bweap") <> "None") AND _
						(World.GetVariable("EQ_lhold") <> "None") Then
						World.Note "You must set bweap and lhold to 'none' before you can set this item."
						ValidType = False
					Else
						ValidType = True
					End If
				End If
			Case "rweap"
				If ItemName = "none" Then
					ValidType = True
				Else
					If (World.GetVariable("EQ_bweap") <> "None") AND _
						(World.GetVariable("EQ_rhold") <> "None") Then
						World.Note "You must set bweap and rhold to 'none' before you can set this item."
						ValidType = False
					Else
						ValidType = True
					End If
				End If
			Case "lhold"
				If ItemName = "none" Then
					ValidType = True
				Else
					If (World.GetVariable("EQ_bweap") <> "None") AND _
						(World.GetVariable("EQ_lweap") <> "None") Then
						World.Note "You must set bweap and lweap to 'none' before you can set this item."
						ValidType = False
					Else
						ValidType = True
					End If
				End If
			Case "rhold"
				If ItemName = "none" Then
					ValidType = True
				Else
					If (World.GetVariable("EQ_bweap") <> "None") AND _
						(World.GetVariable("EQ_rweap") <> "None") Then
						World.Note "You must set bweap and rweap to 'none' before you can set this item."
						ValidType = False
					Else
						ValidType = True
					End If
				End If
			Case Else
				ValidType = True
		End Select
	Else
		World.Note "ItemType not recognized. Syntax:  eqset <ItemType> <Item Name>"
		World.Note "For a list of valid types, use the 'eqlist' command."
	End If
	If ValidType Then
		World.SetVariable("EQ_" & ItemType), ItemName
		World.Note ItemType & ": " & ItemName
		EQ_Save_File "EQ_Set_Item", theoutput, arrWildcards
	End If
End Sub
' ------------------------------------------------------------
Sub EQ_Get (thename, theoutput, arrWildcards)
End Sub

Sub EQ_Drop (thename, theoutput, arrWildcards)
End Sub

Sub EQ_Wear (thename, theoutput, arrWildcards)
End Sub

Sub EQ_Remove (thename, theoutput, arrWildcards)
End Sub
' ------------------------------------------------------------
Sub EQ_Install
	World.AddAlias "EQ_Set_Equipment_Type", "eqt *", Empty, 1057, "Set_EQType"
	World.Note "Alias added: eqt     (Use to declare the Current EQ Type.)"
	World.AddAlias "EQ_Set_Item", "eqset *", Empty, 1057, "EQ_Set_Item"
	World.Note "Alias added: eqset   (Use to declare an item in your current EQ Type.)"
	World.AddAlias "EQ_List_Items", "eqlist", Empty, 1057, "EQ_Display_Inventory"
	World.Note "Alias added: eqlist  (Use to list the items in your current EQ Type.)"
	World.Note "Installation complete."
End Sub
' ------------------------------------------------------------
' ------------------------------------------------------------


Because all the action is taking place strictly between the command box and the script, it should work on ANY mud for now.
Canada #8
Oops, forgot. The following are elsewhere in my script, and would be needed to run the script provided above:

Dim ScriptPath
ScriptPath = "E:\User\Magnum\MUSH Client\Worlds\Ages of Despair\"

(Of course, set an appropriate path for yourself).

Also, only 'padleft' is needed, but here's that script, plus it's brother:


' ------------------------------------------------------------
'   STRING MANIPULATION
' ------------------------------------------------------------
Function PadLeft (String, Length)
	Dim StringLength
	StringLength = Len(String)
	If StringLength < Length Then
		String = Space(Length - StringLength) + String
	End If
	PadLeft = String
End Function

Function PadRight (String, Length)
	Dim StringLength
	StringLength = Len(String)
	If StringLength < Length Then
		String = String + Space(Length - StringLength)
	End If
	PadRight = String
End Function
Canada #9
The reason for using the LBound to UBound in the load script as well, is because I [will] set MushClient variables based on the values in the array. If I used the method you suggested, I would still have to do counting to keep track of which item we are working with... so I used the method that I did, despite it not being as fool-froof.

The one difference I see in my script, as opposed to examples I've looked at, is that I check to see if the file exists immediately before opening the file for reading... but I don't see why that should cause a problem.
Australia Forum Administrator #10
Try to narrow it down a bit. Your code is quite long. Try the example I did (changing the file name of course), you can type it line-by-line into the command window, that is what I did (preceded by "/").

Either you see the file lines echoed or not. If you do, then your surrounding logic is causing the problem. If not, something is wrong with readline. Maybe the file doesn't exist where you think it does, or something.
Canada #11
Hmm... Ok, I tried this:


Sub filetest
	Dim FSO, File
	Dim TempString
	Set FSO = CreateObject("Scripting.FileSystemObject")
	Set File = FSO.OpenTextFile("C:\AOD_EQ_Solo.txt", 1)
	tempstring = File.readline
	world.note tempstring
	File.close
	Set FSO = nothing
End Sub


...and I get the same problem. The only output I get is "þn".

I tried both a long path which I really want, and then the shorter one above. I _DID_ make one realization. I entered a nonsense filename at one point, and I get the same "þn" result.

...So, it's not finding the file. But why? Especially this simple version above!
Canada #12
WORKING !!!

Found the bug.

Save script was inadvertantly saving the file in "Unicode" format. Load script was attempting to load the file in ASCII format.
Australia Forum Administrator #13
Well spotted! :)

I didn't notice that, but from the help file:


object.CreateTextFile(filename[, overwrite[, unicode]])


You had the third argument to true, as you now realise.

And, to confuse matters, Notepad will read Unicode files. ;)