Skip to main content
Engineering LibreTexts

10.4: Using Streams for File Access

  • Page ID
    36387
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    You have already seen how to stream over collections of elements. It’s also possible to stream over files on your hard disk. Once created, a stream on a file is really like a stream on a collection: you will be able to use the same protocol to read, write or position the stream. The main difference appears in the creation of the stream. There are several different ways to create file streams, as we shall now see.

    Creating file streams

    To create file streams, you will have to use one of the following instance creation methods offered by the class FileStream:

    • fileNamed: Open a file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory.
    • newFileNamed: Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, ask the user what to do.
    • forceNewFileNamed: Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, delete it without asking before creating the new file.
    • oldFileNamed: Open an existing file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory.
    • readOnlyFileNamed: Open an existing file with the given name for reading.

    You have to remember that each time you open a stream on a file, you have to close it too. This is done through the close method.

    stream := FileStream forceNewFileNamed: 'test.txt'.
    stream
        nextPutAll: 'This text is written in a file named ';
        print: stream localName.
    stream close.
    
    stream := FileStream readOnlyFileNamed: 'test.txt'.
    stream contents.        →        'This text is written in a file named ''test.txt'''
    stream close.
    

    The method localName answers the last component of the name of the file. You can also access the full path name using the method fullName.

    You will soon notice that manually closing the file stream is painful and error-prone. That’s why FileStream offers a message called forceNewFileNamed:do: to automatically close a new stream after evaluating a block that sets its contents.

    FileStream
        forceNewFileNamed: 'test.txt'
        do: [:stream |
            stream
                nextPutAll: 'This text is written in a file named ';
                print: stream localName].
    string := FileStream
        readOnlyFileNamed: 'test.txt'
        do: [:stream | stream contents].
    string        →    'This text is written in a file named ''test.txt'''
    

    The stream-creation methods that take a block as an argument first create a stream on a file, then execute the block with the stream as an argument, and finally close the stream. These methods return what is returned by the block, which is to say, the value of the last expression in the block. This is used in the previous example to get the content of the file and put it in the variable string.

    Binary streams

    By default, created streams are text-based which means you will read and write characters. If your stream must be binary, you have to send the message binary to your stream.

    When your stream is in binary mode, you can only write numbers from 0 to 255 (1 Byte). If you want to use nextPutAll: to write more than one number at a time, you have to pass a ByteArray as argument.

    FileStream
        forceNewFileNamed: 'test.bin'
        do: [:stream |
            stream
                binary;
                nextPutAll: #(145 250 139 98) asByteArray].
    
    FileStream
        readOnlyFileNamed: 'test.bin'
        do: [:stream |
            stream binary.
            stream size.        →    4
            stream next.        →    145
            stream upToEnd.     →    a ByteArray(250 139 98)
            ].
    

    Here is another example which creates a picture in a file named “test.pgm” (portable graymap file format). You can open this file with your favorite drawing program.

    FileStream
        forceNewFileNamed: 'test.pgm'
        do: [:stream |
            stream
                nextPutAll: 'P5'; cr;
                nextPutAll: '4 4'; cr;
                nextPutAll: '255'; cr;
                binary;
                nextPutAll: #(255 0 255 0) asByteArray;
                nextPutAll: #(0 255 0 255) asByteArray;
                nextPutAll: #(255 0 255 0) asByteArray;
                nextPutAll: #(0 255 0 255) asByteArray
            ]
    

    This creates a 4x4 checkerboard as shown in Figure \(\PageIndex{1}\).

    A 4x4 checkerboard you can draw using binary streams.
    Figure \(\PageIndex{1}\): A 4x4 checkerboard you can draw using binary streams.

    This page titled 10.4: Using Streams for File Access is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Andrew P. Black, Stéphane Ducasse, Oscar Nierstrasz, Damien Pollet via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.