Table of Contents
6.2.0 Stream Input-Output Functions
The following sections introduce you to stream input-output functions. Examples supplement the descriptions.
6.2.0.1 OPEN
Stream = OPEN FileName [ FOR [ READ | INPUT ] [ WRITE | OUTPUT ] [ CREATE | APPEND ] [ WATCH ] ]
Opens a stream for reading, writing, creating or adding data - optionally also for watching. The stream must exist or the keyword CREATE must also be used.
- If the keyword CREATE is specified, the stream is created. If it already exists, it will be deleted first.
- If the keyword APPEND is specified, the stream pointer is moved to the end of the stream immediately after opening.
- If the keywords READ or WRITE are specified, the inputs and outputs are not buffered.
- If the keywords INPUT or OUTPUT are specified, the inputs and outputs are buffered.
- If the keyword WATCH is specified, the stream is monitored by the interpreter via the select(2) system call:
- If at least one byte can be read from the file, the File_Read() event is triggered.
- If at least one byte is written to the file, the File_Write() event is triggered.
6.2.0.2 CLOSE
CLOSE [ # ] hStream hStream.Close()
Both statements close an open stream. These statements never fail. When you have a process stream open, it closes its standard input - just as if you were typing the key combination CTRL+D in a terminal. Standard input and standard output refer to the data channels in a console.
6.2.0.3 OPEN STRING
Stream = OPEN STRING [ aString ] [ FOR [ READ ] [ WRITE ] ]
Use the OPEN STRING command to access a string with the stream interface.
- If the string aString is opened for reading, the string argument aString must be specified! Then the content of the string is accessed sequentially.
- Reading a string stream is always possible.
- If the string aString is opened for writing, an internal string stream buffer collects all written data.
If you set the tag property with a value - like hStringStream.Tag = “open” - after opening, then you have the possibility to check if the StringStream has already been closed.
6.2.0.4 CLOSE StringStream
String = CLOSE [ # ] StringStream
Closing a string stream returns the complete contents of the internal string stream buffer.
Example
' Gambas class file Public sLog As String Public hLogStream As Stream Public fTemperature As Float Public Sub Form_Open() hLogStream = Open String sLog For Write SetLogHeader() LogTimer.Delay = 1 * 1000 LogTimer.Start() End Public Sub btnReadFromStreamString_Click() TextArea1.Text = Close #hLogStream hLogStream = Open String sLog For Write SetLogHeader() End Public Sub SetLogHeader() Write #hLogStream, "PROTOKOLL" & gb.NewLine Write #hLogStream, "Datum: " & Format(Now, "dd. mmmm yyyy") & gb.NewLine Write #hLogStream, String$(38, "-") & gb.NewLine End Public Sub LogTimer_Timer() ' fTemperature = Round(RS232_Value,-2) fTemperature = Round(Rnd(19, 20), -1) Write #hLogStream, Format(Now, "hh:nn:ss") & " | " & "T = " & Str(fTemperature) & " °C" Write #hLogStream, gb.NewLine End
For practical use, you can also read the randomly generated temperature values in the above example by the temperature values of an RS232 interface with temperature sensor.
This is what a protocol extract looks like:
PROTOKOLL Datum: 08. November 2018 -------------------------- 09:36:31 | T = 19,7 °C 09:36:32 | T = 20,0 °C 09:36:33 | T = 19,1 °C 09:36:34 | T = 19,2 °C 09:36:35 | T = 20,0 °C 09:36:36 | T = 19,9 °C
6.2.0.5 WRITE
- (a) WRITE [ # wStream , ] Expression AS Datatype
- (b) WRITE [ # wStream , ] WString [ , Length ]
- (c) WRITE [ # wStream , ] Pointer , Length
(a) Writing data of a specific data type
The first syntax writes an expression to the stream wStream by using its binary representation.
- If wStream is not specified, the standard output is used.
- When writing a string, the length of the string is sent before the content of the string.
- The data type of the expression can be one of the following: NULL, Boolean, Byte, Short, Integer, Long, Pointer, Single, Float, Date, String, Variant, any Array or Collection, or any Structure.
- If Expression is a Collection, an Array or a Structure, then its contents are written recursively.
- When writing a structure, the structure type must be specified as the data type.
- If an unsupported data type is written or a circular reference is detected, then an error is raised.
- This command uses the byte order of wStreams to write the data.
Example
You can save a string array to a file and export it that way. An array is written serialised to a file via Write. The content of the file has a gambas-specific file format:
Public hFile As File Public aNames As String[] ... ' Data export If Dialog.SaveFile() Then Return hFile = Open Dialog.Path For Write Create Write #hFile, aNames As ARRAY Close #hFile
(b) Writing the contents of a string
The second syntax writes a specified number of bytes - specified by the Length value - from the string wString to the specified stream.
If the stream is not specified, the standard output is used.If Length is not specified, the length of wString is used.
(c) Writing the memory content
The third syntax writes a specified number of bytes - specified by the Length value - from the Pointer memory address to the specified stream.
- If the stream is not specified, the standard output is used.
- If Length is not specified, the length of wStream is used.
6.2.0.6 PRINT
PRINT [ # hStream , ] Expression [ { ; | ;; | , } Expression ... ] [ { ; | ;; | , } ]
The instruction writes the contents of Expression to the stream hStream.
- If hStream is not specified, then the standard output is used.
- The standard output can be redirected by the OUTPUT TO instruction.
- You can convert the expressions to strings by using the Str$ function.
- If there is no semicolon or comma after the last expression, then an end-of-line character is inserted after the last expression. The end-of-line character can be defined with the property Stream.EndOfLine.
- If the semicolon is doubled, a space is inserted between the expressions.
- If a comma is used instead of a semicolon, a tab character (ASCII code 9) is inserted to separate the expressions.
6.2.0.7 READ
(a) Variable = READ [ # rStream ] AS Datatype (b) Variable = READ [ # rStream , ] iLength
(a) Read data of a specified data type *. * The first syntax reads the stream as a data stream whose type is specified by Datatype.
- If the stream rStream is not specified, the standard input is used.
- The returned datatype can be one of the following types: NULL, Boolean, Byte, Short, Integer, Long, Pointer, Single, Float, Date, String, Variant, any Array, Collection or Structure.
- When reading a string, the length of the string must precede the string content in the stream data.
- If the stream content cannot be read, an error is raised.
Example
If an array was written serialised into a (file) stream via Write, the array can be read from it with Read:
Public hFile As File Public aNames As String[] ... ' Data import If Dialog.OpenFile() Then Return hFile = Open Dialog.Path For Read aNames = Read #hFile As ARRAY Close #hFile
(b) Read the contents of a stream
- The second syntax reads from the stream rStream a number of bytes specified by the Length argument and returns it as a string.
- If iLength is negative, then at most (-Length) bytes are read until the end of the stream.
- If the stream is not specified, the standard input is used.
Example
Public sTemperatureDigit As String hRS232 = New SerialPort As "hRS232" Public Sub hRS232_Read() sTemperatureDigit = Read #hRS232, Lof(hRS232) End
6.2.0.8 LINE INPUT
LINE INPUT [ # hStream , ] Variable
Reads a line of text from the text stream into a string variable.
If the stream hStream is not specified, then the standard input is used. The complete line is always read - with the exception of the end-of-line character. By default, it is the constant gb.Unix, which represents a single Chr$(10) character. The end-of-line character can be defined with the Stream.EndOfLine property.
- Do not use LINE INPUT … to read from binary files, as you are not using them for a line structure. Use READ instead.
- Do not use this command inside the Read event of a process that is not necessarily sending a new line because it is issuing a prompt, for example. It will block forever and wait for the line break. For example, the shell sends a command prompt to signal that it is ready to receive a new command. This prompt is usually not terminated by a NewLine, which is why a programme - if it uses LINE INPUT - cannot read this prompt. The implied readiness of the shell to continue is lost.
Example 1
Public Sub AddTextToFile(FilePath As String, Text As String) Dim hFile As File Try hFile = Open FilePath For Append Print #hFile, Text Finally If Exist(FilePath) Then Close #hFile Catch Message.Error("Error:\n" & Error.Text & " in " & Error.Where) End Public Function GetTextFromFile(FilePath As String) As String Dim hFile As File Dim sLine, Text As String Try hFile = Open FilePath For Read While Not Eof(hFile) Line Input #hFile, sLine Text &= sLine & "\n" Wend Return Text Finally If Exist(FilePath) Then Close #hFile Catch Message.Error("Error:\n" & Error.Text & " in " & Error.Where) End Public Sub btnAddTextToFile_Click() Dim sLogDir As String sLogDir = Desktop.DataDir &/ "gambasbook" &/ Application.Name If Not Exist(sLogDir) Then Shell.MkDir(sLogDir) AddTextToFile(sLogDir &/ "rs232.log", "Time = " & Format(Now, "hh:nn:ss")) Catch Message.Error("Error:\n" & Error.Text & " in " & Error.Where) End Public Sub btnGetTextFromFile_Click() Dim sLogPath As String sLogPath = Desktop.DataDir &/ "gambasbook" &/ Application.Name &/ "rs232.log" txaLog.Text = GetTextFromFile(sLogPath) End
Example 2
Characters are read in via standard input (terminal) to control the MediaPlayer. Notes can be found at: http://www.mplayerhq.hu/DOCS/man/de/mplayer.1.html.
' Gambas module file Public mPlayer As New MediaPlayer Public Sub Main() If mPlayer Then mPlayer = Null Start() End Public Sub Start() mPlayer = New MediaPlayer mPlayer.URL = "http://mp3channels.webradio.rockantenne.de/classic-perlen" mPlayer.Play() mPlayer.Audio.Volume = 1.0 Print #File.Out, "" Print #File.Out, "-------------------------------------" Print #File.Out, "Instructions for use" Print #File.Out, "-------------------------------------" Print #File.Out, "+ ▶ Audio.Volume ▲" Print #File.Out, "- ▶ Audio.Volume ▼" Print #File.Out, "p ▶ Player.Pause" Print #File.Out, "r ▶ Player.Run (After a pause)" Print #File.Out, "m ▶ Audio.Mute (off/on)" Print #File.Out, "q ▶ Player.Stop" Print #File.Out, "-------------------------------------" Print #File.Out, "Each command is followed by <ENTER>." Print #File.Out, "-------------------------------------" Print #File.Out, "" End Public Sub Application_Read() Dim sInput As String Dim fDeltaVolume As Float If mPlayer.Audio.Volume > 1.1 Then fDeltaVolume = 1.0 Else fDeltaVolume = 0.1 Endif Line Input #File.In, sInput Select Case sInput Case "q" mPlayer.Stop() Quit Case "p" mPlayer.Pause() Case "r" ' run mPlayer.Play() Case "m" ' toggle switch: mute on/mute off mPlayer.Audio.Mute = Not mPlayer.Audio.Mute Case "+" If mPlayer.Audio.Volume > 0.09 And mPlayer.Audio.Volume < 9.0 Then mPlayer.Audio.Volume += fDeltaVolume Endif Case "-" If mPlayer.Audio.Volume > 0.2 And mPlayer.Audio.Volume < 10.0 Then mPlayer.Audio.Volume -= fDeltaVolume Endif End Select End
In this example, characters are output to standard output (File.Out) and read from standard input (File.In). The standard error output is not used.
6.2.0.9 INPUT FROM STREAM
INPUT FROM sStream
- Redirects the standard input to the stream sStream.
- The standard input is used by INPUT, READ, LINE INPUT, Eof and Lof if you do not specify a stream argument.
- Calls to this instruction can be nested.
6.2.0.10 INPUT FROM DEFAULT
INPUT FROM DEFAULT
Redirects the standard input to the state before the last redirection.
6.2.0.11 OUTPUT TO STREAM
OUTPUT TO sStream
- Redirects the standard output to the stream sStream.
- The standard output is used by PRINT and WRITE if you do not specify a stream argument.
- Calls to this statement can be nested.
6.2.0.12 OUTPUT TO DEFAULT
OUTPUT TO DEFAULT
Redirects the standard output to the state before the last redirection.
6.2.0.13 ERROR TO STREAM
ERROR TO eStream
Redirects the standard error output to the stream eStream. The standard error output is used by the ERROR and DEBUG instructions. Calls to the instruction can be nested.
6.2.0.14 ERROR TO DEFAULT
ERROR TO DEFAULT
Redirects the standard output to the state before the last redirection.
6.2.0.15 SEEK
SEEK [ # ] hStream , iPosition
Positions the stream pointer for the next read/write. If iPosition is negative, then the stream pointer is moved to a location relative to the end of the file. To move the stream pointer to the end of a file, you must use the Lof(hStream) function.
Example
A text file is read in different ways. After variant 1, the stream pointer is set back to the beginning (position 0) and the file is read out completely again after a second variant:
hFile = Open $sCurrentFilePath For Input ' Variant 1 hFile = Open $sCurrentFilePath For Input While Not Eof(hFile) Line Input #hFile, sLine sContent = sContent & sLine & gb.NewLine Wend sContent = sContent & gb.NewLine ' Variant 2 seek">Seek #hFile, 0 For Each sLine In hFile.Lines sContent = sContent & sLine & gb.Lf Next sContent = sContent & gb.NewLine
6.2.0.16 Seek
iPosition = Seek ( Stream )
Returns the current value of the stream pointer of the specified stream. The return value iPosition is an integer number of type Long. Note: Many stream types, such as process or socket, do not have a stream pointer.
6.2.0.17 LOCK
hStream = LOCK sPath
Use LOCK and the specified path sPath to achieve a system-wide stream lock. If the specified stream is already locked by another process (advisory look), the command will fail. You can unlock a locked stream with the command UNLOCK.
Example
To safely prevent a second programme start, you can place a system-wide lock on a (pseudo) file. If another programme is started, an error is triggered in (YX) because a lock already exists. Do not forget to release the lock when you exit the programme.
' Gambas class file Public hLockFile As File Public sFilePath As String Public Sub _new() sFilePath = Desktop.DataDir &/ "lock.lock" Try hLockFile = Lock sFilePath ' <--- Step 1 (XY) If Error Then Message.Warning(Subst("&1 '&2' &3", ("There is already an instance of"), Application.Name, "!")) FMain.Close() Endif End ' Main program ... Public Sub Form_Close() Try Unlock hLockFile ' <--- Step 2 FMain.Close() End
6.2.0.18 LOCK WAIT
Stream = LOCK Path fWait Delay
- Locks a stream for a specified time fWait.
- The delay is specified in seconds. The type of fWait is Float.
- If the delay has passed before the lock is acquired, the command fails - an error is raised.
6.2.0.19 UNLOCK
UNLOCK [ # ] Stream
Unlocks a stream previously locked by a LOCK command. Closing the stream also automatically unlocks the stream.
6.2.0.20 EOF (end of file)
Result = Eof ( [ hStream AS Stream ] ) AS Boolean
The function returns TRUE when the end of the stream has been reached.
If hStream is not specified, the standard input is used.The behaviour of Eof() depends on the stream blocking mode: (1) If the stream is in non-blocking mode, then Eof() returns the function value True if at least one byte can be read from the stream. (2) If, on the other hand, the stream is in blocking mode, Eof() first waits for data before checking whether anything can be read.
hFile = Open $sCurrentFilePath For Input
While Not Eof(hFile)
Line Input #hFile, sLine
sContent = sContent & sLine & gb.NewLine
Wend
6.2.0.21 LOF (length of file)
Length = Lof ( hStream AS Stream ) AS Long
- Returns the length of the open stream hStream if hStream is a regular file.
- On the other hand, if hStream is not a regular file, such as a socket, then the number of bytes that can be read simultaneously is returned.
6.2.0.22 FLUSH
FLUSH [ [ # ] Stream ]
The data of a buffered stream is output immediately - the buffer is emptied. If no stream is specified, the data of any open stream is output. However, you cannot use the flush instruction to request data from another process if you want to read its data from a stream.
Example
The prompt “Enter your name: ” is to be displayed in the terminal and the cursor is to remain - as usual - in the same line for entering the answer. This is accomplished by the double semicolon after the print instruction. It outputs a space and suppresses the line break that Print normally appends to the output. Now, however, terminals are line-buffered. This means that the Gambas programme receives the printed string and buffers it until it reads the next end-of-line character. Only then is the data sent to the terminal. Since the line break is missing after the print instruction in *** due to the double semicolon, you would not see a prompt in the terminal (without flush), although the Gambas programme is already at the line input instruction and waiting for input. Why don't you try it without flush and just type your name blindly. It will work, but you won't see the prompt until after the second print instruction, which returns an end-of-line! With Flush you tell Gambas to print the data immediately. This will make the programme work as expected.
' Gambas module file Public Sub Main() Dim sName As String Print "Enter your name:";; ' *** Flush Line Input sName Print "Good to know that you are " & sName; Print End
Programme start in the project directory:
hans@mint-183 ~/GB3BUCH/6K_Stream/6.2.0_Stream-Input-Output-Funktionen/Projekte/Flush $ gbr3 flush.gambas Enter your name: Mister Red Good to know that you are Mister Red
6.2.0.23 NAMED PIPE
hStream = PIPE sPipeName FOR [ READ ] [ WRITE ] [ WATCH ] hStream = OPEN PIPE sPipeName FOR [ READ ] [ WRITE ] [ WATCH ]
- Opens a named pipe for reading, writing or both.
- If the named pipe does not exist, it is created automatically.
- If the named pipe was opened successfully, a stream object is returned to the variable hStream.
A detailed description of Named Pipes can be found in Chapter 6.2.2.
