I'm just guessing here, but what you
seem to want to "write" to a string as if it was a file, kind of like
snprintf in C. You're right: QBASIC doesn't support this (directly), but you can implement a few SUBs and FUNCTIONS that will make it hard to tell the difference. Let's suppose you'd be happy with a
read and
write command, similar to the Linux system calls, except they work on strings instead of files.
Unfortunately, QBASIC is very picky about what we call our SUBs and FUNCTIONs, so we have to call our routines
readbuf and
writebuf. Like Linux's
read and
write, they operate on an integer "descriptor" that represents the buffer.
Furthermore, QBASIC doesn't allow structured types to contain variable-length strings or any kind of array.
QBASIC also doesn't have a way to lengthen an array without erasing the contents. But you can lengthen strings that aren't part of TYPEs with impunity (within the 32,767-character limit, of course).
What this is leading up to is a need to use global variables, which is generally considered a bad thing. If we were writing this in C++, we would declare no global variables. But, since we have no choice here in QB, we'll do it anyway:
Code: Select all
CONST maxStreams = 100
DIM SHARED streams(maxStreams) AS STRING
DIM SHARED positions(maxStreams) AS INTEGER
DIM SHARED numStreams AS INTEGER
This allows there to be many streams in memory that can be "read" or "written to".
numStreams holds the number of currently-active streams.
The only fortunate thing is that we can put this code in its own module, and then only FUNCTIONs and SUBs from that module can access the variables. I'll bet Microsoft never realized that QuickBASIC supports object-oriented programming, complete with private data members! Having said that, QuickBASIC still sucks ass, and you should really consider migrating to a real programming language, like Python.
We'll need a function to allocate a new stream. Since our streams are allocated on a stack, we'll call our allocation function
pushbuf%. It returns an integer descriptor that will be accepted by the other functions that make up the interface.
Code: Select all
FUNCTION pushbuf%
pushbuf% = numStreams
numStreams = numStreams + 1
END FUNCTION
And here's a SUB that frees the most recently allocated stream:
Code: Select all
SUB popbuf
' Reset the stream and deallocate memory.
positions(numStreams-1) = 0
streams(numStreams-1) = ""
numStreams = numStreams - 1
END SUB
Here's a couple of procedures that can be used to manipulate the current position in the buffer.
tellbuf% tells you the current position in the buffer (like ANSI C's
ftell), and
seekbuf sets the position within the stream:
Code: Select all
FUNCTION tellbuf%(desc AS INTEGER)
tellbuf = positions(desc)
END FUNCTION
SUB seekbuf(desc AS INTEGER, position AS INTEGER)
IF position > LEN(streams) THEN
position = LEN(streams)
END IF
positions(desc) = position
END SUB
Finally, we can write our functions for "reading" and "writing" a buffer. WARNING: MID$ is 1-based, going against convention:
Code: Select all
FUNCTION readbuf% (desc AS INTEGER, outbuf AS STRING, count AS INTEGER)
outbuf=""
IF desc >= numStreams THEN
' Bad file name or number
ERROR 52
END IF
IF positions(desc) > LEN(streams(desc)) THEN
' Input past end of file
ERROR 62
END IF
outbuf = MID$(streams(desc), positions(desc)+1, count)
positions(desc) = positions(desc) + LEN(outbuf)
readbuf% = LEN(outbuf)
END FUNCTION
SUB writebuf(desc AS INTEGER, inbuf AS STRING)
IF desc >= numStreams THEN
' Bad file name or number
ERROR 52
END IF
streams(desc) = streams(desc) + inbuf
positions(desc) = positions(desc) + LEN(inbuf)
END FUNCTION
Save all the above to one module, STREAMS.BAS, and use File|Create File to create your main module. (Set the Main Module in Run|Set Main Module or your program will do nothing)
Here is a main main module that shows how to use the new interface:
Code: Select all
' These declarations are absolutely necessary.
DECLARE FUNCTION pushbuf% ()
DECLARE FUNCTION tellbuf% (desc AS INTEGER)
DECLARE FUNCTION readbuf% (desc AS INTEGER, outbuf AS STRING, count AS INTEGER)
DECLARE SUB writebuf (desc AS INTEGER, inbuf AS STRING)
DECLARE SUB seekbuf (desc AS INTEGER, position AS INTEGER)
DECLARE SUB popbuf ()
' Our variables.
DIM fd AS INTEGER
DIM hello AS STRING
DIM status AS INTEGER
CLS
' Work with a new buffer.
fd = pushbuf%
' Store the string "Hello, world!" in the buffer
writebuf fd, "Hello, world!"
' Seek the beginning of the buffer.
seekbuf fd, 0
' Retrieve the first 5 bytes stored in the buffer.
status = readbuf%(fd, hello, 5)
' Print the retrieved bytes
PRINT hello
' Retrieve five more bytes from the buffer.
status = readbuf%(fd, hello, 5)
PRINT hello ' Different this time.
________
Buy Vaporizer