[Newbies] Oponing a file twice

David T. Lewis lewis at mail.msen.com
Thu Jul 5 13:58:36 UTC 2007


Hi John,

On Thu, Jul 05, 2007 at 10:53:14PM +1000, johnps11 at bigpond.com wrote:
> Just as I was thinking I was getting a handle on this I'm terribly
> confused...
> 
> a _ FileStream fileNamed: 'readme.txt'.
> 
> gives me a stream an  the file.
> 
> b _ FileStream fileNamed: 'readme.txt'.
> 
> gives me nil

This is because you have tried to open two file streams on the same
file, and you are using Windows. The nil result just means that the
file open failed the second time because Windows will not let you
open the file twice. If you had closed the first file stream
("a close"), then the second one would have succeeded, and your
b variable would have pointed to a file stream.

> a _ nil.
> a _ FileStream fileNamed: 'readme.txt'.
> 
> a is still nil

This is because that first file stream (the one that used to be
associated with your "a" variable) still exists. It has not yet been
garbage collected, and it also is still connected to an open Windows
file. Therefore the operating system will still refuse to let you open
the file a second time.

> It seems that the only way I can reopen this file is to
> 
> Smalltalk garbageCollect.

This cleans up that "orphaned" instance of the file stream. It just
so happens that file streams are set up with a finalization method that
is automatically invoked as the instance is being collected, and the
finalization method closes the open Windows file handle. This is meant
as a safety net to protect against the sort of programming error in
which someone opens a file, but forgets to close it properly. Without
the finalization method, file handles could accumulated indefinitely,
eventually causing problems (the actual effect of this will vary from
one operating system to another).

> Now if I simply wanted to have two references to the file I could just
> 
> b _ a copy.
> 
> But what if I open a stream on a file, then reuse my variable, then
> sometime later want to use that file again? Do I have to explicitly
> perform garbage collection? That seems very wrong.  Do I have to keep a
> hold of all references to files and their filenames if I think I _might_
> need that file again?  That also seems very wrong.

Just keep track of one actual object that refers to the open file. Don't
make copies of it, and do remember to close it when you a finished using
the file. If you need to reuse your variable, assign the file stream
object to some other variable so you don't lose track of it.

> Scenario:
> 
> ...
> some code
> a _ FileStream fileNamed: 'readme.txt'.
> some more code
> ...
> a _ result of something
> ...
> even more code
> b _ FileStream fileNamed: 'readme.txt'.
> 
> I've looked at FileStream, StandardFileStream and MultiByteFileStream for
> 2 days now and am none the wiser (not quite true, I've learned you can
> send Squeak into lala land by putting 'self halt' inside
> FileStream>>fileNamed: and then getting any error at all... the debugger
> seems to open a stream that opens a debugger that opens a stream that
> opens a debugger etc etc)
> 
> What am I missing (I assume I'm overlooking something really simple)?

You probably would be better off to not use the "a" variable for two different
purposes. It's also a good idea to use longer, more descriptive variable
names. Possibly something more like this:

readmeFile := FileStream fileNamed: 'readme.txt'.

Then don't use that variable for anything other than refering to the
readme file stream. Then later on, do something like this:

readmeFile ifNotNilDo: [readmeFile close].

(Note: I'm using ":=" rather than "_" for assigning to a variable.
Either one will work, but in the last couple of years, folks have
been moving to the convention of using ":=" instead of "_").

As a side note, there is a method called #ensure: that can be used to
ensure that a file gets closed even if things go wrong in your code.
This should probably be the topic of a separate discussion, but the
basic idea is to write something like this:

readmeFile := FileStream fileNamed: 'readme.txt'.
[
  "Do stuff with readmeFile here"
] ensure: [readmeFile ifNotNil: [readmeFile close]].

Or another way to write this is:

(FileStream fileNamed: 'readme.txt') ifNotNilDo:
  [:readmeFile |
  [
    "Do stuff with readmeFile here"
  ] ensure: [readmeFile close]].

HTH,
Dave



More information about the Beginners mailing list