Usually with my output I am writing it to the Transcript with...
Transcript show:
How does one write the output to a file instead?
You want to use a FileStream
See this link describing FileStreams
Excerpt below:
FileStream
FileStreams support all of ExternalStreams protocol. They can be created to read, write, readWrite or append from/to a file.
Creation:
* for reading:
aStream := FileStream readonlyFileNamed:aFilenameString
* to read/write an existing file:
aStream := FileStream oldFileNamed:aFilenameString
* to create a new file for writing:
aStream := FileStream newFileNamed:aFilenameString
The above was the internal low level instance creation protocol, which is somewhat politically incorrect to use. For portability, please use the companion class Filename to create fileStreams:
* for reading:
aStream := aFilenameString asFilename readStream
* to read/write an existing file:
aStream := aFilenameString asFilename readWriteStream
* to create a new file for writing:
aStream := aFilenameString asFilename writeStream
* to append to an existing file:
aStream := aFilenameString asFilename appendingWriteStream
| fileName aStream |
fileName := (Filename named: 'stream.st').
aStream := fileName readAppendStream.
aStream nextPutAll: 'What is the best class I have ever taken?'.
aStream cr.
aStream flush.
aStream nextPutAll: 'It is the VisualWorks Intro class!'.
aStream close.
And then of course don't forget to handle the character encoding you want, if you're not writing binary or the default encoding. In Pharo/Squeak, set the converter to the needed TextConverter subclass.
Related
has anyone had problems with the use of the pascal version of the file 'ShapefileII.pas'?
The OSGEO maintain a DLL to manage shapefile and DBF associated file (https://github.com/OSGeo/shapelib).
I'm trying to create a shp file and the associated .DBF file using Delphi-10.3-Rio. I can create the SHP and DBF file, but I can not add/updade string field data to the DBF file.
I used the steps described in the example file 'dbfadd.c' using the function 'DBFWriteStringAttribute' and 'DBFWriteIntegerAttribute'.
The function does not return any errors, but the string field in DBF file is not updated.
I know that is related to differences PChar C++ and PAnsichar in Delphi 10.3 code.
The Delphi equivalent from C++ is:
function DBFWriteIntegerAttribute(hDBF: DBFHandle;
iShape: LongInt; iField: LongInt; nFieldValue: LongInt): LongInt; cdecl;
function DBFWriteStringAttribute(hDBF: DBFHandle; iShape: LongInt; iField: LongInt; pszFieldValue: PAnsiChar{PChar}): LongInt; cdecl;
//c++ DLL code
{
function DBFWriteIntegerAttribute(hDBF: DBFHandle;iShape: LongInt; iField: LongInt; nFieldValue: LongInt): LongInt; cdecl;
DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField,
const char * pszFieldValue );
int}
Please, can anyone help me?
Here my code:
//open dbf file and add/update data
FDBFHandle := DBFOpen(pAnsichar(FileNameDBF), pAnsichar('r+b')); //open a DBF file created
if FDBFHandle = nil then
exit;
iRecord:= DBFGetRecordCount(FDBFHandle); // get record number
DBFWriteIntegerAttribute(FDBFHandle, iRecord, 0, 99 ); //works fine update first field
DBFWriteStringAttribute (FDBFHandle, iRecord, 1, PAnsichar('TEST' ));//NOT WORK BUT RETURN TRUE AND SAVE VALUE '0' TO FIELD
//Using PAnsistring also not work
DBFClose(FDBFHandle);
Solved. Sorry, I found that the problem was due to procedure DBFCreate and not due to DBFWriteStringAttribute. The field was created as an Integer and not as a string. So, the DBFWriteStringAttribute save 0 value.
I'm trying to go though a array and add characters from that array to another object. The problem is I keep getting a error "Instances of character are not indexable". However when I run tag := tag,char outside of the do block then it works.
|data startTag tag|.
data := '123456778'
startTag := false.
tag := ''.
data asArray do: [:char |
tag := tag,char]
The , is defined as
Collection>>, aCollection
^self copy addAll: aCollection; yourself
so that tries to operate on your single character as if it were a collection. That explains the error.
For larger collections you do not want to build up using , because of the copy that happens each time. Therefore use the streaming protocol:
|data tag|
data := '123456778'.
tag := String streamContents: [:s |
data do: [ :char |
s nextPut: char]]
Also take a look at Collection>>do:separatedBy: to add separators between your data.
[edit] Ah, ok, that's something like
|data tag tags state|
data := '<html>bla 12 <h1/></html>'.
state := #outside.
tags := OrderedCollection new.
tag := ''.
data do: [ :char |
state = #outside ifTrue: [
char = $< ifTrue: [
state := #inside.
tag := '' ]]
ifFalse: [
char = $> ifTrue: [
state := #outside.
tags add: tag]
ifFalse: [ tag := tag, (char asString)]]].
tags
"an OrderedCollection('html' 'h1/' '/html')"
I am having trouble regarding Smalltalk. I am attempting to populate an array with the numbers that are read from the file, but it doesn't seem to work. I've tried numerous options and I was hoping someone would explain to me what I'm doing wrong.
Object subclass: #MyStack
instanceVariableNames:'anArray aStack'
classVariableNames:''
poolDictionaries:''
!
MyStack class comment: 'Creates a Stack Class.'
!
!
MyStack methodsFor: 'initialize Stack'
!
new "instance creation"
^ super new.
!
init "initialization"
anArray := Array new: 32.
aStack := 0.
! !
!MyStack methodsFor: 'methods for stacks' !
pop "Removes the top entry from the stack"
| item |
item := anArray at: aStack.
aStack := aStack - 1.
!
push: x "Pushes a new entry onto the stack"
aStack := aStack + 1.
anArray at:aStack put:x.
!
top "Returns the current top of the stack"
^anArray at: aStack.
!
empty "True if the stack is empty"
^aStack = 0.
!
full "True if the stack is full"
^aStack = 32.
!
printOn: aStream "Prints entire stack one entry per line, starting the top entry"
aStream show: 'Stack:'.
aStack to:1 by:-1 do:[:i |(anArray at:i) printOn:aStream. ].
aStream show: ''
! !
"----------------------------------------------------------------------------------"
Object subclass: #IOExample
instanceVariableNames: 'input output'
classVariableNames: ''
poolDictionaries: ''
!
IOExample class comment: '
basic I/O.
'
!
!
IOExample methodsFor: 'initialize'
!
new
^ super new.
!
init
[ input := FileSelectionBrowser open asFilename readStream. ]
on: Error
do: [ :exception |
Dialog warn: 'Unable to open file'.
exception retry.
].
[ output := FileSelectionBrowser open asFilename writeStream. ]
on: Error
do: [ :exception |
Dialog warn: 'Unable to open file'.
exception retry.
].
! !
!
IOExample methodsFor: 'copy input to output turning :: into :'
!
copy
| data lookAhead theStack myStack|
[ input atEnd ] whileFalse: [
data := input next.
(data isKindOf: Integer)
ifTrue: [
(input atEnd) ifFalse: [
"myStack push: data."
lookAhead = input peek.
(lookAhead asCharacter isDigit)
ifTrue: [
]
].
].
output show: myStack.
].
input close.
output close.
! !
Did you try to run this code? If you did, I'm surprised you didn't get a compilation warning due to #2 below.
There are a number of problems in #copy (besides the fact that I don't understand exactly what it's trying to do)...
First you seems to expect the data to be numbers: data isKindOf: Integer. But then later you treat it as a stream of Characters: lookAhead asCharacter isDigit. If the first condition is true to get you past that point, the second one never can be, as you would've matched [0-9], which aren't ASCII values for digits.
lookAhead = input peek. Here you're comparing uninitialized lookAhead (nil) with the peeked value, and then throwing away the result. I assume you meant lookAhead := input peek.
Then there is the empty inner condition ifTrue: [ ]. What are you trying to do there?
Then there's the odd protocol name, 'copy input to output turning :: into :'. What does that mean, and what does that have to do with copying numbers between streams?
Justin, let me try to help you with the class MyStack and defer to another answer any comments on your example.
I've divided your code into fragments and appended my comments.
Fragment A:
Object subclass: #MyStack
instanceVariableNames:'anArray aStack'
classVariableNames:''
poolDictionaries:''
Comments for A:
A Smalltalker would have used instance variable names without indeterminate articles a or an
Object subclass: #MyStack
instanceVariableNames:'array stack'
classVariableNames:''
poolDictionaries:''
Fragment B:
MyStack class comment: 'Creates a Stack Class.'
Comments for B:
This is weird. I would have expected this instead (with no class):
MyStack comment: 'Creates a Stack Class.'
Fragment C:
MyStack methodsFor: 'initialize Stack'
new "instance creation"
^ super new.
Comments for C:*
This code puts new on the instance side of the class, which makes no sense because you usually send new to the class rather than its instances. The correct form requires adding class:
MyStack class methodsFor: 'initialize Stack'
new
^super new.
You forgot to send the initialization method (however, see Fragment D below)
new
^super new init.
Fragment D:
init "initialization"
anArray := Array new: 32.
aStack := 0.
Comments for D:
In Smalltalk people use the selector initialize so it can send super first
initialize
super initialize.
array := Array new: 32.
stack := 0.
Note that this change would require also writing new as
new
^super new initialize.
However, if your dialect already sends the initialize method by default, you should remove the implementation of new from your class.
Fragment E:
pop "Removes the top entry from the stack"
| item |
item := anArray at: aStack.
aStack := aStack - 1.
Comments for E:
You forgot to answer the item just popped out
pop
| item |
item := array at: stack.
stack := stack - 1.
^item
Fragment F:
push: x "Pushes a new entry onto the stack"
aStack := aStack + 1.
anArray at:aStack put:x.
Comments for F:
This is ok. Note however that the stack will refuse to push any item beyond the limit of 32.
push: x
stack := stack + 1.
array at: stack put: x.
Fragment G:
top "Returns the current top of the stack"
^anArray at: aStack.
empty "True if the stack is empty"
^aStack = 0.
full "True if the stack is full"
^aStack = 32.
Comments for G:
These are ok too. However, a more appropraite name for empty would have been isEmpty because all collections understand this polymorphic message. Similarly, the recommended selector for full would be isFull:
top
^array at: aStack.
isEmpty
^stack = 0.
isFull
^stack = 32.
Note also that isFull repeats the magic constant 32, which you used in the initialization code. That's not a good idea because if you change your mind in the future and decide to change 32 with, say, 64 you will have to modify two methods an not just one. You can eliminate this duplication in this way
isFull
^stack = array size.
Fragment H:
printOn: aStream
"Prints entire stack one entry per line, starting the top entry"
aStream show: 'Stack:'.
aStack to:1 by:-1 do:[:i |(anArray at:i) printOn:aStream. ].
aStream show: ''
Comments for H:
The last line of this code is superfluous and I would get rid of it. However, you may want to separate every item from the next with a space
printOn: aStream
stream show: 'Stack:'.
stack to: 1 by: -1 do:[:i |
aStream space.
(array at: i) printOn: aStream].
I' m reading large process output data in Lazarus using the TProcess and the suggestions from this freepascal wiki page.
The wiki page suggests to create a loop to read the process output data like this:
// ... If you want to read output from an external process, this is the code you should adapt for production use.
while True do
begin
MemStream.SetSize(BytesRead + 2024); // make sure we have room
NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
if NumBytes > 0
then begin
Inc(BytesRead, NumBytes);
Write('.') //Output progress to screen.
end else
BREAK // Program has finished execution.
end;
// "Then read the MemStream to do your job"
The wiki page also mentions that the calling program should read from the output pipe to prevent it from getting full.
So, how much data makes the output pipe full?
Why we should use a MemStream (TMemoryStream) and not directly read from OurProcess.Output stream (using the bytesAvailable, etc) in the above loop?
I'm reading 80MB of wav data from a process and I have noticed that both MemStream and OurProcess.Output streams have the same amount of data! The memory usage gets doubled. So the suggested method from the wiki cannot be considered as efficient or optimized. Or there is something I'm missing?
Afaik output/input streams are a stream form of a pipe, not memory streams. The values you see are retrieved from the OS handle, not from memory allocated to the FPC app per se.
It is just like you can ask for the .size of a file on disk without reading the whole file.
procedure RunExternalAppInMemo(DosApp:String;AMemo:TMemo);
const READ_BYTES = 2048;
var
aProcess: TProcess; //TProcess is crossplatform is best way
MemStream: TMemoryStream;
NumBytes: LongInt;
BytesRead: LongInt;
Lines: TStringList;
begin
// A temp Memorystream is used to buffer the output
MemStream := TMemoryStream.Create;
Lines :=TStringList.Create;
BytesRead := 0;
aProcess := TProcess.Create(nil);
aProcess.CommandLine := DosApp;
aprocess.ShowWindow := swoHIDE;
AProcess.Options := AProcess.Options + [poUsePipes];
aProcess.Execute;
while aProcess.Running do
begin
// make sure we have room
MemStream.SetSize(BytesRead + READ_BYTES);
// try reading it
NumBytes := aProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
if NumBytes > 0 // All read() calls will block, except the final one.
then Inc(BytesRead, NumBytes)
else
BREAK // Program has finished execution.
end;
MemStream.SetSize(BytesRead);
Lines.LoadFromStream(MemStream);
AMemo.lines.AddStrings(Lines);
aProcess.Free;
Lines.Free;
MemStream.Free;
end;
I was dealing with this problem today, I've modified Georgescu answer, as I wanted Memo to display output stream on the fly
procedure RunExternalAppInMemo(DosApp:String;AMemo:TMemo);
const READ_BYTES = 2048;
var
aProcess: TProcess; //TProcess is crossplatform is best way
NumBytes: LongInt;
Buffer: array of byte;
begin
// set the size of your buffer
SetLength(Buffer,READ_BYTES);
aProcess := TProcess.Create(nil);
aProcess.CommandLine := DosApp;
aprocess.ShowWindow := swoHIDE;
AProcess.Options := AProcess.Options + [poUsePipes];
aProcess.Execute;
while aProcess.Running do
begin
// try reading it
NumBytes := aProcess.Output.Read(Buffer[0], length(buffer)*sizeof(byte)); // I usually do it that way, so I can change Buffer size on if needed
AProcess.Suspend; //I have no experience with pipes, but it seems way I won loose eny output?
if NumBytes > 0 then // All read() calls will block, except the final one.
begin
AMemo.Lines.Add(Pchar(Buffer);
application.ProcessMessages;
AProcess.Resume;
end
else
BREAK; // Program has finished execution.
end;
setlength(Buffer,0);
aProcess.Free;
end;
Here is basically what I want to do (I have implemented the Stream sub-class WordStream):
| aStream |
aStream := WordStream on: 'Test My Word Stream Class!'.
self assert: (aStream next) = 'Test'.
self assert: (aStream next) = 'My'.
self assert: (aStream next) = 'Word'.
self assert: (aStream next) = 'Stream'.
self assert: (aStream next) = 'Class'.
self assert: aStream atEnd
I've got everything stored correctly in my stream, but I can only figure out how to get the next word by doing:
next
|tmpStream|
tmpStream := ReadStream on: myStream contents.
^tmpStream nextDelimited: Character space.
This works for only the first word (obviously) - when I try to use my instance variable myStream, it just keeps returning ''.
i.e., I can't get this to work:
next
^myStream nextDelimited: Character space.
Could someone give me a hand?
edit:
This is how I implemented on:
on: inString
|tmpStream|
myStream := ReadWriteStream on: String new.
tmpStream := ReadStream on: inString.
[ tmpStream atEnd ] whileFalse: [ myStream nextPutAll: (tmpStream nextDelimited: Character space); nextPut: $ ].
PositionableStream defines #nextDelimited: already, but that uses #next. One fairly horrible thing you could do is copy PositionableStream >> #nextDelimited:'s implementation, and replace the self next call with your own get-a-single-character method.