summaryrefslogtreecommitdiff
path: root/doc/stringio/stringio.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/stringio/stringio.md')
-rw-r--r--doc/stringio/stringio.md700
1 files changed, 700 insertions, 0 deletions
diff --git a/doc/stringio/stringio.md b/doc/stringio/stringio.md
new file mode 100644
index 0000000000..8931d1c30c
--- /dev/null
+++ b/doc/stringio/stringio.md
@@ -0,0 +1,700 @@
+\Class \StringIO supports accessing a string as a stream,
+similar in some ways to [class IO][io class].
+
+You can create a \StringIO instance using:
+
+- StringIO.new: returns a new \StringIO object containing the given string.
+- StringIO.open: passes a new \StringIO object to the given block.
+
+Like an \IO stream, a \StringIO stream has certain properties:
+
+- **Read/write mode**: whether the stream may be read, written, appended to, etc.;
+ see [Read/Write Mode][read/write mode].
+- **Data mode**: text-only or binary;
+ see [Data Mode][data mode].
+- **Encodings**: internal and external encodings;
+ see [Encodings][encodings].
+- **Position**: where in the stream the next read or write is to occur;
+ see [Position][position].
+- **Line number**: a special, line-oriented, "position" (different from the position mentioned above);
+ see [Line Number][line number].
+- **Open/closed**: whether the stream is open or closed, for reading or writing.
+ see [Open/Closed Streams][open/closed streams].
+- **BOM**: byte mark order;
+ see [Byte Order Mark][bom (byte order mark)].
+
+## About the Examples
+
+Examples on this page assume that \StringIO has been required:
+
+```ruby
+require 'stringio'
+```
+
+And that this constant has been defined:
+
+```ruby
+TEXT = <<EOT
+First line
+Second line
+
+Fourth line
+Fifth line
+EOT
+```
+
+## Stream Properties
+
+### Read/Write Mode
+
+#### Summary
+
+| Mode | Initial Clear? | Read | Write |
+|:--------------------------:|:--------------:|:--------:|:--------:|
+| <tt>'r'</tt>: read-only | No | Anywhere | Error |
+| <tt>'w'</tt>: write-only | Yes | Error | Anywhere |
+| <tt>'a'</tt>: append-only | No | Error | End only |
+| <tt>'r+'</tt>: read/write | No | Anywhere | Anywhere |
+| <tt>'w+'</tt>: read-write | Yes | Anywhere | Anywhere |
+| <tt>'a+'</tt>: read/append | No | Anywhere | End only |
+
+Each section below describes a read/write mode.
+
+Any of the modes may be given as a string or as file constants;
+example:
+
+```ruby
+strio = StringIO.new('foo', 'a')
+strio = StringIO.new('foo', File::WRONLY | File::APPEND)
+```
+
+#### `'r'`: Read-Only
+
+Mode specified as one of:
+
+- String: `'r'`.
+- Constant: `File::RDONLY`.
+
+Initial state:
+
+```ruby
+strio = StringIO.new('foobarbaz', 'r')
+strio.pos # => 0 # Beginning-of-stream.
+strio.string # => "foobarbaz" # Not cleared.
+```
+
+May be read anywhere:
+
+```ruby
+strio.gets(3) # => "foo"
+strio.gets(3) # => "bar"
+strio.pos = 9
+strio.gets(3) # => nil
+```
+
+May not be written:
+
+```ruby
+strio.write('foo') # Raises IOError: not opened for writing
+```
+
+#### `'w'`: Write-Only
+
+Mode specified as one of:
+
+- String: `'w'`.
+- Constant: `File::WRONLY`.
+
+Initial state:
+
+```ruby
+strio = StringIO.new('foo', 'w')
+strio.pos # => 0 # Beginning of stream.
+strio.string # => "" # Initially cleared.
+```
+
+May be written anywhere (even past end-of-stream):
+
+```ruby
+strio.write('foobar')
+strio.string # => "foobar"
+strio.rewind
+strio.write('FOO')
+strio.string # => "FOObar"
+strio.pos = 3
+strio.write('BAR')
+strio.string # => "FOOBAR"
+strio.pos = 9
+strio.write('baz')
+strio.string # => "FOOBAR\u0000\u0000\u0000baz" # Null-padded.
+```
+
+May not be read:
+
+```ruby
+strio.read # Raises IOError: not opened for reading
+```
+
+#### `'a'`: Append-Only
+
+Mode specified as one of:
+
+- String: `'a'`.
+- Constant: `File::WRONLY | File::APPEND`.
+
+Initial state:
+
+```ruby
+strio = StringIO.new('foo', 'a')
+strio.pos # => 0 # Beginning-of-stream.
+strio.string # => "foo" # Not cleared.
+```
+
+May be written only at the end; position does not affect writing:
+
+```ruby
+strio.write('bar')
+strio.string # => "foobar"
+strio.write('baz')
+strio.string # => "foobarbaz"
+strio.pos = 400
+strio.write('bat')
+strio.string # => "foobarbazbat"
+```
+
+May not be read:
+
+```ruby
+strio.gets # Raises IOError: not opened for reading
+```
+
+#### `'r+'`: Read/Write
+
+Mode specified as one of:
+
+- String: `'r+'`.
+- Constant: `File::RDRW`.
+
+Initial state:
+
+```ruby
+strio = StringIO.new('foobar', 'r+')
+strio.pos # => 0 # Beginning-of-stream.
+strio.string # => "foobar" # Not cleared.
+```
+
+May be written anywhere (even past end-of-stream):
+
+```ruby
+strio.write('FOO')
+strio.string # => "FOObar"
+strio.write('BAR')
+strio.string # => "FOOBAR"
+strio.write('BAZ')
+strio.string # => "FOOBARBAZ"
+strio.pos = 12
+strio.write('BAT')
+strio.string # => "FOOBARBAZ\u0000\u0000\u0000BAT" # Null padded.
+```
+
+May be read anywhere:
+
+```ruby
+strio.pos = 0
+strio.gets(3) # => "FOO"
+strio.pos = 6
+strio.gets(3) # => "BAZ"
+strio.pos = 400
+strio.gets(3) # => nil
+```
+
+#### `'w+'`: Read/Write (Initially Clear)
+
+Mode specified as one of:
+
+- String: `'w+'`.
+- Constant: `File::RDWR | File::TRUNC`.
+
+Initial state:
+
+```ruby
+strio = StringIO.new('foo', 'w+')
+strio.pos # => 0 # Beginning-of-stream.
+strio.string # => "" # Truncated.
+```
+
+May be written anywhere (even past end-of-stream):
+
+```ruby
+strio.write('foobar')
+strio.string # => "foobar"
+strio.rewind
+strio.write('FOO')
+strio.string # => "FOObar"
+strio.write('BAR')
+strio.string # => "FOOBAR"
+strio.write('BAZ')
+strio.string # => "FOOBARBAZ"
+strio.pos = 12
+strio.write('BAT')
+strio.string # => "FOOBARBAZ\u0000\u0000\u0000BAT" # Null-padded.
+```
+
+May be read anywhere:
+
+```ruby
+strio.rewind
+strio.gets(3) # => "FOO"
+strio.gets(3) # => "BAR"
+strio.pos = 12
+strio.gets(3) # => "BAT"
+strio.pos = 400
+strio.gets(3) # => nil
+```
+
+#### `'a+'`: Read/Append
+
+Mode specified as one of:
+
+- String: `'a+'`.
+- Constant: `File::RDWR | File::APPEND`.
+
+Initial state:
+
+```ruby
+strio = StringIO.new('foo', 'a+')
+strio.pos # => 0 # Beginning-of-stream.
+strio.string # => "foo" # Not cleared.
+```
+
+May be written only at the end; #rewind; position does not affect writing:
+
+```ruby
+strio.write('bar')
+strio.string # => "foobar"
+strio.write('baz')
+strio.string # => "foobarbaz"
+strio.pos = 400
+strio.write('bat')
+strio.string # => "foobarbazbat"
+```
+
+May be read anywhere:
+
+```ruby
+strio.rewind
+strio.gets(3) # => "foo"
+strio.gets(3) # => "bar"
+strio.pos = 9
+strio.gets(3) # => "bat"
+strio.pos = 400
+strio.gets(3) # => nil
+```
+
+### Data Mode
+
+To specify whether the stream is to be treated as text or as binary data,
+either of the following may be suffixed to any of the string read/write modes above:
+
+- `'t'`: Text;
+ initializes the encoding as Encoding::UTF_8.
+- `'b'`: Binary;
+ initializes the encoding as Encoding::ASCII_8BIT.
+
+If neither is given, the stream defaults to text data.
+
+Examples:
+
+```ruby
+strio = StringIO.new('foo', 'rt')
+strio.external_encoding # => #<Encoding:UTF-8>
+data = "\u9990\u9991\u9992\u9993\u9994"
+strio = StringIO.new(data, 'rb')
+strio.external_encoding # => #<Encoding:BINARY (ASCII-8BIT)>
+```
+
+When the data mode is specified, the read/write mode may not be omitted:
+
+```ruby
+StringIO.new(data, 'b') # Raises ArgumentError: invalid access mode b
+```
+
+A text stream may be changed to binary by calling instance method #binmode;
+a binary stream may not be changed to text.
+
+### Encodings
+
+A stream has an encoding; see [Encodings][encodings document].
+
+The initial encoding for a new or re-opened stream depends on its [data mode][data mode]:
+
+- Text: `Encoding::UTF_8`.
+- Binary: `Encoding::ASCII_8BIT`.
+
+These instance methods are relevant:
+
+- #external_encoding: returns the current encoding of the stream as an `Encoding` object.
+- #internal_encoding: returns +nil+; a stream does not have an internal encoding.
+- #set_encoding: sets the encoding for the stream.
+- #set_encoding_by_bom: sets the encoding for the stream to the stream's BOM (byte order mark).
+
+Examples:
+
+```ruby
+strio = StringIO.new('foo', 'rt') # Text mode.
+strio.external_encoding # => #<Encoding:UTF-8>
+data = "\u9990\u9991\u9992\u9993\u9994"
+strio = StringIO.new(data, 'rb') # Binary mode.
+strio.external_encoding # => #<Encoding:BINARY (ASCII-8BIT)>
+strio = StringIO.new('foo')
+strio.external_encoding # => #<Encoding:UTF-8>
+strio.set_encoding('US-ASCII')
+strio.external_encoding # => #<Encoding:US-ASCII>
+```
+
+### Position
+
+A stream has a _position_, and integer offset (in bytes) into the stream.
+The initial position of a stream is zero.
+
+#### Getting and Setting the Position
+
+Each of these methods initializes (to zero) the position of a new or re-opened stream:
+
+- ::new: returns a new stream.
+- ::open: passes a new stream to the block.
+- #reopen: re-initializes the stream.
+
+Each of these methods queries, gets, or sets the position, without otherwise changing the stream:
+
+- #eof?: returns whether the position is at end-of-stream.
+- #pos: returns the position.
+- #pos=: sets the position.
+- #rewind: sets the position to zero.
+- #seek: sets the position.
+
+Examples:
+
+```ruby
+strio = StringIO.new('foobar')
+strio.pos # => 0
+strio.pos = 3
+strio.pos # => 3
+strio.eof? # => false
+strio.rewind
+strio.pos # => 0
+strio.seek(0, IO::SEEK_END)
+strio.pos # => 6
+strio.eof? # => true
+```
+
+#### Position Before and After Reading
+
+Except for #pread, a stream reading method (see [Basic Reading][basic reading])
+begins reading at the current position.
+
+Except for #pread, a read method advances the position past the read substring.
+
+Examples:
+
+```ruby
+strio = StringIO.new(TEXT)
+strio.string # => "First line\nSecond line\n\nFourth line\nFifth line\n"
+strio.pos # => 0
+strio.getc # => "F"
+strio.pos # => 1
+strio.gets # => "irst line\n"
+strio.pos # => 11
+strio.pos = 24
+strio.gets # => "Fourth line\n"
+strio.pos # => 36
+
+strio = StringIO.new('тест') # Four 2-byte characters.
+strio.pos = 0 # At first byte of first character.
+strio.read # => "тест"
+strio.pos = 1 # At second byte of first character.
+strio.read # => "\x82ест"
+strio.pos = 2 # At first of second character.
+strio.read # => "ест"
+
+strio = StringIO.new(TEXT)
+strio.pos = 15
+a = []
+strio.each_line {|line| a.push(line) }
+a # => ["nd line\n", "\n", "Fourth line\n", "Fifth line\n"]
+strio.pos # => 47 ## End-of-stream.
+```
+
+#### Position Before and After Writing
+
+Each of these methods begins writing at the current position,
+and advances the position to the end of the written substring:
+
+- #putc: writes the given character.
+- #write: writes the given objects as strings.
+- [Kernel#puts][kernel#puts]: writes given objects as strings, each followed by newline.
+
+Examples:
+
+```ruby
+strio = StringIO.new('foo')
+strio.pos # => 0
+strio.putc('b')
+strio.string # => "boo"
+strio.pos # => 1
+strio.write('r')
+strio.string # => "bro"
+strio.pos # => 2
+strio.puts('ew')
+strio.string # => "brew\n"
+strio.pos # => 5
+strio.pos = 8
+strio.write('foo')
+strio.string # => "brew\n\u0000\u0000\u0000foo"
+strio.pos # => 11
+```
+
+Each of these methods writes _before_ the current position, and decrements the position
+so that the written data is next to be read:
+
+- #ungetbyte: unshifts the given byte.
+- #ungetc: unshifts the given character.
+
+Examples:
+
+```ruby
+strio = StringIO.new('foo')
+strio.pos = 2
+strio.ungetc('x')
+strio.pos # => 1
+strio.string # => "fxo"
+strio.ungetc('x')
+strio.pos # => 0
+strio.string # => "xxo"
+```
+
+This method does not affect the position:
+
+- #truncate: truncates the stream's string to the given size.
+
+Examples:
+
+```ruby
+strio = StringIO.new('foobar')
+strio.pos # => 0
+strio.truncate(3)
+strio.string # => "foo"
+strio.pos # => 0
+strio.pos = 500
+strio.truncate(0)
+strio.string # => ""
+strio.pos # => 500
+```
+
+### Line Number
+
+A stream has a line number, which initially is zero:
+
+- Method #lineno returns the line number.
+- Method #lineno= sets the line number.
+
+The line number can be affected by reading (but never by writing);
+in general, the line number is incremented each time the record separator (default: `"\n"`) is read.
+
+Examples:
+
+```ruby
+strio = StringIO.new(TEXT)
+strio.string # => "First line\nSecond line\n\nFourth line\nFifth line\n"
+strio.lineno # => 0
+strio.gets # => "First line\n"
+strio.lineno # => 1
+strio.getc # => "S"
+strio.lineno # => 1
+strio.gets # => "econd line\n"
+strio.lineno # => 2
+strio.gets # => "\n"
+strio.lineno # => 3
+strio.gets # => "Fourth line\n"
+strio.lineno # => 4
+```
+
+Setting the position does not affect the line number:
+
+```ruby
+strio.pos = 0
+strio.lineno # => 4
+strio.gets # => "First line\n"
+strio.pos # => 11
+strio.lineno # => 5
+```
+
+And setting the line number does not affect the position:
+
+```ruby
+strio.lineno = 10
+strio.pos # => 11
+strio.gets # => "Second line\n"
+strio.lineno # => 11
+strio.pos # => 23
+```
+
+### Open/Closed Streams
+
+A new stream is open for either reading or writing, and may be open for both;
+see [Read/Write Mode][read/write mode].
+
+Each of these methods initializes the read/write mode for a new or re-opened stream:
+
+- ::new: returns a new stream.
+- ::open: passes a new stream to the block.
+- #reopen: re-initializes the stream.
+
+Other relevant methods:
+
+- #close: closes the stream for both reading and writing.
+- #close_read: closes the stream for reading.
+- #close_write: closes the stream for writing.
+- #closed?: returns whether the stream is closed for both reading and writing.
+- #closed_read?: returns whether the stream is closed for reading.
+- #closed_write?: returns whether the stream is closed for writing.
+
+### BOM (Byte Order Mark)
+
+The string provided for ::new, ::open, or #reopen
+may contain an optional [BOM][bom] (byte order mark) at the beginning of the string;
+the BOM can affect the stream's encoding.
+
+The BOM (if provided):
+
+- Is stored as part of the stream's string.
+- Does _not_ immediately affect the encoding.
+- Is _initially_ considered part of the stream.
+
+```ruby
+utf8_bom = "\xEF\xBB\xBF"
+string = utf8_bom + 'foo'
+string.bytes # => [239, 187, 191, 102, 111, 111]
+strio.string.bytes.take(3) # => [239, 187, 191] # The BOM.
+strio = StringIO.new(string, 'rb')
+strio.string.bytes # => [239, 187, 191, 102, 111, 111] # BOM is part of the stored string.
+strio.external_encoding # => #<Encoding:BINARY (ASCII-8BIT)> # Default for a binary stream.
+strio.gets # => "\xEF\xBB\xBFfoo" # BOM is part of the stream.
+```
+
+You can call instance method #set_encoding_by_bom to "activate" the stored BOM;
+after doing so the BOM:
+
+- Is _still_ stored as part of the stream's string.
+- _Determines_ (and may have changed) the stream's encoding.
+- Is _no longer_ considered part of the stream.
+
+```ruby
+strio.set_encoding_by_bom
+strio.string.bytes # => [239, 187, 191, 102, 111, 111] # BOM is still part of the stored string.
+strio.external_encoding # => #<Encoding:UTF-8> # The new encoding.
+strio.rewind # => 0
+strio.gets # => "foo" # BOM is not part of the stream.
+```
+
+## Basic Stream \IO
+
+### Basic Reading
+
+You can read from the stream using these instance methods:
+
+- #getbyte: reads and returns the next byte.
+- #getc: reads and returns the next character.
+- #gets: reads and returns all or part of the next line.
+- #read: reads and returns all or part of the remaining data in the stream.
+- #readlines: reads the remaining data the stream and returns an array of its lines.
+- [Kernel#readline][kernel#readline]: like #gets, but raises an exception if at end-of-stream.
+
+You can iterate over the stream using these instance methods:
+
+- #each_byte: reads each remaining byte, passing it to the block.
+- #each_char: reads each remaining character, passing it to the block.
+- #each_codepoint: reads each remaining codepoint, passing it to the block.
+- #each_line: reads all or part of each remaining line, passing the read string to the block
+
+This instance method is useful in a multi-threaded application:
+
+- #pread: reads and returns all or part of the stream.
+
+### Basic Writing
+
+You can write to the stream, advancing the position, using these instance methods:
+
+- #putc: writes a given character.
+- #write: writes the given objects as strings.
+- [Kernel#puts][kernel#puts] writes given objects as strings, each followed by newline.
+
+You can "unshift" to the stream using these instance methods;
+each writes _before_ the current position, and decrements the position
+so that the written data is next to be read.
+
+- #ungetbyte: unshifts the given byte.
+- #ungetc: unshifts the given character.
+
+One more writing method:
+
+- #truncate: truncates the stream's string to the given size.
+
+## Line \IO
+
+Reading:
+
+- #gets: reads and returns the next line.
+- [Kernel#readline][kernel#readline]: like #gets, but raises an exception if at end-of-stream.
+- #readlines: reads the remaining data the stream and returns an array of its lines.
+- #each_line: reads each remaining line, passing it to the block
+
+Writing:
+
+- [Kernel#puts][kernel#puts]: writes given objects, each followed by newline.
+
+## Character \IO
+
+Reading:
+
+- #each_char: reads each remaining character, passing it to the block.
+- #getc: reads and returns the next character.
+
+Writing:
+
+- #putc: writes the given character.
+- #ungetc.: unshifts the given character.
+
+## Byte \IO
+
+Reading:
+
+- #each_byte: reads each remaining byte, passing it to the block.
+- #getbyte: reads and returns the next byte.
+
+Writing:
+
+- #ungetbyte: unshifts the given byte.
+
+## Codepoint \IO
+
+Reading:
+
+- #each_codepoint: reads each remaining codepoint, passing it to the block.
+
+[bom]: https://en.wikipedia.org/wiki/Byte_order_mark
+[encodings document]: https://docs.ruby-lang.org/en/master/language/encodings_rdoc.html
+[io class]: https://docs.ruby-lang.org/en/master/IO.html
+[kernel#puts]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-puts
+[kernel#readline]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-readline
+
+[basic reading]: rdoc-ref:StringIO@Basic+Reading
+[basic writing]: rdoc-ref:StringIO@Basic+Writing
+[bom (byte order mark)]: rdoc-ref:StringIO@BOM+-28Byte+Order+Mark-29
+[data mode]: rdoc-ref:StringIO@Data+Mode
+[encodings]: rdoc-ref:StringIO@Encodings
+[end-of-stream]: rdoc-ref:StringIO@End-of-Stream
+[line number]: rdoc-ref:StringIO@Line+Number
+[open/closed streams]: rdoc-ref:StringIO@Open-2FClosed+Streams
+[position]: rdoc-ref:StringIO@Position
+[read/write mode]: rdoc-ref:StringIO@Read-2FWrite+Mode