class IO::Handle
class IO::Handle { }
Instances of IO::Handle
encapsulate a handle to manipulate input/output
resources. Usually there is no need to create directly an IO::Handle
instance, since it will be done by other roles and methods. For instance, an
IO::Path object provides an open method that
returns an IO::Handle
:
my $fh = '/tmp/log.txt'.IO.open;
say $fh.^name; # OUTPUT: IO::Handle
The first line is pretty much equivalent to the following piece of code:
my $fh = IO::Handle.new( :path( '/tmp/log.txt'.IO.path ) ).open;
Methods
method open
method open(IO::Handle:D:
:$r, :$w, :$x, :$a, :$update,
:$rw, :$rx, :$ra,
:$mode is copy,
:$create is copy,
:$append is copy,
:$truncate is copy,
:$exclusive is copy,
:$bin,
:$enc is copy,
:$chomp = $!chomp,
:$nl-in is copy = $!nl-in,
Str:D :$nl-out is copy = $!nl-out,
:$out-buffer is copy,
)
Opens the handle in one of the modes. Fails with appropriate exception if the open fails.
See description of individual methods for the accepted values and behavior of
:$chomp,
:$nl-in,
:$nl-out, and
:$enc. The values for parameters default
to the invocant's attributes and if any of them are provided, the attributes
will be updated to the new values. Specify :$bin
set to True
instead of
:$enc
to indicate the handle should be opened in binary mode. Specifying
undefined value as :$enc
is equivalent to not specifying :$enc
at all.
Specifying both a defined encoding as :$enc
and :$bin
set to true will
cause X::IO::BinaryAndEncoding
exception to be thrown.
The open mode defaults to non-exclusive, read only (same as specifying
:r
) and can be controlled by a mix of the following arguments:
:r same as specifying :mode<ro> same as specifying nothing
:w same as specifying :mode<wo>, :create, :truncate
:a same as specifying :mode<wo>, :create, :append
:x same as specifying :mode<wo>, :create, :exclusive
:update same as specifying :mode<rw>
:rw same as specifying :mode<rw>, :create
:ra same as specifying :mode<rw>, :create, :append
:rx same as specifying :mode<rw>, :create, :exclusive
Argument :r
along with :w
, :a
, :x
are exactly the same as the
combination of both letters, shown in the three last rows in the table above.
Support for combinations of modes other than what is listed above
is implementation-dependent and should be assumed unsupported. That is,
specifying, for example, .open(:r :create)
or
.open(:mode<wo> :append :truncate)
might work or might cause the Universe
to implode, depending on a particular implementation. This applies to reads/writes
to a handle opened in such unsupported modes as well.
The mode details are:
:mode<ro> means "read only"
:mode<wo> means "write only"
:mode<rw> means "read and write"
:create means the file will be created, if it does not exist
:truncate means the file will be emptied, if it exists
:exclusive means .open will fail if the file already exists
:append means writes will be done at the end of file's current contents
Attempts to open a directory, write to a handle opened in read-only mode or read from a handle opened in write-only mode, or using text-reading methods on a handle opened in binary mode will fail or throw.
In 6.c language, it's possible to open path '-'
, which will cause
open
to open (if closed
) the $*IN
handle if opening in read-only
mode or to open the $*OUT
handle if opening in write-only mode. All other
modes in this case will result in exception being thrown.
As of 6.d language version, the use of path '-'
is deprecated and it will
be removed in future language versions entirely.
The :out-buffer
controls output buffering and by default behaves as if
it were Nil. See method out-buffer for details.
Note (Rakudo versions before 2017.09): Filehandles are NOT flushed or closed
when they go out of scope. While they will get closed when garbage
collected, garbage collection isn't guaranteed to get run. This means you
should use an explicit close
on handles opened for writing, to avoid data
loss, and an explicit close
is recommended on handles opened for reading
as well, so that your program does not open too many files at the same time,
triggering exceptions on further open
calls.
Note (Rakudo versions 2017.09 and after): Open filehandles are automatically
closed on program exit, but it is still highly recommended that you close
opened handles explicitly.
method comb
method comb(IO::Handle:D: Bool :$close, |args --> Seq:D)
Read the handle and processes its contents the same way
Str.comb does, taking the same arguments, closing
the handle when done if $close
is set to a true value. Implementations may
slurp the file in its entirety when this method is called.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = 'path/to/file'.IO.open;
say "The file has {+$fh.comb: '♥', :close} ♥s in it";
method chomp
has $.chomp is rw = True
One of the attributes that can be set via .new
or open. Defaults to
True
. Takes a Bool specifying whether the line separators (as defined by
.nl-in) should be removed from content when
using .get or
.lines methods.
routine get
method get(IO::Handle:D: --> Str:D)
multi get (IO::Handle $fh = $*ARGFILES --> Str:D)
Reads a single line of input from the handle, removing the trailing newline
characters (as set by .nl-in) if the handle's .chomp
attribute is set to True
. Returns Nil, if no more input is available. The
subroutine form defaults to
$*ARGFILES if no handle is
given.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
$*IN.get.say; # Read one line from the standard input
my $fh = open 'filename';
$fh.get.say; # Read one line from a file
$fh.close;
say get; # Read one line from $*ARGFILES
routine getc
method getc(IO::Handle:D: --> Str:D)
multi getc (IO::Handle $fh = $*ARGFILES --> Str:D)
Reads a single character from the input stream. Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown. The subroutine form defaults to $*ARGFILES if no handle is given. Returns Nil, if no more input is available, otherwise operation will block, waiting for at least one character to be available; these caveats apply:
Buffering terminals
Using getc to get a single keypress from a terminal will only work properly if you've set the terminal to "unbuffered". Otherwise the terminal will wait for the return key to be struck or the buffer to be filled up before the compiler gets even a single byte of data.
Waiting for potential combiners
If your handle's encoding allows combining characters to be read, raku will wait for more data to be available before it provides a character. This means that inputting an "e" followed by a combining acute will give you an e with an acute rather than giving an "e" and letting the next reading function give you a dangling combiner. However, it also means that when the user inputs just an "e" and has no intention to also input a combining acute, your program will be waiting for another keypress before the initial "e" is returned.
submethod DESTROY
submethod DESTROY(IO::Handle:D:)
Closes the filehandle, unless its native-descriptor is 2
or lower. This
ensures the standard filehandles do not get inadvertently closed.
Note that garbage collection is not guaranteed to happen, so you must NOT rely
on DESTROY
for closing the handles you write to and instead close them
yourself. Programs that open a lot of files should close the handles explicitly
as well, regardless of whether they were open for writing, since too many files
might get opened before garbage collection happens and the no longer used
handles get closed.
method gist
method gist(IO::Handle:D: --> Str:D)
Returns a string containing information which .path, if any, the handle is created for and whether it is .opened.
say IO::Handle.new; # IO::Handle<(Any)>(closed)
say "foo".IO.open; # IO::Handle<"foo".IO>(opened)
method eof
method eof(IO::Handle:D: --> Bool:D)
Non-blocking. Returns True
if the read operations have exhausted the
contents of the handle. For seekable handles, this means
current position is at or beyond the end of file and seeking
an exhausted handle back into the file's contents will result in
eof returning False
again.
On non-seekable handles and handles opened to zero-size
files (including special files in /proc/
), EOF won't be set
until a read operation fails to read any bytes. For example, in this code,
the first read
consumes all of the data, but it's only until the second
read
that reads nothing would the EOF on a TTY handle be set:
$ echo "x" | raku -e 'with $*IN { .read: 10000; .eof.say; .read: 10; .eof.say }'
False
True
method encoding
multi method encoding(IO::Handle:D: --> Str:D)
multi method encoding(IO::Handle:D: $enc --> Str:D)
Returns a Str representing the encoding currently used by
the handle, defaulting to "utf8"
. Nil indicates the filehandle is
currently in binary mode. Specifying an optional positional $enc
argument
switches the encoding used by the handle; specify Nil as encoding to put the
handle into binary mode.
The accepted values for encoding are case-insensitive. The available encodings vary by implementation and backend. On Rakudo MoarVM the following are supported:
utf8
utf16
utf16le
utf16be
utf8-c8
iso-8859-1
windows-1251
windows-1252
windows-932
ascii
The default encoding is utf8, which undergoes normalization into Unicode NFC
(normalization form canonical). In some cases you may want to ensure no
normalization is done; for this you can use utf8-c8
. Before using utf8-c8
please read Unicode: Filehandles and I/O
for more information on utf8-c8
and NFC.
As of Rakudo 2018.04 windows-932 is also supported which is a variant of ShiftJIS.
Implementation may choose to also provide support for aliases, e.g. Rakudo
allows aliases latin-1
for iso-8859-1
encoding and dashed utf versions:
utf-8
and utf-16
.
utf16, utf16le and utf16be
Unlike utf8, utf16 has an
endianness — either big endian or little endian. This relates to the ordering of
bytes. Computer CPUs also have an endianness. Raku's utf16
format specifier
will use the endianness of host system when encoding. When decoding it will look
for a byte order mark and if it is there use that to set the endianness. If there
is no byte order mark it will assume the file uses the same endianness as the host
system. A byte order mark is the codepoint U+FEFF which is ZERO WIDTH NO-BREAK
SPACE. On utf16
encoded files the standard states if it exists at the start
of a file it shall be interpreted as a byte order mark, not a U+FEFF codepoint.
While writing will cause a different file to be written on different endian systems, at the release of 2018.10 the byte order mark will be written out when writing a file and files created with the utf16 encoding will be able to be read on either big or little endian systems.
When using utf16be or utf16le encodings a byte order mark is not used. The endianness used is not affected by the host cpu type and is either big endian for utf16be or little endian for utf16le.
In keeping with the standard, a 0xFEFF byte at the start of a file is interpreted as a ZERO WIDTH NO-BREAK SPACE and not as a byte order mark. No byte order mark is written to files that use the utf16be or utf16le encodings.
As of Rakudo 2018.09 on MoarVM, utf16, utf16le and utf16be are supported. In 2018.10, writing to a file with utf16 will properly add a byte order mark (BOM).
Examples
with 'foo'.IO {
.spurt: "First line is text, then:\nBinary";
my $fh will leave {.close} = .open;
$fh.get.say; # OUTPUT: «First line is text, then:»
$fh.encoding: Nil;
$fh.slurp.say; # OUTPUT: «Buf[uint8]:0x<42 69 6e 61 72 79>»
}
routine lines
sub lines( $what = $*ARGFILES, |c)
multi method lines( IO::Handle:D: $limit, :$close )
multi method lines( IO::Handle:D: :$close )
The sub form, which takes $*ARGFILES
by default, will apply the lines
method to the object that's the first argument, and pass it the rest of the
arguments.
The method will return a Seq each element of which is a line from
the handle (that is
chunks delineated by .nl-in). If the
handle's .chomp attribute is set to True
,
then characters specified by .nl-in will be
stripped from each line.
Reads up to $limit
lines, where $limit
can be a non-negative Int,
Inf
, or Whatever (which is interpreted to mean Inf
). If :$close
is
set to True
, will close the handle when the file ends or $limit
is
reached. Subroutine form defaults to
$*ARGFILES, if no
handle is provided.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
NOTE: the lines are read lazily, so ensure the returned Seq is either fully reified or is no longer needed when you close the handle or attempt to use any other method that changes the file position.
say "The file contains ",
'50GB-file'.IO.open.lines.grep(*.contains: 'Raku').elems,
" lines that mention Raku";
# OUTPUT: «The file contains 72 lines that mention Raku»
You can use lines
in /proc/*
files (from the 6.d version):
say lines( "/proc/$*PID/statm".IO ); # OUTPUT: «(58455 31863 8304 2 0 29705 0)»
method lock
method lock(IO::Handle:D:
Bool:D :$non-blocking = False, Bool:D :$shared = False
--> True)
Places an advisory lock on the file the filehandle if open for. If
:$non-blocking
is True
will fail with X::IO::Lock
if lock could not be obtained, otherwise will block until the lock can be
placed. If :$shared
is True
will place a shared (read) lock, otherwise
will place an exclusive (write) lock. On success, returns True
;
fails with X::IO::Lock if lock cannot be placed (e.g. when
trying to place a shared lock on a filehandle opened in write mode or trying to
place an exclusive lock on a filehandle opened in read mode).
You can use .lock
again to replace an existing lock with another one.
To remove a lock, close the filehandle or use
unlock.
# One program writes, the other reads, and thanks to locks either
# will wait for the other to finish before proceeding to read/write
# Writer
given "foo".IO.open(:w) {
.lock;
.spurt: "I ♥ Raku!";
.close; # closing the handle unlocks it; we could also use `unlock` for that
}
# Reader
given "foo".IO.open {
.lock: :shared;
.slurp.say; # OUTPUT: «I ♥ Raku!»
.close;
}
method unlock
method unlock(IO::Handle:D: --> True)
Removes a lock from the filehandle. It will return
True
or fail with an exception if it's not possible.
routine words
multi words(IO::Handle:D $fh = $*ARGFILES, $limit = Inf, :$close --> Seq:D)
multi method words(IO::Handle:D: $limit = Inf, :$close --> Seq:D)
Similar to Str.words, separates the handle's
stream on contiguous chunks of whitespace (as defined
by Unicode) and returns a Seq of the resultant "words." Takes an optional
$limit
argument that can be a non-negative Int, Inf
, or Whatever
(which is interpreted to mean Inf
), to indicate only up-to $limit
words
must be returned. If Bool :$close
named argument is set to True
,
will automatically close the handle when the returned Seq is exhausted.
Subroutine form defaults to
$*ARGFILES, if no handle
is provided.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my %dict := bag $*IN.words;
say "Most common words: ", %dict.sort(-*.value).head: 5;
NOTE: implementations may read more data than necessary when a call
to .words
is made. That is, $handle.words(2)
may read more data than two
"words" worth of data and subsequent calls to read methods might not read from
the place right after the two fetched words. After a call to .words
, the
file position should be treated as undefined.
method split
method split(IO::Handle:D: :$close, |c)
Slurps the handle's content and calls
Str.split on it, forwarding any of the given
arguments. If :$close
named parameter is set to True
, will
close the invocant after slurping.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = 'path/to/file'.IO.open;
$fh.split: '♥', :close; # Returns file content split on ♥
method spurt
multi method spurt(IO::Handle:D: Blob $data, :$close = False)
multi method spurt(IO::Handle:D: Cool $data, :$close = False)
Writes all of the $data
into the filehandle, closing it when finished,
if $close
is True
. For Cool $data
, will use the
encoding the handle is set to use (IO::Handle.open
or IO::Handle.encoding).
Behavior for spurting a Cool when the handle is in binary mode or spurting a Blob when the handle is NOT in binary mode is undefined.
method print
multi method print(**@text --> True)
multi method print(Junction:D --> True)
Writes the given @text
to the handle, coercing any non-Str
objects to Str by calling .Str method on them.
Junction arguments
autothread and the order of
printed strings is not guaranteed. See write to write bytes.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = 'path/to/file'.IO.open: :w;
$fh.print: 'some text';
$fh.close;
method print-nl
method print-nl(IO::Handle:D: --> True)
Writes the value of $.nl-out
attribute into the handle. This attribute, by
default, is 
, but see the page on newline for the rules
it follows in different platforms and environments.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = 'path/to/file'.IO.open: :w, :nl-out("\r\n");
$fh.print: "some text";
$fh.print-nl; # prints \r\n
$fh.close;
method printf
multi method printf(IO::Handle:D: Cool $format, *@args)
Formats a string based on the given format and arguments and .print
s the
result into the filehandle. See
sprintf for details on
acceptable format directives.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = open 'path/to/file', :w;
$fh.printf: "The value is %d\n", 32;
$fh.close;
method out-buffer
method out-buffer(--> Int:D) is rw
Controls output buffering and can be set via an argument to open. Takes
an int
as the size of the buffer to use (zero is acceptable). Can take
a Bool: True
means to use default, implementation-defined buffer size;
False
means to disable buffering (equivalent to using 0
as buffer size).
Lastly, can take a Nil to enable TTY-based buffering control: if the handle is a TTY, the buffering is disabled, otherwise, default, implementation-defined buffer size is used.
See flush to write out data currently in the buffer. Changing buffer size flushes the filehandle.
given 'foo'.IO.open: :w, :1000out-buffer {
.say: 'Hello world!'; # buffered
.out-buffer = 42; # buffer resized; previous print flushed
.say: 'And goodbye';
.close; # closing the handle flushes the buffer
}
method put
multi method put(**@text --> True)
multi method put(Junction:D --> True)
Writes the given @text
to the handle, coercing any non-Str objects to
Str by calling .Str method on them, and appending the
value of .nl-out at the end. Junction
arguments autothread and the
order of printed strings is not guaranteed.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = 'path/to/file'.IO.open: :w;
$fh.put: 'some text';
$fh.close;
method say
multi method say(IO::Handle:D: **@text --> True)
This method is identical to put except that it stringifies its arguments by calling .gist instead of .Str.
Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.
my $fh = open 'path/to/file', :w;
$fh.say(Complex.new(3, 4)); # OUTPUT: «3+4i»
$fh.close;
method read
method read(IO::Handle:D: Int(Cool:D) $bytes = 65536 --> Buf:D)
Binary reading; reads and returns up to $bytes
bytes from the filehandle.
$bytes
defaults to an implementation-specific value (in Rakudo,
the value of $*DEFAULT-READ-ELEMS
, which by default is set to 65536
).
This method can be called even when the handle is not
in binary mode.
(my $file = 'foo'.IO).spurt: 'I ♥ Raku';
given $file.open {
say .read: 6; # OUTPUT: «Buf[uint8]:0x<49 20 e2 99 a5 20>»
.close;
}
method readchars
method readchars(IO::Handle:D: Int(Cool:D) $chars = 65536 --> Str:D)
Reading chars; reads and returns up to $chars
chars (graphemes) from the
filehandle. $chars
defaults to an implementation-specific value (in Rakudo,
the value of $*DEFAULT-READ-ELEMS
, which by default is set to 65536
).
Attempting to call this method when the handle is
in binary mode will result in
X::IO::BinaryMode exception being thrown.
(my $file = 'foo'.IO).spurt: 'I ♥ Raku';
given $file.open {
say .readchars: 5; # OUTPUT: «I ♥ R»
.close;
}
method write
method write(IO::Handle:D: Blob:D $buf --> True)
Writes $buf
to the filehandle. This method can be called even when the
handle is not in binary mode.
method seek
method seek(IO::Handle:D: Int:D $offset, SeekType:D $whence --> True)
Move the file pointer (that is, the position at which any subsequent read
or write operations will begin) to the byte position specified by
$offset
relative to the location specified by $whence
which may be
one of:
SeekFromBeginning
: The beginning of the file.SeekFromCurrent
: The current position in the file.SeekFromEnd
: The end of the file. Please note that you need to specify a negative offset if you want to position before the end of the file.
method tell
method tell(IO::Handle:D: --> Int:D)
Return the current position of the file pointer in bytes.
method slurp-rest
multi method slurp-rest(IO::Handle:D: :$bin! --> Buf)
multi method slurp-rest(IO::Handle:D: :$enc --> Str)
DEPRECATION NOTICE: this method is deprecated in the 6.d
version. Do
not use it for new code, use .slurp method instead.
Returns the remaining content of the file from the current file position (which
may have been set by previous reads or by seek
.) If the adverb :bin
is
provided a Buf will be returned; otherwise the return will be a Str with
the optional encoding :enc
.
method slurp
method slurp(IO::Handle:D: :$close, :$bin)
Returns all the content from the current file pointer to the end. If the
invocant is in binary mode or if $bin
is set to True
, will return a
Buf, otherwise will decode the content using invocant's current
.encoding and return a Str.
If :$close
is set to True
, will close the handle when finished reading.
Note: On Rakudo this method was introduced
with release 2017.04; $bin
arg was added in 2017.10.
method Supply
multi method Supply(IO::Handle:D: :$size = 65536)
Returns a Supply that will emit the contents of the handle in chunks. The chunks will be Buf if the handle is in binary mode or, if it isn't, Str decoded using same encoding as IO::Handle.encoding.
The size of the chunks is determined by the optional :size
named
parameter and 65536
bytes in binary mode or 65536
characters in non-binary
mode.
"foo".IO.open(:bin).Supply(:size<10>).tap: *.raku.say;
# OUTPUT:
# Buf[uint8].new(73,32,226,153,165,32,80,101,114,108)
# Buf[uint8].new(32,54,33,10)
"foo".IO.open.Supply(:size<10>).tap: *.raku.say;
# OUTPUT:
# "I ♥ Perl"
# " 6!\n"
method path
method path(IO::Handle:D:)
For a handle opened on a file this returns the IO::Path that represents the file. For the standard I/O handles $*IN, $*OUT, and $*ERR it returns an IO::Special object.
method IO
method IO(IO::Handle:D:)
Alias for .path
method Str
Returns the value of .path, coerced to Str.
say "foo".IO.open.Str; # OUTPUT: «foo»
routine close
method close(IO::Handle:D: --> Bool:D)
multi close(IO::Handle $fh)
Closes an open filehandle, returning True
on success. No error is thrown if the
filehandle is already closed, although if you close one of the standard
filehandles (by default: $*IN
, $*OUT
, or $*ERR
: any handle
with native-descriptor 2
or lower), you won't be
able to re-open them.
given "foo/bar".IO.open(:w) {
.spurt: "I ♥ Raku!";
.close;
}
It's a common idiom to use LEAVE phaser for closing the handles, which ensures the handle is closed regardless of how the block is left.
do {
my $fh = open "path-to-file";
LEAVE close $fh;
# ... do stuff with the file
}
sub do-stuff-with-the-file (IO $path-to-file) {
my $fh = $path-to-file.open;
# stick a `try` on it, since this will get run even when the sub is
# called with wrong arguments, in which case the `$fh` will be an `Any`
LEAVE try close $fh;
# ... do stuff with the file
}
Note: unlike some other languages, Raku does not use reference counting,
and so the filehandles are NOT closed when they go out of scope. While
they will get closed when garbage collected, garbage collection isn't
guaranteed to get run. This means you must use an explicit close
on
handles opened for writing, to avoid data loss, and an explicit close
is recommended on handles opened for reading as well, so that your program
does not open too many files at the same time, triggering exceptions on further
open
calls.
Note several methods allow for providing :close
argument, to close the handle
after the operation invoked by the method completes. As a simpler alternative,
the IO::Path type provides many reading and writing methods that let you work
with files without dealing with filehandles directly.
method flush
method flush(IO::Handle:D: --> True)
Will flush the handle, writing any of the buffered data. Returns True
on success; otherwise, fails with X::IO::Flush.
given "foo".IO.open: :w {
LEAVE .close;
.print: 'something';
'foo'.IO.slurp.say; # (if the data got buffered) OUTPUT: «»
.flush; # flush the handle
'foo'.IO.slurp.say; # OUTPUT: «something»
}
method native-descriptor
method native-descriptor(IO::Handle:D:)
This returns a value that the operating system would understand as a "file
descriptor" and is suitable for passing to a native function that requires a
file descriptor as an argument such as fcntl
or ioctl
.
method nl-in
method nl-in(--> Str:D) is rw
One of the attributes that can be set via .new
or open.
Defaults to ["\x0A", "\r\n"]
. Takes either a Str or
Array of Str specifying input line ending(s) for this handle.
If .chomp
attribute is set to True
, will strip these endings in routines
that chomp
, such as get and lines.
with 'test'.IO {
.spurt: '1foo2bar3foo'; # write some data into our test file
my $fh will leave {.close} = .open; # can also set .nl-in via .open arg
$fh.nl-in = [<foo bar>]; # set two possible line endings to use;
$fh.lines.say; # OUTPUT: ("1", "2", "3").Seq
}
method nl-out
has Str:D $.nl-out is rw = "\n";
One of the attributes that can be set via .new
or open.
Defaults to "\n"
. Takes a Str specifying output line ending for this
handle, to be used by methods .put
and .say.
with 'test'.IO {
given .open: :w {
.put: 42;
.nl-out = 'foo';
.put: 42;
.close;
}
.slurp.raku.say; # OUTPUT: «"42\n42foo"»
}
method opened
method opened(IO::Handle:D: --> Bool:D)
Returns True
if the handle is open, False
otherwise.
method t
method t(IO::Handle:D: --> Bool:D)
Returns True
if the handle is opened to a
TTY, False
otherwise.
Creating Custom Handles
As of 6.d language (early implementation available in Rakudo compiler
version 2018.08), a few helper methods are available to simplify creation of
custom IO::Handle
objects. In your subclass you simply need to implement
those methods to affect all of the related features. If your handle wants
to work with textual read/write methods and doesn't use the standard .open
method, be sure to call .encoding
method in your custom handle to get
decoder/encoder properly set up:
class IO::URL is IO::Handle {
has $.URL is required;
has Buf $!content;
submethod TWEAK {
use WWW; # ecosystem module that will let us `get` a web page
use DOM::Tiny; # ecosystem module that will parse out all text from HTML
$!content := Buf.new: DOM::Tiny.parse(get $!URL).all-text(:trim).encode;
self.encoding: 'utf8'; # set up encoder/decoder
}
method open(|) { self } # block out some IO::Handle methods
method close(|) { self } # that work with normal low-level file
method opened { ! self.EOF } # handles, since we don't. This isn't
method lock(| --> True) { } # necessary, but will make our handle
method unlock( --> True) { } # be more well-behaved if someone
# actually calls one of these methods. There are more of these you
# can handle, such as .tell, .seek, .flush, .native-descriptor, etc.
method WRITE(|) {
# For this handle we'll just die on write. If yours can handle writes.
# The data to write will be given as a Blob positional argument.
die "Cannot write into IO::URL";
}
method READ(\bytes) {
# We splice off the requested number of bytes from the head of
# our content Buf. The handle's decoder will handle decoding them
# automatically, if textual read methods were called on the handle.
$!content.splice: 0, bytes
}
method EOF {
# For "end of file", we'll simply report whether we still have
# any bytes of the website we fetched on creation.
not $!content
}
}
my $fh := IO::URL.new: :URL<raku.org>;
# .slurp and print all the content from the website. We can use all other
# read methods, such as .lines, or .get, or .readchars. All of them work
# correctly, even though we only defined .READ and .EOF
$fh.slurp.say;
method WRITE
method WRITE(IO::Handle:D: Blob:D \data --> Bool:D)
Called whenever a write operation is performed on the handle. Always receives the data as a Blob, even if a textual writing method has been called.
class IO::Store is IO::Handle {
has @.lines = [];
submethod TWEAK {
self.encoding: 'utf8'; # set up encoder/decoder
}
method WRITE(IO::Handle:D: Blob:D \data --> Bool:D) {
@!lines.push: data.decode();
True;
}
method gist() {
return @!lines.join("\n" );
}
}
my $store = IO::Store.new();
my $output = $PROCESS::OUT;
$PROCESS::OUT = $store;
.say for <one two three>;
$PROCESS::OUT = $output;
say $store.lines(); # OUTPUT: «[one two three]»
In this example we are creating a simple WRITE
redirection which stores
anything written to the filehandle to an array. Se need to save the standard
output first, which we do in $output
, and then everything that is print
ed
or said (through say
) gets stored in the defined IO::Store
class. Two
things should be taken into account in this class. By default, IO::Handle
s
are in binary mode, so we need to TWEAK
the objects if we want them to work
with text. Second, a WRITE
operation should return True
if successful. It
will fail if it does not.
method READ
method READ(IO::Handle:D: Int:D \bytes --> Buf:D)
Called whenever a read operation is performed on the handle. Receives the number of bytes requested to read. Returns a Buf with those bytes which can be used to either fill the decoder buffer or returned from reading methods directly. The result is allowed to have fewer than the requested number of bytes, including no bytes at all.
If you provide your own .READ
, you very likely need to provide your own
.EOF as well, for all the features to behave correctly.
The compiler may call .EOF method any number of times during
a read operation to ascertain whether a call to .READ
should be made. More
bytes than necessary to satisfy a read operation may be requested from .READ
,
in which case the extra data may be buffered by the IO::Handle
or the decoder
it's using, to fulfill any subsequent reading operations, without necessarily
having to make another .READ
call.
class IO::Store is IO::Handle {
has @.lines = [];
submethod TWEAK {
self.encoding: 'utf8'; # set up encoder/decoder
}
method WRITE(IO::Handle:D: Blob:D \data --> Bool:D) {
@!lines.push: data;
True;
}
method whole() {
my Buf $everything = Buf.new();
for @!lines -> $b {
$everything ~= $b;
}
return $everything;
}
method READ(IO::Handle:D: Int:D \bytes --> Buf:D) {
my Buf $everything := self.whole();
return $everything;
}
method EOF {
my $everything = self.whole();
!$everything;
}
}
my $store := IO::Store.new();
$store.print( $_ ) for <one two three>;
say $store.read(3).decode; # OUTPUT: «one»
say $store.read(3).decode; # OUTPUT: «two»
In this case, we have programmed the two READ
and EOF
methods, as well as
WRITE
, which stores every line in an element in an array. The read
method
actually calls READ
, returning 3 bytes, which correspond to the three
characters in the first two elements. Please note that it's the IO::Handle
base class the one that is taking care of cursor, since READ
just provides a
handle into the whole content of the object; the base class will READ
1024 *
1024 bytes at a time. If your object is planned to hold an amount of bytes
bigger than that, you will have to handle an internal cursor yourself. That is
why in this example we don't actually use the bytes
argument.
method EOF
method EOF(IO::Handle:D: --> Bool:D)
Indicates whether "end of file" has been reached for the data source of the
handle; i.e. no more data can be obtained by calling .READ
method. Note that this is not the same as eof method, which
will return True
only if .EOF
returns True
and all the decoder
buffers, if any were used by the handle, are also empty. See
.READ for an example implementation.
Related roles and classes
See also the related role IO and the related class IO::Path.