class StringIO - Documentation for Ruby 4.0
Description: class StringIO: Class \StringIO supports accessing a string as a stream, similar in some ways to [class IO][io class]. StringIO.
Keywords: ruby,class,StringIO

🔍

No recent searches

# class StringIO

Class StringIO supports accessing a string as a stream, similar in some ways to class IO.

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.

- Data mode: text-only or binary; see Data Mode.

- Encodings: internal and external encodings; see Encodings.

- Position: where in the stream the next read or write is to occur; see Position.

- Line number: a special, line-oriented, “position” (different from the position mentioned above); see Line Number.

- Open/closed: whether the stream is open or closed, for reading or writing. see Open/Closed Streams.

- BOM: byte mark order; see Byte Order Mark.

## About the Examples

Examples on this page assume that StringIO has been required:

    require 'stringio'

And that this constant has been defined:

    TEXT = <<EOT
    First line
    Second line

    Fourth line
    Fifth line
    EOT

## Stream Properties

### Read/Write Mode

#### Summary

Mode | Initial Clear? | Read | Write
--- | --- | --- | ---
`'r'`: read-only | No | Anywhere | Error
`'w'`: write-only | Yes | Error | Anywhere
`'a'`: append-only | No | Error | End only
`'r+'`: read/write | No | Anywhere | Anywhere
`'w+'`: read-write | Yes | Anywhere | Anywhere
`'a+'`: 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:

    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:

    strio = StringIO.new('foobarbaz', 'r')
    strio.pos    # => 0            # Beginning-of-stream.
    strio.string # => "foobarbaz"  # Not cleared.

May be read anywhere:

    strio.gets(3) # => "foo"
    strio.gets(3) # => "bar"
    strio.pos = 9
    strio.gets(3) # => nil

May not be written:

    strio.write('foo')  # Raises IOError: not opened for writing

#### `'w'`: Write-Only

Mode specified as one of:

- String: `'w'`.

- Constant: `File::WRONLY`.

Initial state:

    strio = StringIO.new('foo', 'w')
    strio.pos    # => 0   # Beginning of stream.
    strio.string # => ""  # Initially cleared.

May be written anywhere (even past end-of-stream):

    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:

    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:

    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:

    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:

    strio.gets  # Raises IOError: not opened for reading

#### `'r+'`: Read/Write

Mode specified as one of:

- String: `'r+'`.

- Constant: `File::RDRW`.

Initial state:

    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):

    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:

    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:

    strio = StringIO.new('foo', 'w+')
    strio.pos    # => 0   # Beginning-of-stream.
    strio.string # => ""  # Truncated.

May be written anywhere (even past end-of-stream):

    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:

    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:

    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:

    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:

    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:

    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:

    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.

The initial encoding for a new or re-opened stream depends on its 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:

    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:

    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) begins reading at the current position.

Except for `pread`, a read method advances the position past the read substring.

Examples:

    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: writes given objects as strings, each followed by newline.

Examples:

    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:

    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:

    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:

    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:

    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:

    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.

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 (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.

    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.

    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: 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 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: 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: 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.

### Constants

MAX_LENGTH

  Maximum length that a `StringIO` instance can hold

VERSION

  The version string

### Public Class Methods

new(string = '', mode = 'r+') → new_stringio
Source

    static VALUE
    strio_initialize(int argc, VALUE *argv, VALUE self)
    {
        struct StringIO *ptr = check_strio(self);

        if (!ptr) {
            DATA_PTR(self) = ptr = strio_alloc();
        }
        rb_call_super(0, 0);
        return strio_init(argc, argv, ptr, self);
    }

Returns a new StringIO instance formed from `string` and `mode`; the instance should be closed when no longer needed:

    strio = StringIO.new
    strio.string        # => ""
    strio.closed_read?  # => false
    strio.closed_write? # => false
    strio.close

If `string` is frozen, the default `mode` is `'r'`:

    strio = StringIO.new('foo'.freeze)
    strio.string        # => "foo"
    strio.closed_read?  # => false
    strio.closed_write? # => true
    strio.close

Argument `mode` must be a valid Access Mode, which may be a string or an integer constant:

    StringIO.new('foo', 'w+')
    StringIO.new('foo', File::RDONLY)

Related: `StringIO.open` (passes the StringIO object to the block; closes the object automatically on block exit).

open(string = '', mode = 'r+') → new_stringio
open(string = '', mode = 'r+') {|strio| ... } → object
Source

    static VALUE
    strio_s_open(int argc, VALUE *argv, VALUE klass)
    {
        VALUE obj = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);
        if (!rb_block_given_p()) return obj;
        return rb_ensure(rb_yield, obj, strio_finalize, obj);
    }

Creates new StringIO instance by calling `StringIO.new(string, mode)`.

With no block given, returns the new instance:

    strio = StringIO.open # => #<StringIO>

With a block given, calls the block with the new instance and returns the block’s value; closes the instance on block exit:

    StringIO.open('foo') {|strio| strio.string.upcase } # => "FOO"

Related: `StringIO.new`.

### Public Instance Methods

binmode → self
Source

    static VALUE
    strio_binmode(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);
        rb_encoding *enc = rb_ascii8bit_encoding();

        ptr->enc = enc;
        if (WRITABLE(self)) {
            rb_enc_associate(ptr->string, enc);
        }
        return self;
    }

Sets the data mode in `self` to binary mode; see Data Mode.

close → nil
Source

    static VALUE
    strio_close(VALUE self)
    {
        StringIO(self);
        RBASIC(self)->flags &= ~STRIO_READWRITE;
        return Qnil;
    }

Closes `self` for both reading and writing; returns `nil`:

    strio = StringIO.new
    strio.closed? # => false
    strio.close   # => nil
    strio.closed? # => true
    strio.read    # Raises IOError: not opened for reading
    strio.write   # Raises IOError: not opened for writing

Related: `StringIO#close_read`, `StringIO#close_write`, `StringIO.closed?`.

close_read → nil
Source

    static VALUE
    strio_close_read(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);
        if (!(ptr->flags & FMODE_READABLE)) {
            rb_raise(rb_eIOError, "closing non-duplex IO for reading");
        }
        RBASIC(self)->flags &= ~STRIO_READABLE;
        return Qnil;
    }

Closes `self` for reading; closed-write setting remains unchanged; returns `nil`:

    strio = StringIO.new
    strio.closed_read?  # => false
    strio.close_read    # => nil
    strio.closed_read?  # => true
    strio.closed_write? # => false
    strio.read          # Raises IOError: not opened for reading

Related: `StringIO#close`, `StringIO#close_write`.

close_write → nil
Source

    static VALUE
    strio_close_write(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);
        if (!(ptr->flags & FMODE_WRITABLE)) {
            rb_raise(rb_eIOError, "closing non-duplex IO for writing");
        }
        RBASIC(self)->flags &= ~STRIO_WRITABLE;
        return Qnil;
    }

Closes `self` for writing; closed-read setting remains unchanged; returns `nil`:

    strio = StringIO.new
    strio.closed_write? # => false
    strio.close_write   # => nil
    strio.closed_write? # => true
    strio.closed_read?  # => false
    strio.write('foo')  # Raises IOError: not opened for writing

Related: `StringIO#close`, `StringIO#close_read`, `StringIO#closed_write?`.

closed? → true or false
Source

    static VALUE
    strio_closed(VALUE self)
    {
        StringIO(self);
        if (!CLOSED(self)) return Qfalse;
        return Qtrue;
    }

Returns whether `self` is closed for both reading and writing:

    strio = StringIO.new
    strio.closed?     # => false  # Open for reading and writing.
    strio.close_read
    strio.closed?     # => false  # Still open for writing.
    strio.close_write
    strio.closed?     # => true   # Now closed for both.

Related: `StringIO.closed_read?`, `StringIO.closed_write?`.

closed_read? → true or false
Source

    static VALUE
    strio_closed_read(VALUE self)
    {
        StringIO(self);
        if (READABLE(self)) return Qfalse;
        return Qtrue;
    }

Returns whether `self` is closed for reading:

    strio = StringIO.new
    strio.closed_read?   # => false
    strio.close_read
    strio.closed_read?   # => true

Related: `StringIO#closed?`, `StringIO#closed_write?`, `StringIO#close_read`.

closed_write? → true or false
Source

    static VALUE
    strio_closed_write(VALUE self)
    {
        StringIO(self);
        if (WRITABLE(self)) return Qfalse;
        return Qtrue;
    }

Returns whether `self` is closed for writing:

    strio = StringIO.new
    strio.closed_write? # => false
    strio.close_write
    strio.closed_write? # => true

Related: `StringIO#close_write`, `StringIO#closed?`, `StringIO#closed_read?`.

each
Source

    static VALUE
    strio_each(int argc, VALUE *argv, VALUE self)
    {
        VALUE line;
        struct StringIO *ptr = readable(self);
        struct getline_arg arg;

        RETURN_ENUMERATOR(self, argc, argv);

        if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
            rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
        }

        while (!NIL_P(line = strio_getline(&arg, ptr))) {
            rb_yield(line);
        }
        return self;
    }

Also aliased as: each_line
each_byte {|byte| ... } → self
Source

    static VALUE
    strio_each_byte(VALUE self)
    {
        struct StringIO *ptr;

        RETURN_ENUMERATOR(self, 0, 0);

        while ((ptr = strio_to_read(self)) != NULL) {
            char c = RSTRING_PTR(ptr->string)[ptr->pos++];
            rb_yield(CHR2FIX(c));
        }
        return self;
    }

With a block given, calls the block with each remaining byte in the stream; positions the stream at end-of-file; returns `self`:

    bytes = []
    strio = StringIO.new('hello')     #  Five 1-byte characters.
    strio.each_byte {|byte| bytes.push(byte) }
    strio.eof? # => true
    bytes # => [104, 101, 108, 108, 111]
    bytes = []
    strio = StringIO.new('тест')      # Four 2-byte characters.
    strio.each_byte {|byte| bytes.push(byte) }
    bytes # => [209, 130, 208, 181, 209, 129, 209, 130]
    bytes = []
    strio = StringIO.new('こんにちは')  # Five 3-byte characters.
    strio.each_byte {|byte| bytes.push(byte) }
    bytes # => [227, 129, 147, 227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175]

The position in the stream matters:

    bytes = []
    strio = StringIO.new('こんにちは')
    strio.getc # => "こ"
    strio.pos  # => 3  # 3-byte character was read.
    strio.each_byte {|byte| bytes.push(byte) }
    bytes      # => [227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175]

If at end-of-file, does not call the block:

    strio.eof? # => true
    strio.each_byte {|byte| fail 'Boo!' }
    strio.eof? # => true

With no block given, returns a new `Enumerator`.

Related: `StringIO#each_char`, `StringIO#each_codepoint`, `StringIO#each_line`.

each_char {|char| ... } → self
Source

    static VALUE
    strio_each_char(VALUE self)
    {
        VALUE c;

        RETURN_ENUMERATOR(self, 0, 0);

        while (!NIL_P(c = strio_getc(self))) {
            rb_yield(c);
        }
        return self;
    }

With a block given, calls the block with each remaining character in the stream; positions the stream at end-of-file; returns `self`:

    chars = []
    strio = StringIO.new('hello')
    strio.each_char {|char| chars.push(char) }
    strio.eof? # => true
    chars      # => ["h", "e", "l", "l", "o"]
    chars = []
    strio = StringIO.new('тест')
    strio.each_char {|char| chars.push(char) }
    chars      # => ["т", "е", "с", "т"]
    chars = []
    strio = StringIO.new('こんにちは')
    strio.each_char {|char| chars.push(char) }
    chars      # => ["こ", "ん", "に", "ち", "は"]

Stream position matters:

    chars = []
    strio = StringIO.new('こんにちは')
    strio.getc # => "こ"
    strio.pos  # => 3  # 3-byte character was read.
    strio.each_char {|char| chars.push(char) }
    chars      # => ["ん", "に", "ち", "は"]

When at end-of-stream does not call the block:

    strio.eof? # => true
    strio.each_char {|char| fail 'Boo!' }
    strio.eof? # => true

With no block given, returns a new `Enumerator`.

Related: `StringIO#each_byte`, `StringIO#each_codepoint`, `StringIO#each_line`.

each_codepoint {|codepoint| ... } → self
Source

    static VALUE
    strio_each_codepoint(VALUE self)
    {
        struct StringIO *ptr;
        rb_encoding *enc;
        unsigned int c;
        int n;

        RETURN_ENUMERATOR(self, 0, 0);

        ptr = readable(self);
        enc = get_enc(ptr);
        while ((ptr = strio_to_read(self)) != NULL) {
            c = rb_enc_codepoint_len(RSTRING_PTR(ptr->string)+ptr->pos,
                                     RSTRING_END(ptr->string), &n, enc);
            ptr->pos += n;
            rb_yield(UINT2NUM(c));
        }
        return self;
    }

With a block given, calls the block with each successive codepoint from self; sets the position to end-of-stream; returns `self`.

Each codepoint is the integer value for a character; returns self:

    codepoints = []
    strio = StringIO.new('hello')
    strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
    strio.eof? # => true
    codepoints # => [104, 101, 108, 108, 111]
    codepoints = []
    strio = StringIO.new('тест')
    strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
    codepoints # => [1090, 1077, 1089, 1090]
    codepoints = []
    strio = StringIO.new('こんにちは')
    strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
    codepoints # => [12371, 12435, 12395, 12385, 12399]

Position in the stream matters:

    codepoints = []
    strio = StringIO.new('こんにちは')
    strio.getc # => "こ"
    strio.pos  # => 3
    strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
    codepoints # => [12435, 12395, 12385, 12399]

When at end-of-stream, the block is not called:

    strio.eof? # => true
    strio.each_codepoint {|codepoint| fail 'Boo!' }
    strio.eof? # => true

With no block given, returns a new `Enumerator`.

Related: `StringIO#each_byte`, `StringIO#each_char`, `StringIO#each_line`.

each_line(sep = $/, chomp: false) {|line| ... } → self
each_line(limit, chomp: false) {|line| ... } → self
each_line(sep, limit, chomp: false) {|line| ... } → self

With a block given calls the block with each remaining line (see “Position” below) in the stream; returns `self`.

Leaves stream position at end-of-stream.

No Arguments

With no arguments given, reads lines using the default record separator (global variable `$/`, whose initial value is `"\n"`).

    strio = StringIO.new(TEXT)
    strio.each_line {|line| p line }
    strio.eof? # => true

Output:

    "First line\n"
    "Second line\n"
    "\n"
    "Fourth line\n"
    "Fifth line\n"

Argument `sep`

With only string argument `sep` given, reads lines using that string as the record separator:

    strio = StringIO.new(TEXT)
    strio.each_line(' ') {|line| p line }

Output:

    "First "
    "line\nSecond "
    "line\n\nFourth "
    "line\nFifth "
    "line\n"

Argument `limit`

With only integer argument `limit` given, reads lines using the default record separator; also limits the size (in characters) of each line to the given limit:

    strio = StringIO.new(TEXT)
    strio.each_line(10) {|line| p line }

Output:

    "First line"
    "\n"
    "Second lin"
    "e\n"
    "\n"
    "Fourth lin"
    "e\n"
    "Fifth line"
    "\n"

Arguments `sep` and `limit`

With arguments `sep` and `limit` both given, honors both:

    strio = StringIO.new(TEXT)
    strio.each_line(' ', 10) {|line| p line }

Output:

    "First "
    "line\nSecon"
    "d "
    "line\n\nFour"
    "th "
    "line\nFifth"
    " "
    "line\n"

Position

As stated above, method `each` remaining line in the stream.

In the examples above each `strio` object starts with its position at beginning-of-stream; but in other cases the position may be anywhere (see `StringIO#pos`):

    strio = StringIO.new(TEXT)
    strio.pos = 30 # Set stream position to character 30.
    strio.each_line {|line| p line }

Output:

    " line\n"
    "Fifth line\n"

In all the examples above, the stream position is at the beginning of a character; in other cases, that need not be so:

    s = 'こんにちは'  # Five 3-byte characters.
    strio = StringIO.new(s)
    strio.pos = 3   # At beginning of second character.
    strio.each_line {|line| p line }
    strio.pos = 4   # At second byte of second character.
    strio.each_line {|line| p line }
    strio.pos = 5   # At third byte of second character.
    strio.each_line {|line| p line }

Output:

    "んにちは"
    "\x82\x93にちは"
    "\x93にちは"

Special Record Separators

Like some methods in class `IO`, `StringIO.each` honors two special record separators; see Special Line Separators.

    strio = StringIO.new(TEXT)
    strio.each_line('') {|line| p line } # Read as paragraphs (separated by blank lines).

Output:

    "First line\nSecond line\n\n"
    "Fourth line\nFifth line\n"

    strio = StringIO.new(TEXT)
    strio.each_line(nil) {|line| p line } # "Slurp"; read it all.

Output:

    "First line\nSecond line\n\nFourth line\nFifth line\n"

Keyword Argument `chomp`

With keyword argument `chomp` given as `true` (the default is `false`), removes trailing newline (if any) from each line:

    strio = StringIO.new(TEXT)
    strio.each_line(chomp: true) {|line| p line }

Output:

    "First line"
    "Second line"
    ""
    "Fourth line"
    "Fifth line"

With no block given, returns a new Enumerator.

Related: `StringIO.each_byte`, `StringIO.each_char`, `StringIO.each_codepoint`.

Alias for: each
eof
Source

    static VALUE
    strio_eof(VALUE self)
    {
        if (strio_to_read(self)) return Qfalse;
        return Qtrue;
    }

Also aliased as: eof?
eof? → true or false

Returns whether `self` is positioned at end-of-stream:

    strio = StringIO.new('foo')
    strio.pos  # => 0
    strio.eof? # => false
    strio.read # => "foo"
    strio.pos  # => 3
    strio.eof? # => true
    strio.close_read
    strio.eof? # Raises IOError: not opened for reading

Related: `StringIO#pos`.

Alias for: eof
external_encoding → encoding or nil
Source

    static VALUE
    strio_external_encoding(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);
        return rb_enc_from_encoding(get_enc(ptr));
    }

Returns an `Encoding` object that represents the encoding of the string; see Encodings:

    strio = StringIO.new('foo')
    strio.external_encoding # => #<Encoding:UTF-8>

Returns `nil` if `self` has no string and is in write mode:

    strio = StringIO.new(nil, 'w+')
    strio.external_encoding # => nil

fcntl (*args)
Source

    static VALUE
    strio_unimpl(int argc, VALUE *argv, VALUE self)
    {
        StringIO(self);
        rb_notimplement();

        UNREACHABLE;
    }

Raises `NotImplementedError`.

fileno ()
Source

    static VALUE
    strio_nil(VALUE self)
    {
        StringIO(self);
        return Qnil;
    }

Returns `nil`; for compatibility with `IO`.

flush ()
Source

    static VALUE
    strio_self(VALUE self)
    {
        StringIO(self);
        return self;
    }

Returns `self`; for compatibility with `IO`.

fsync ()
Source

    static VALUE
    strio_0(VALUE self)
    {
        StringIO(self);
        return INT2FIX(0);
    }

Returns 0; for compatibility with `IO`.

getbyte → integer or nil
Source

    static VALUE
    strio_getbyte(VALUE self)
    {
        struct StringIO *ptr = readable(self);
        int c;
        if (eos_p(ptr)) {
            return Qnil;
        }
        c = RSTRING_PTR(ptr->string)[ptr->pos++];
        return CHR2FIX(c);
    }

Reads and returns the next integer byte (not character) from the stream:

    s = 'foo'
    s.bytes       # => [102, 111, 111]
    strio = StringIO.new(s)
    strio.getbyte # => 102
    strio.getbyte # => 111
    strio.getbyte # => 111

Returns `nil` if at end-of-stream:

    strio.eof?    # => true
    strio.getbyte # => nil

Returns a byte, not a character:

    s = 'Привет'
    s.bytes
    # => [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130]
    strio = StringIO.new(s)
    strio.getbyte # => 208
    strio.getbyte # => 159

    s = 'こんにちは'
    s.bytes
    # => [227, 129, 147, 227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175]
    strio = StringIO.new(s)
    strio.getbyte # => 227
    strio.getbyte # => 129

Related: `each_byte`, `ungetbyte`, `getc`.

getc → character, byte, or nil
Source

    static VALUE
    strio_getc(VALUE self)
    {
        struct StringIO *ptr = readable(self);
        rb_encoding *enc = get_enc(ptr);
        VALUE str = ptr->string;
        long pos = ptr->pos;
        int len;
        char *p;

        if (eos_p(ptr)) {
            return Qnil;
        }
        p = RSTRING_PTR(str)+pos;
        len = rb_enc_mbclen(p, RSTRING_END(str), enc);
        ptr->pos += len;
        return enc_subseq(str, pos, len, enc);
    }

Reads and returns the next character (or byte; see below) from the stream:

    strio = StringIO.new('foo')
    strio.getc # => "f"
    strio.getc # => "o"
    strio.getc # => "o"

Returns `nil` if at end-of-stream:

    strio.eof? # => true
    strio.getc # => nil

Returns characters, not bytes:

    strio = StringIO.new('Привет')
    strio.getc # => "П"
    strio.getc # => "р"

    strio = StringIO.new('こんにちは')
    strio.getc # => "こ"
    strio.getc # => "ん"

In each of the examples above, the stream is positioned at the beginning of a character; in other cases that need not be true:

    strio = StringIO.new('こんにちは')  # Five 3-byte characters.
    strio.pos = 3 # => 3     # At beginning of second character; returns character.
    strio.getc    # => "ん"
    strio.pos = 4 # => 4     # At second byte of second character; returns byte.
    strio.getc    # => "\x82"
    strio.pos = 5 # => 5     # At third byte of second character; returns byte.
    strio.getc    # => "\x93"

Related: `getbyte`, `putc`, `ungetc`.

gets(sep = $/, chomp: false) → string or nil
gets(limit, chomp: false) → string or nil
gets(sep, limit, chomp: false) → string or nil
Source

    static VALUE
    strio_gets(int argc, VALUE *argv, VALUE self)
    {
        struct StringIO *ptr = readable(self);
        struct getline_arg arg;
        VALUE str;

        if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
            if (NIL_P(ptr->string)) return Qnil;
            return rb_enc_str_new(0, 0, get_enc(ptr));
        }

        str = strio_getline(&arg, ptr);
        rb_lastline_set(str);
        return str;
    }

Reads and returns a line from the stream; returns `nil` if at end-of-stream.

Side effects:

- Increments stream position by the number of bytes read.

- Assigns the return value to global variable `$_`.

With no arguments given, reads a line using the default record separator (global variable `$/`,* whose initial value is `"\n"`):

    strio = StringIO.new(TEXT)
    strio.pos  # => 0
    strio.gets # => "First line\n"
    strio.pos  # => 11
    $_         # => "First line\n"
    strio.gets # => "Second line\n"
    strio.read # => "\nFourth line\nFifth line\n"
    strio.eof? # => true
    strio.gets # => nil

    strio = StringIO.new('Привет')  # Six 2-byte characters
    strio.pos  # => 0
    strio.gets # => "Привет"
    strio.pos  # => 12

Argument `sep`

With only string argument `sep` given, reads a line using that string as the record separator:

    strio = StringIO.new(TEXT)
    strio.gets(' ') # => "First "
    strio.gets(' ') # => "line\nSecond "
    strio.gets(' ') # => "line\n\nFourth "

Argument `limit`

With only integer argument `limit` given, reads a line using the default record separator; limits the size (in characters) of each line to the given limit:

    strio = StringIO.new(TEXT)
    strio.gets(10) # => "First line"
    strio.gets(10) # => "\n"
    strio.gets(10) # => "Second lin"
    strio.gets(10) # => "e\n"

Arguments `sep` and `limit`

With arguments `sep` and `limit` both given, honors both:

    strio = StringIO.new(TEXT)
    strio.gets(' ', 10) # => "First "
    strio.gets(' ', 10) # => "line\nSecon"
    strio.gets(' ', 10) # => "d "

Position

As stated above, method `gets` reads and returns the next line in the stream.

In the examples above each `strio` object starts with its position at beginning-of-stream; but in other cases the position may be anywhere:

    strio = StringIO.new(TEXT)
    strio.pos = 12
    strio.gets # => "econd line\n"

The position need not be at a character boundary:

    strio = StringIO.new('Привет') # Six 2-byte characters.
    strio.pos = 2                  # At beginning of second character.
    strio.gets # => "ривет"
    strio.pos = 3                  # In middle of second character.
    strio.gets # => "\x80ивет"

Special Record Separators

Like some methods in class `IO`, method `gets` honors two special record separators; see Special Line Separators:

    strio = StringIO.new(TEXT)
    strio.gets('')  # Read "paragraph" (up to empty line).
    # => "First line\nSecond line\n\n"

    strio = StringIO.new(TEXT)
    strio.gets(nil) # "Slurp": read all.
    # => "First line\nSecond line\n\nFourth line\nFifth line\n"

Keyword Argument `chomp`

With keyword argument `chomp` given as `true` (the default is `false`), removes the trailing newline (if any) from the returned line:

    strio = StringIO.new(TEXT)
    strio.gets              # => "First line\n"
    strio.gets(chomp: true) # => "Second line"

Related: `each_line`, `readlines`, `Kernel#puts`.

internal_encoding → nil
Source

    static VALUE
    strio_internal_encoding(VALUE self)
    {
        return Qnil;
    }

Returns `nil`; for compatibility with `IO`.

isatty ()
Source

    static VALUE
    strio_false(VALUE self)
    {
        StringIO(self);
        return Qfalse;
    }

Returns `false`; for compatibility with `IO`.

Also aliased as: tty?
length
Alias for: size
lineno → current_line_number
Source

    static VALUE
    strio_get_lineno(VALUE self)
    {
        return LONG2NUM(StringIO(self)->lineno);
    }

Returns the current line number in `self`; see Line Number.

lineno = new_line_number → new_line_number
Source

    static VALUE
    strio_set_lineno(VALUE self, VALUE lineno)
    {
        StringIO(self)->lineno = NUM2LONG(lineno);
        return lineno;
    }

Sets the current line number in `self` to the given `new_line_number`; see Line Number.

pid ()
Source

    static VALUE
    strio_nil(VALUE self)
    {
        StringIO(self);
        return Qnil;
    }

Returns `nil`; for compatibility with `IO`.

pos → stream_position
Source

    static VALUE
    strio_get_pos(VALUE self)
    {
        return LONG2NUM(StringIO(self)->pos);
    }

Returns the current position (in bytes); see Position.

pos = new_position → new_position
Source

    static VALUE
    strio_set_pos(VALUE self, VALUE pos)
    {
        struct StringIO *ptr = StringIO(self);
        long p = NUM2LONG(pos);
        if (p < 0) {
            error_inval(0);
        }
        ptr->pos = p;
        return pos;
    }

Sets the current position (in bytes); see Position.

pread(maxlen, offset) → string
pread(maxlen, offset, out_string) → string
Source

    static VALUE
    strio_pread(int argc, VALUE *argv, VALUE self)
    {
        VALUE rb_len, rb_offset, rb_buf;
        rb_scan_args(argc, argv, "21", &rb_len, &rb_offset, &rb_buf);
        long len = NUM2LONG(rb_len);
        long offset = NUM2LONG(rb_offset);

        if (len < 0) {
            rb_raise(rb_eArgError, "negative string size (or size too big): %" PRIsVALUE, rb_len);
        }

        if (len == 0) {
            if (NIL_P(rb_buf)) {
                return rb_str_new("", 0);
            }
            return rb_buf;
        }

        if (offset < 0) {
            rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset));
        }

        struct StringIO *ptr = readable(self);

        if (outside_p(ptr, offset)) {
            rb_eof_error();
        }

        if (NIL_P(rb_buf)) {
            return strio_substr(ptr, offset, len, rb_ascii8bit_encoding());
        }

        long rest = RSTRING_LEN(ptr->string) - offset;
        if (len > rest) len = rest;
        rb_str_resize(rb_buf, len);
        rb_enc_associate(rb_buf, rb_ascii8bit_encoding());
        MEMCPY(RSTRING_PTR(rb_buf), RSTRING_PTR(ptr->string) + offset, char, len);
        return rb_buf;
    }

See `IO#pread`.

putc(obj) → obj
Source

    static VALUE
    strio_putc(VALUE self, VALUE ch)
    {
        struct StringIO *ptr = writable(self);
        VALUE str;

        check_modifiable(ptr);
        if (RB_TYPE_P(ch, T_STRING)) {
            if (NIL_P(ptr->string)) return ch;
            str = rb_str_substr(ch, 0, 1);
        }
        else {
            char c = NUM2CHR(ch);
            if (NIL_P(ptr->string)) return ch;
            str = rb_str_new(&c, 1);
        }
        strio_write(self, str);
        return ch;
    }

See `IO#putc`.

read([length [, outbuf]]) → string, outbuf, or nil
Source

    static VALUE
    strio_read(int argc, VALUE *argv, VALUE self)
    {
        struct StringIO *ptr = readable(self);
        VALUE str = Qnil;
        long len;
        int binary = 0;

        switch (argc) {
          case 2:
            str = argv[1];
            if (!NIL_P(str)) {
                StringValue(str);
                rb_str_modify(str);
            }
            /* fall through */
          case 1:
            if (!NIL_P(argv[0])) {
                len = NUM2LONG(argv[0]);
                if (len < 0) {
                    rb_raise(rb_eArgError, "negative length %ld given", len);
                }
                if (eos_p(ptr)) {
                    if (!NIL_P(str)) rb_str_resize(str, 0);
                    return len > 0 ? Qnil : rb_str_new(0, 0);
                }
                binary = 1;
                break;
            }
            /* fall through */
          case 0:
            if (NIL_P(ptr->string)) return Qnil;
            len = RSTRING_LEN(ptr->string);
            if (len <= ptr->pos) {
                rb_encoding *enc = get_enc(ptr);
                if (NIL_P(str)) {
                    str = rb_str_new(0, 0);
                }
                else {
                    rb_str_resize(str, 0);
                }
                rb_enc_associate(str, enc);
                return str;
            }
            else {
                len -= ptr->pos;
            }
            break;
          default:
            rb_error_arity(argc, 0, 2);
        }
        if (NIL_P(str)) {
            rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
            str = strio_substr(ptr, ptr->pos, len, enc);
        }
        else {
            long rest = RSTRING_LEN(ptr->string) - ptr->pos;
            if (len > rest) len = rest;
            rb_str_resize(str, len);
            MEMCPY(RSTRING_PTR(str), RSTRING_PTR(ptr->string) + ptr->pos, char, len);
            if (!binary) {
                rb_enc_copy(str, ptr->string);
            }
        }
        ptr->pos += RSTRING_LEN(str);
        return str;
    }

See `IO#read`.

readlines(sep=$/, chomp: false) → array
readlines(limit, chomp: false) → array
readlines(sep, limit, chomp: false) → array
Source

    static VALUE
    strio_readlines(int argc, VALUE *argv, VALUE self)
    {
        VALUE ary, line;
        struct StringIO *ptr = readable(self);
        struct getline_arg arg;

        if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
            rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
        }

        ary = rb_ary_new();
        while (!NIL_P(line = strio_getline(&arg, ptr))) {
            rb_ary_push(ary, line);
        }
        return ary;
    }

See `IO#readlines`.

reopen(other, mode = 'r+') → self
Source

    static VALUE
    strio_reopen(int argc, VALUE *argv, VALUE self)
    {
        rb_io_taint_check(self);
        if (argc == 1 && !RB_TYPE_P(*argv, T_STRING)) {
            return strio_copy(self, *argv);
        }
        return strio_init(argc, argv, StringIO(self), self);
    }

Reinitializes the stream with the given `other` (string or `StringIO`) and `mode`; see `IO.new`:

    StringIO.open('foo') do |strio|
      p strio.string
      strio.reopen('bar')
      p strio.string
      other_strio = StringIO.new('baz')
      strio.reopen(other_strio)
      p strio.string
      other_strio.close
    end

Output:

    "foo"
    "bar"
    "baz"

rewind → 0
Source

    static VALUE
    strio_rewind(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);
        ptr->pos = 0;
        ptr->lineno = 0;
        return INT2FIX(0);
    }

Sets the current position and line number to zero; see Position and Line Number.

seek(offset, whence = SEEK_SET) → 0
Source

    static VALUE
    strio_seek(int argc, VALUE *argv, VALUE self)
    {
        VALUE whence;
        struct StringIO *ptr = StringIO(self);
        long amount, offset;

        rb_scan_args(argc, argv, "11", NULL, &whence);
        amount = NUM2LONG(argv[0]);
        if (CLOSED(self)) {
            rb_raise(rb_eIOError, "closed stream");
        }
        switch (NIL_P(whence) ? 0 : NUM2LONG(whence)) {
          case 0:
            offset = 0;
            break;
          case 1:
            offset = ptr->pos;
            break;
          case 2:
            if (NIL_P(ptr->string)) {
                offset = 0;
            } else {
                offset = RSTRING_LEN(ptr->string);
            }
            break;
          default:
            error_inval("invalid whence");
        }
        if (amount > LONG_MAX - offset || amount + offset < 0) {
            error_inval(0);
        }
        ptr->pos = amount + offset;
        return INT2FIX(0);
    }

Sets the position to the given integer `offset` (in bytes), with respect to a given constant `whence`; see `IO#seek`.

set_encoding(ext_enc, [int_enc[, opt]]) → strio
Source

    static VALUE
    strio_set_encoding(int argc, VALUE *argv, VALUE self)
    {
        rb_encoding* enc;
        struct StringIO *ptr = StringIO(self);
        VALUE ext_enc, int_enc, opt;

        argc = rb_scan_args(argc, argv, "11:", &ext_enc, &int_enc, &opt);

        if (NIL_P(ext_enc)) {
            enc = rb_default_external_encoding();
        }
        else {
            enc = rb_find_encoding(ext_enc);
            if (!enc) {
                rb_io_enc_t convconfig;
                int oflags;
                rb_io_mode_t fmode;
                VALUE vmode = rb_str_append(rb_str_new_cstr("r:"), ext_enc);
                rb_io_extract_modeenc(&vmode, 0, Qnil, &oflags, &fmode, &convconfig);
                enc = convconfig.enc2;
            }
        }
        ptr->enc = enc;
        if (!NIL_P(ptr->string) && WRITABLE(self) && !str_chilled_p(ptr->string)) {
            rb_enc_associate(ptr->string, enc);
        }

        return self;
    }

Specify the encoding of the `StringIO` as ext_enc. Use the default external encoding if ext_enc is nil. 2nd argument int_enc and optional hash opt argument are ignored; they are for API compatibility to `IO`.

set_encoding_by_bom → strio or nil
Source

    static VALUE
    strio_set_encoding_by_bom(VALUE self)
    {
        struct StringIO *ptr = StringIO(self);

        if (!set_encoding_by_bom(ptr)) return Qnil;
        return rb_enc_from_encoding(ptr->enc);
    }

Sets the encoding according to the BOM (Byte Order Mark) in the string.

Returns `self` if the BOM is found, otherwise +nil.

size → integer
Source

    static VALUE
    strio_size(VALUE self)
    {
        VALUE string = StringIO(self)->string;
        if (NIL_P(string)) {
            return INT2FIX(0);
        }
        return ULONG2NUM(RSTRING_LEN(string));
    }

Returns the number of bytes in the string in `self`:

    StringIO.new('hello').size     # => 5  # Five 1-byte characters.
    StringIO.new('тест').size      # => 8  # Four 2-byte characters.
    StringIO.new('こんにちは').size # => 15 # Five 3-byte characters.

Also aliased as: length
string → string
Source

    static VALUE
    strio_get_string(VALUE self)
    {
        return StringIO(self)->string;
    }

Returns underlying string:

    StringIO.open('foo') do |strio|
      p strio.string
      strio.string = 'bar'
      p strio.string
    end

Output:

    "foo"
    "bar"

Related: `StringIO#string=` (assigns the underlying string).

string = other_string → other_string
Source

    static VALUE
    strio_set_string(VALUE self, VALUE string)
    {
        struct StringIO *ptr = StringIO(self);

        rb_io_taint_check(self);
        ptr->flags &= ~FMODE_READWRITE;
        StringValue(string);
        ptr->flags = readonly_string_p(string) ? FMODE_READABLE : FMODE_READWRITE;
        ptr->pos = 0;
        ptr->lineno = 0;
        RB_OBJ_WRITE(self, &ptr->string, string);
        return string;
    }

Replaces the stored string with `other_string`, and sets the position to zero; returns `other_string`:

    StringIO.open('foo') do |strio|
      p strio.string
      strio.string = 'bar'
      p strio.string
    end

Output:

    "foo"
    "bar"

Related: `StringIO#string` (returns the stored string).

sync → true
Source

    static VALUE
    strio_get_sync(VALUE self)
    {
        StringIO(self);
        return Qtrue;
    }

Returns `true`; implemented only for compatibility with other stream classes.

sync= (p1)
Source

    static VALUE
    strio_first(VALUE self, VALUE arg)
    {
        StringIO(self);
        return arg;
    }

Returns the argument unchanged. Just for compatibility to `IO`.

pos → stream_position
Source

    static VALUE
    strio_get_pos(VALUE self)
    {
        return LONG2NUM(StringIO(self)->pos);
    }

Returns the current position (in bytes); see Position.

truncate(integer) → 0
Source

    static VALUE
    strio_truncate(VALUE self, VALUE len)
    {
        VALUE string = writable(self)->string;
        long l = NUM2LONG(len);
        long plen;
        if (l < 0) {
            error_inval("negative length");
        }
        if (NIL_P(string)) return 0;
        plen = RSTRING_LEN(string);
        rb_str_resize(string, l);
        if (plen < l) {
            MEMZERO(RSTRING_PTR(string) + plen, char, l - plen);
        }
        return INT2FIX(0);
    }

Truncates the buffer string to at most integer bytes. The stream must be opened for writing.

tty? ()

Returns `false`; for compatibility with `IO`.

Alias for: isatty
ungetbyte(byte) → nil
Source

    static VALUE
    strio_ungetbyte(VALUE self, VALUE c)
    {
        struct StringIO *ptr = readable(self);

        check_modifiable(ptr);
        if (NIL_P(ptr->string)) return Qnil;
        if (NIL_P(c)) return Qnil;
        if (RB_INTEGER_TYPE_P(c)) {
            /* rb_int_and() not visible from exts */
            VALUE v = rb_funcall(c, '&', 1, INT2FIX(0xff));
            const char cc = NUM2INT(v) & 0xFF;
            strio_unget_bytes(ptr, &cc, 1);
        }
        else {
            StringValue(c);
            strio_unget_string(ptr, c);
        }
        return Qnil;
    }

Pushes back (“unshifts”) an 8-bit byte onto the stream; see Byte IO.

ungetc(character) → nil
Source

    static VALUE
    strio_ungetc(VALUE self, VALUE c)
    {
        struct StringIO *ptr = readable(self);
        rb_encoding *enc, *enc2;

        check_modifiable(ptr);
        if (NIL_P(ptr->string)) return Qnil;
        if (NIL_P(c)) return Qnil;
        if (RB_INTEGER_TYPE_P(c)) {
            int len, cc = NUM2INT(c);
            char buf[16];

            enc = rb_enc_get(ptr->string);
            len = rb_enc_codelen(cc, enc);
            if (len <= 0) {
                rb_enc_uint_chr(cc, enc); /* to raise an exception */
                UNREACHABLE;
            }
            rb_enc_mbcput(cc, buf, enc);
            return strio_unget_bytes(ptr, buf, len);
        }
        else {
            StringValue(c);
            if (RSTRING_LEN(c) == 0) return Qnil;
            enc = rb_enc_get(ptr->string);
            enc2 = rb_enc_get(c);
            if (enc != enc2 && enc != rb_ascii8bit_encoding()) {
                c = rb_str_conv_enc(c, enc2, enc);
            }
            strio_unget_string(ptr, c);
            return Qnil;
        }
    }

Pushes back (“unshifts”) a character or integer onto the stream; see Character IO.

write(string, ...) → integer
syswrite(string) → integer
Source

    static VALUE
    strio_write_m(int argc, VALUE *argv, VALUE self)
    {
        long len = 0;
        while (argc-- > 0) {
            /* StringIO can't exceed long limit */
            len += strio_write(self, *argv++);
        }
        return LONG2NUM(len);
    }

Appends the given string to the underlying buffer string. The stream must be opened for writing. If the argument is not a string, it will be converted to a string using `to_s`. Returns the number of bytes written. See `IO#write`.