abstract class IO
inherits Reference
¶
The IO class is the basis for all input and output in Crystal.
This class is inherited by types like File, Socket and IO::Memory and
provides many useful methods for reading from and writing to an IO, like print, puts,
gets and printf.
The only requirement for a type including the IO module is to define
these two methods:
read(slice : Bytes): read at most slice.size bytes from IO into slice and return the number of bytes readwrite(slice : Bytes): write the whole slice into the IO
For example, this is a simple IO on top of a Bytes:
class SimpleSliceIO < IO
def initialize(@slice : Bytes)
end
def read(slice : Bytes)
slice.size.times { |i| slice[i] = @slice[i] }
@slice += slice.size
slice.size
end
def write(slice : Bytes) : Nil
slice.size.times { |i| @slice[i] = slice[i] }
@slice += slice.size
end
end
slice = Slice.new(9) { |i| ('a'.ord + i).to_u8 }
String.new(slice) # => "abcdefghi"
io = SimpleSliceIO.new(slice)
io.gets(3) # => "abc"
io.print "xyz"
String.new(slice) # => "abcxyzghi"
Encoding¶
An IO can be set an encoding with the #set_encoding method. When this is
set, all string operations (gets, gets_to_end, read_char, <<, print, puts
printf) will write in the given encoding, and read from the given encoding.
Byte operations (read, write, read_byte, write_byte) never do
encoding/decoding operations.
If an encoding is not set, the default one is UTF-8.
Mixing string and byte operations might not give correct results and should be avoided, as string operations might need to read extra bytes in order to get characters in the given encoding.
Direct known subclasses
Compress::Deflate::Reader
Compress::Deflate::Writer
Compress::Gzip::Reader
Compress::Gzip::Writer
Compress::Zlib::Reader
Compress::Zlib::Writer
HTTP::Server::Response
IO::Delimited
IO::Digest
IO::FileDescriptor
IO::Hexdump
IO::Memory
IO::MultiWriter
IO::Sized
IO::Stapled
OpenSSL::SSL::Socket
Socket
String::Builder
Class methods¶
.copy(src, dst, limit : Int) : Int64
¶
(src, dst, limit : Int) : Int64
Copy at most limit bytes from src to dst.
io = IO::Memory.new "hello"
io2 = IO::Memory.new
IO.copy io, io2, 3
io2.to_s # => "hel"
.copy(src, dst) : Int64
¶
(src, dst) : Int64
Copy all contents from src to dst.
io = IO::Memory.new "hello"
io2 = IO::Memory.new
IO.copy io, io2
io2.to_s # => "hello"
.pipe(read_blocking = false, write_blocking = false) : Tuple(IO::FileDescriptor, IO::FileDescriptor)
¶
(read_blocking = false, write_blocking = false) : Tuple(IO::FileDescriptor, IO::FileDescriptor)
Creates a pair of pipe endpoints (connected to each other)
and returns them as a two-element Tuple.
reader, writer = IO.pipe
writer.puts "hello"
writer.puts "world"
reader.gets # => "hello"
reader.gets # => "world"
.pipe(read_blocking = false, write_blocking = false
¶
(read_blocking = false, write_blocking = false
Creates a pair of pipe endpoints (connected to each other) and passes them to the given block. Both endpoints are closed after the block.
IO.pipe do |reader, writer|
writer.puts "hello"
writer.puts "world"
reader.gets # => "hello"
reader.gets # => "world"
end
.same_content?(stream1 : IO, stream2 : IO)
¶
(stream1 : IO, stream2 : IO)
Compares two streams stream1 to stream2 to determine if they are identical.
Returns true if content are the same, false otherwise.
File.write("afile", "123")
stream1 = File.open("afile")
stream2 = IO::Memory.new("123")
IO.same_content?(stream1, stream2) # => true
Methods¶
#<<(obj) : self
¶
(obj) : self
Writes the given object into this IO.
This ends up calling to_s(io) on the object.
io = IO::Memory.new
io << 1
io << '-'
io << "Crystal"
io.to_s # => "1-Crystal"
#close
¶
View source
#closed?
¶
View source
#each_byte(&) : Nil
¶
(&) : Nil
Invokes the given block with each byte (UInt8) in this IO.
io = IO::Memory.new("aあ")
io.each_byte do |byte|
puts byte
end
Output:
97
227
129
130
#each_byte
¶
Returns an Iterator for the bytes in this IO.
io = IO::Memory.new("aあ")
iter = io.each_byte
iter.next # => 97
iter.next # => 227
iter.next # => 129
iter.next # => 130
#each_char(&) : Nil
¶
(&) : Nil
Invokes the given block with each Char in this IO.
io = IO::Memory.new("あめ")
io.each_char do |char|
puts char
end
Output:
あ
め
#each_char
¶
Returns an Iterator for the chars in this IO.
io = IO::Memory.new("あめ")
iter = io.each_char
iter.next # => 'あ'
iter.next # => 'め'
#each_line(*args, **options, &block : String -> ) : Nil
¶
(*args, **options, &block : String -> ) : Nil
Invokes the given block with each line in this IO, where a line
is defined by the arguments passed to this method, which can be the same
ones as in the gets methods.
io = IO::Memory.new("hello\nworld")
io.each_line do |line|
puts line
end
# output:
# hello
# world
#each_line(*args, **options)
¶
(*args, **options)
Returns an Iterator for the lines in this IO, where a line
is defined by the arguments passed to this method, which can be the same
ones as in the gets methods.
io = IO::Memory.new("hello\nworld")
iter = io.each_line
iter.next # => "hello"
iter.next # => "world"
#flush
¶
Flushes buffered data, if any.
IO defines this is a no-op method, but including types may override.
#gets(delimiter : Char, limit : Int, chomp = false) : String?
¶
(delimiter : Char, limit : Int, chomp = false) : String?
Reads until delimiter is found, limit bytes are read, or the end of the IO is reached.
Returns nil if called at the end of this IO.
io = IO::Memory.new "hello\nworld"
io.gets('o', 3) # => "hel"
io.gets('r', 10) # => "lo\nwor"
io.gets('z', 10) # => "ld"
io.gets('w', 10) # => nil
#gets(delimiter : Char, chomp = false) : String?
¶
(delimiter : Char, chomp = false) : String?
Reads until delimiter is found, or the end of the IO is reached.
Returns nil if called at the end of this IO.
io = IO::Memory.new "hello\nworld"
io.gets('o') # => "hello"
io.gets('r') # => "\nwor"
io.gets('z') # => "ld"
io.gets('w') # => nil
#gets(chomp = true) : String?
¶
(chomp = true) : String?
Reads a line from this IO. A line is terminated by the \n character.
Returns nil if called at the end of this IO.
By default the newline is removed from the returned string,
unless chomp is false.
io = IO::Memory.new "hello\nworld\nfoo\n"
io.gets # => "hello"
io.gets(chomp: false) # => "world\n"
io.gets # => "foo"
io.gets # => nil
#gets(limit : Int, chomp = false) : String?
¶
(limit : Int, chomp = false) : String?
Reads a line of at most limit bytes from this IO.
A line is terminated by the \n character.
Returns nil if called at the end of this IO.
io = IO::Memory.new "hello\nworld"
io.gets(3) # => "hel"
io.gets(3) # => "lo\n"
io.gets(3) # => "wor"
io.gets(3) # => "ld"
io.gets(3) # => nil
#gets(delimiter : String, chomp = false) : String?
¶
(delimiter : String, chomp = false) : String?
Reads until delimiter is found or the end of the IO is reached.
Returns nil if called at the end of this IO.
io = IO::Memory.new "hello\nworld"
io.gets("wo") # => "hello\nwo"
io.gets("wo") # => "rld"
io.gets("wo") # => nil
#gets_to_end : String
¶
: String
Reads the rest of this IO data as a String.
io = IO::Memory.new "hello world"
io.gets_to_end # => "hello world"
#peek : Bytes?
¶
: Bytes?
Peeks into this IO, if possible.
It returns:
- nil if this IO isn't peekable
- an empty slice if it is, but EOF was reached
- a non-empty slice if some data can be peeked
The returned bytes are only valid data until a next call to any method that reads from this IO is invoked.
By default this method returns nil, but IO implementations
that provide buffering or wrap other IOs should override
this method.
#pos
¶
Returns the current position (in bytes) in this IO.
The IO class raises on this method, but some subclasses, notable
IO::FileDescriptor and IO::Memory implement it.
File.write("testfile", "hello")
file = File.new("testfile")
file.pos # => 0
file.gets(2) # => "he"
file.pos # => 2
#pos=(value)
¶
(value)
Sets the current position (in bytes) in this IO.
The IO class raises on this method, but some subclasses, notable
IO::FileDescriptor and IO::Memory implement it.
File.write("testfile", "hello")
file = File.new("testfile")
file.pos = 3
file.gets_to_end # => "lo"
#print(*objects : _) : Nil
¶
(*objects : _) : Nil
Writes the given objects into this IO by invoking to_s(io)
on each of the objects.
io = IO::Memory.new
io.print 1, '-', "Crystal"
io.to_s # => "1-Crystal"
#print(obj) : Nil
¶
(obj) : Nil
Same as <<.
io = IO::Memory.new
io.print 1
io.print '-'
io.print "Crystal"
io.to_s # => "1-Crystal"
#printf(format_string, *args) : Nil
¶
(format_string, *args) : Nil
Writes a formatted string to this IO.
For details on the format string, see top-level ::printf.
#printf(format_string, args : Array | Tuple) : Nil
¶
(format_string, args : Array | Tuple) : Nil
Writes a formatted string to this IO.
For details on the format string, see top-level ::printf.
#puts(*objects : _) : Nil
¶
(*objects : _) : Nil
Writes objects to this IO, each followed by a newline character unless
the object is a String and already ends with a newline.
io = IO::Memory.new
io.puts 1, '-', "Crystal"
io.to_s # => "1\n-\nCrystal\n"
#puts(obj) : Nil
¶
(obj) : Nil
Writes obj to this IO, followed by a newline character.
io = IO::Memory.new
io.puts 1
io.puts "Crystal"
io.to_s # => "1\nCrystal\n"
#puts(string : String) : Nil
¶
(string : String) : Nil
Writes string to this IO, followed by a newline character
unless the string already ends with one.
io = IO::Memory.new
io.puts "hello\n"
io.puts "world"
io.to_s # => "hello\nworld\n"
abstract
#read(slice : Bytes)
¶
(slice : Bytes)
Reads at most slice.size bytes from this IO into slice.
Returns the number of bytes read, which is 0 if and only if there is no
more data to read (so checking for 0 is the way to detect end of file).
io = IO::Memory.new "hello"
slice = Bytes.new(4)
io.read(slice) # => 4
slice # => Bytes[104, 101, 108, 108]
io.read(slice) # => 1
slice # => Bytes[111, 101, 108, 108]
io.read(slice) # => 0
#read_at(offset, bytesize, & : IO -> )
¶
(offset, bytesize, & : IO -> )
Yields an IO to read a section inside this IO.
The IO class raises on this method, but some subclasses, notable
File and IO::Memory implement it.
Multiple sections can be read concurrently.
#read_byte : UInt8?
¶
: UInt8?
Reads a single byte from this IO. Returns nil if there is no more
data to read.
io = IO::Memory.new "a"
io.read_byte # => 97
io.read_byte # => nil
#read_bytes(type, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
¶
(type, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
Reads an instance of the given type from this IO using the specified format.
This ends up invoking type.from_io(self, format), so any type defining a
from_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
method can be read in this way.
See Int.from_io and Float.from_io.
io = IO::Memory.new
io.puts "\u{4}\u{3}\u{2}\u{1}"
io.rewind
io.read_bytes(Int32, IO::ByteFormat::LittleEndian) # => 0x01020304
#read_char : Char?
¶
: Char?
Reads a single Char from this IO. Returns nil if there is no
more data to read.
io = IO::Memory.new "あ"
io.read_char # => 'あ'
io.read_char # => nil
#read_fully(slice : Bytes)
¶
(slice : Bytes)
Tries to read exactly slice.size bytes from this IO into slice.
Raises EOFError if there aren't slice.size bytes of data.
io = IO::Memory.new "123451234"
slice = Bytes.new(5)
io.read_fully(slice) # => 5
slice # => Bytes[49, 50, 51, 52, 53]
io.read_fully(slice) # raises IO::EOFError
#read_fully?(slice : Bytes)
¶
(slice : Bytes)
Tries to read exactly slice.size bytes from this IO into slice.
Returns nil if there aren't slice.size bytes of data, otherwise
returns the number of bytes read.
io = IO::Memory.new "123451234"
slice = Bytes.new(5)
io.read_fully?(slice) # => 5
slice # => Bytes[49, 50, 51, 52, 53]
io.read_fully?(slice) # => nil
#read_line(*args, **options) : String
¶
View source
(*args, **options) : String
#read_string(bytesize : Int) : String
¶
(bytesize : Int) : String
Reads an UTF-8 encoded string of exactly bytesize bytes.
Raises EOFError if there are not enough bytes to build
the string.
io = IO::Memory.new("hello world")
io.read_string(5) # => "hello"
io.read_string(1) # => " "
io.read_string(6) # raises IO::EOFError
#read_utf8(slice : Bytes)
¶
(slice : Bytes)
Reads UTF-8 decoded bytes into the given slice. Returns the number of UTF-8 bytes read.
If no encoding is set, this is the same as #read(slice).
bytes = "你".encode("GB2312") # => Bytes[196, 227]
io = IO::Memory.new(bytes)
io.set_encoding("GB2312")
buffer = uninitialized UInt8[1024]
bytes_read = io.read_utf8(buffer.to_slice) # => 3
buffer.to_slice[0, bytes_read] # => Bytes[228, 189, 160]
"你".bytes # => [228, 189, 160]
#read_utf8_byte
¶
Reads a single decoded UTF-8 byte from this IO.
Returns nil if there is no more data to read.
If no encoding is set, this is the same as #read_byte.
bytes = "你".encode("GB2312") # => Bytes[196, 227]
io = IO::Memory.new(bytes)
io.set_encoding("GB2312")
io.read_utf8_byte # => 228
io.read_utf8_byte # => 189
io.read_utf8_byte # => 160
io.read_utf8_byte # => nil
"你".bytes # => [228, 189, 160]
#rewind
¶
Rewinds this IO. By default this method raises, but including types
may implement it.
#seek(offset, whence : Seek = Seek::Set)
¶
(offset, whence : Seek = Seek::Set)
Seeks to a given offset (in bytes) according to the whence argument.
The IO class raises on this method, but some subclasses, notable
IO::FileDescriptor and IO::Memory implement it.
Returns self.
File.write("testfile", "abc")
file = File.new("testfile")
file.gets(3) # => "abc"
file.seek(1, IO::Seek::Set)
file.gets(2) # => "bc"
file.seek(-1, IO::Seek::Current)
file.gets(1) # => "c"
#set_encoding(encoding : String, invalid : Symbol? = nil)
¶
(encoding : String, invalid : Symbol? = nil)
Sets the encoding of this IO.
The invalid argument can be:
* nil: an exception is raised on invalid byte sequences
* :skip: invalid byte sequences are ignored
String operations (gets, gets_to_end, read_char, <<, print, puts
printf) will use this encoding.
#skip(bytes_count : Int) : Nil
¶
(bytes_count : Int) : Nil
Reads and discards exactly bytes_count bytes.
Raises IO::EOFError if there aren't at least bytes_count bytes.
io = IO::Memory.new "hello world"
io.skip(6)
io.gets # => "world"
io.skip(1) # raises IO::EOFError
#tty? : Bool
¶
: Bool
Returns true if this IO is associated with a terminal device (tty), false otherwise.
IO returns false, but including types may override.
STDIN.tty? # => true
IO::Memory.new.tty? # => false
abstract
#write(slice : Bytes) : Nil
¶
(slice : Bytes) : Nil
Writes the contents of slice into this IO.
io = IO::Memory.new
slice = Bytes.new(4) { |i| ('a'.ord + i).to_u8 }
io.write(slice)
io.to_s # => "abcd"
#write_byte(byte : UInt8)
¶
(byte : UInt8)
Writes a single byte into this IO.
io = IO::Memory.new
io.write_byte 97_u8
io.to_s # => "a"
#write_bytes(object, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
¶
(object, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
Writes the given object to this IO using the specified format.
This ends up invoking object.to_io(self, format), so any object defining a
to_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
method can be written in this way.
See Int#to_io and Float#to_io.
io = IO::Memory.new
io.write_bytes(0x01020304, IO::ByteFormat::LittleEndian)
io.rewind
io.gets(4) # => "\u{4}\u{3}\u{2}\u{1}"
#write_utf8(slice : Bytes)
¶
(slice : Bytes)
Writes a slice of UTF-8 encoded bytes to this IO, using the current encoding.