class StringIO
Класс StringIO поддерживает доступ к строке как к потоку, в некотором отношении подобному классу IO.
Создать экземпляр StringIO можно с помощью:
-
StringIO.new: возвращает новый объект StringIO, содержащий заданную строку. -
StringIO.open: передаёт новый объект StringIO заданному блоку.
Как и поток IO, поток StringIO имеет определённые свойства:
-
Режим чтения/записи: можно ли читать из потока, записывать в него, добавлять данные и т. д.; см. раздел Режим чтения/записи.
-
Режим данных: только текст или двоичные данные; см. раздел Режим данных.
-
Кодировки: внутренняя и внешняя кодировки; см. раздел Кодировки.
-
Позиция: место в потоке, откуда будет выполнено следующее чтение или куда будет выполнена следующая запись; см. раздел Позиция.
-
Номер строки: специальная «позиция», ориентированная на строки (отличается от упомянутой выше позиции); см. раздел Номер строки.
-
Открыт/закрыт: открыт или закрыт поток для чтения или записи. См. раздел Открытые/закрытые потоки.
-
BOM: метка порядка байтов; см. раздел Метка порядка байтов.
Об этих примерах
В примерах на этой странице предполагается, что подключён StringIO:
require 'stringio'
И что определена эта константа:
TEXT = <<EOT First line Second line Fourth line Fifth line EOT
Свойства потока
Режим чтения/записи
Краткое описание
| Режим | Изначально очищен? | Чтение | Запись |
|---|---|---|---|
'r': только для чтения | Нет | В любом месте | Ошибка |
'w': только для записи | Да | Ошибка | В любом месте |
'a': только добавление | Нет | Ошибка | Только в конце |
'r+': чтение/запись | Нет | В любом месте | В любом месте |
'w+': чтение-запись | Да | В любом месте | В любом месте |
'a+': чтение/добавление | Нет | В любом месте | Только в конце |
В каждом разделе ниже описан режим чтения/записи.
Любой режим можно указать строкой или файловой константой; пример:
strio = StringIO.new('foo', 'a')
strio = StringIO.new('foo', File::WRONLY | File::APPEND)
'r': только для чтения
Режим задаётся одним из следующих способов:
-
Строка:
'r'. -
Константа:
File::RDONLY.
Начальное состояние:
strio = StringIO.new('foobarbaz', 'r')
strio.pos # => 0 # Beginning-of-stream.
strio.string # => "foobarbaz" # Not cleared.
Читать можно в любом месте:
strio.gets(3) # => "foo" strio.gets(3) # => "bar" strio.pos = 9 strio.gets(3) # => nil
Запись невозможна:
strio.write('foo') # Raises IOError: not opened for writing
'w': только для записи
Режим задаётся одним из следующих способов:
-
Строка:
'w'. -
Константа:
File::WRONLY.
Начальное состояние:
strio = StringIO.new('foo', 'w')
strio.pos # => 0 # Beginning of stream.
strio.string # => "" # Initially cleared.
Записывать можно в любом месте (даже за концом потока):
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.
Чтение невозможно:
strio.read # Raises IOError: not opened for reading
'a': только добавление
Режим задаётся одним из следующих способов:
-
Строка:
'a'. -
Константа:
File::WRONLY | File::APPEND.
Начальное состояние:
strio = StringIO.new('foo', 'a')
strio.pos # => 0 # Beginning-of-stream.
strio.string # => "foo" # Not cleared.
Записывать можно только в конце; позиция не влияет на запись:
strio.write('bar')
strio.string # => "foobar"
strio.write('baz')
strio.string # => "foobarbaz"
strio.pos = 400
strio.write('bat')
strio.string # => "foobarbazbat"
Чтение невозможно:
strio.gets # Raises IOError: not opened for reading
'r+': чтение/запись
Режим задаётся одним из следующих способов:
-
Строка:
'r+'. -
Константа:
File::RDRW.
Начальное состояние:
strio = StringIO.new('foobar', 'r+')
strio.pos # => 0 # Beginning-of-stream.
strio.string # => "foobar" # Not cleared.
Записывать можно в любом месте (даже за концом потока):
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.
Читать можно в любом месте:
strio.pos = 0 strio.gets(3) # => "FOO" strio.pos = 6 strio.gets(3) # => "BAZ" strio.pos = 400 strio.gets(3) # => nil
'w+': чтение/запись (изначально очищен)
Режим задаётся одним из следующих способов:
-
Строка:
'w+'. -
Константа:
File::RDWR | File::TRUNC.
Начальное состояние:
strio = StringIO.new('foo', 'w+')
strio.pos # => 0 # Beginning-of-stream.
strio.string # => "" # Truncated.
Записывать можно в любом месте (даже за концом потока):
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.
Читать можно в любом месте:
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+': чтение/добавление
Режим задаётся одним из следующих способов:
-
Строка:
'a+'. -
Константа:
File::RDWR | File::APPEND.
Начальное состояние:
strio = StringIO.new('foo', 'a+')
strio.pos # => 0 # Beginning-of-stream.
strio.string # => "foo" # Not cleared.
Записывать можно только в конце; rewind; позиция не влияет на запись:
strio.write('bar')
strio.string # => "foobar"
strio.write('baz')
strio.string # => "foobarbaz"
strio.pos = 400
strio.write('bat')
strio.string # => "foobarbazbat"
Читать можно в любом месте:
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 режим
Чтобы указать, следует ли обрабатывать поток как текст или как двоичные данные, к любому из описанных выше строковых режимов чтения/записи можно добавить один из следующих суффиксов:
-
't': текст; инициализирует кодировку как Encoding::UTF_8. -
'b': двоичные данные; инициализирует кодировку как Encoding::ASCII_8BIT.
Если не указан ни один из них, по умолчанию поток содержит текстовые данные.
Примеры:
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)>
Если режим данных указан, режим чтения/записи нельзя опускать:
StringIO.new(data, 'b') # Raises ArgumentError: invalid access mode b
Текстовый поток можно преобразовать в двоичный, вызвав метод экземпляра binmode; двоичный поток нельзя преобразовать в текстовый.
Кодировки
У потока есть кодировка; см. раздел Кодировки.
Начальная кодировка нового или повторно открытого потока зависит от его режима данных:
-
Текст:
Encoding::UTF_8. -
Двоичные данные:
Encoding::ASCII_8BIT.
Важны следующие методы экземпляра:
-
external_encoding: возвращает текущую кодировку потока в виде объектаEncoding. -
internal_encoding: возвращаетnil; у потока нет внутренней кодировки. -
set_encoding: задаёт кодировку потока. -
set_encoding_by_bom: задаёт кодировку потока в соответствии с BOM (меткой порядка байтов) потока.
Примеры:
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>
Позиция
У потока есть позиция — целочисленное смещение (в байтах) внутри потока. Начальная позиция потока равна нулю.
Получение и установка позиции
Каждый из этих методов устанавливает позицию нового или повторно открытого потока в ноль:
-
::new: возвращает новый поток. -
::open: передаёт новый поток блоку. -
reopen: повторно инициализирует поток.
Каждый из этих методов запрашивает, получает или устанавливает позицию, не изменяя при этом поток иным образом:
-
eof?: возвращает, находится ли позиция в конце потока. -
pos: возвращает позицию. -
pos=: задаёт позицию. -
rewind: устанавливает позицию в ноль. -
seek: задаёт позицию.
Примеры:
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
Позиция до и после чтения
За исключением pread, метод чтения из потока (см. раздел Основы чтения) начинает чтение с текущей позиции.
За исключением pread, метод чтения перемещает позицию за пределы прочитанной подстроки.
Примеры:
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.
Позиция до и после записи
Каждый из этих методов начинает запись с текущей позиции и перемещает позицию в конец записанной подстроки:
-
putc: записывает заданный символ. -
write: записывает заданные объекты как строки. -
Kernel#puts: записывает заданные объекты как строки, добавляя после каждой перевод строки.
Примеры:
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
Каждый из этих методов записывает данные перед текущей позицией и уменьшает позицию, чтобы записанные данные стали следующими для чтения:
-
ungetbyte: помещает заданный байт обратно в поток. -
ungetc: помещает заданный символ обратно в поток.
Примеры:
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"
Этот метод не влияет на позицию:
-
truncate: усекает строку потока до заданного размера.
Примеры:
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
Номер строки
У потока есть номер строки, изначально равный нулю:
На номер строки может влиять чтение (но никогда — запись); как правило, номер строки увеличивается каждый раз при чтении разделителя записей (по умолчанию: "\n").
Примеры:
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
Установка позиции не влияет на номер строки:
strio.pos = 0 strio.lineno # => 4 strio.gets # => "First line\n" strio.pos # => 11 strio.lineno # => 5
И установка номера строки не влияет на позицию:
strio.lineno = 10 strio.pos # => 11 strio.gets # => "Second line\n" strio.lineno # => 11 strio.pos # => 23
Открытые/закрытые потоки
Новый поток открыт для чтения или записи и может быть открыт для обеих операций; см. раздел Режим чтения/записи.
Каждый из этих методов устанавливает режим чтения/записи нового или повторно открытого потока:
-
::new: возвращает новый поток. -
::open: передаёт новый поток блоку. -
reopen: повторно инициализирует поток.
Другие важные методы:
-
close: закрывает поток для чтения и записи. -
close_read: закрывает поток для чтения. -
close_write: закрывает поток для записи. -
closed?: возвращает, закрыт ли поток для чтения и записи. -
closed_read?: возвращает, закрыт ли поток для чтения. -
closed_write?: возвращает, закрыт ли поток для записи.
BOM (метка порядка байтов)
Строка, переданная в ::new, ::open или reopen, может содержать в начале необязательную BOM (метку порядка байтов); BOM может повлиять на кодировку потока.
BOM (если она указана):
-
Хранится как часть строки потока.
-
Не влияет на кодировку немедленно.
-
Изначально считается частью потока.
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.
Можно вызвать метод экземпляра set_encoding_by_bom, чтобы «активировать» сохранённую BOM; после этого BOM:
-
По-прежнему хранится как часть строки потока.
-
Определяет (и может изменять) кодировку потока.
-
Больше не считается частью потока.
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.
Базовый ввод-вывод потока
Базовое чтение
Для чтения из потока можно использовать следующие методы экземпляра:
-
getbyte: читает и возвращает следующий байт. -
getc: читает и возвращает следующий символ. -
gets: читает и возвращает всю следующую строку или её часть. -
read: читает и возвращает все оставшиеся данные потока или их часть. -
readlines: читает оставшиеся данные потока и возвращает массив его строк. -
Kernel#readline: работает как
gets, но вызывает исключение при достижении конца потока.
Перебирать элементы потока можно с помощью следующих методов экземпляра:
-
each_byte: читает каждый оставшийся байт и передаёт его блоку. -
each_char: читает каждый оставшийся символ и передаёт его блоку. -
each_codepoint: читает каждую оставшуюся кодовую точку и передаёт её блоку. -
each_line: читает каждую оставшуюся строку целиком или частично и передаёт прочитанную строку блоку
Этот метод экземпляра полезен в многопоточном приложении:
-
pread: читает и возвращает весь поток или его часть.
Базовая запись
Для записи в поток с перемещением позиции можно использовать следующие методы экземпляра:
-
putc: записывает заданный символ. -
write: записывает заданные объекты в виде строк. -
Kernel#puts записывает заданные объекты в виде строк, добавляя после каждой символ новой строки.
С помощью следующих методов экземпляра можно «поместить данные в начало» потока: каждый из них записывает данные перед текущей позицией и уменьшает позицию, чтобы записанные данные стали следующими для чтения.
-
ungetbyte: помещает заданный байт перед текущей позицией. -
ungetc: помещает заданный символ перед текущей позицией.
Ещё один метод записи:
-
truncate: обрезает строку потока до заданного размера.
Ввод-вывод строк
Чтение:
-
gets: читает и возвращает следующую строку. -
Kernel#readline: работает как
gets, но вызывает исключение при достижении конца потока. -
readlines: читает оставшиеся данные потока и возвращает массив его строк. -
each_line: читает каждую оставшуюся строку и передаёт её блоку
Запись:
-
Kernel#puts: записывает заданные объекты, добавляя после каждого символ новой строки.
Ввод-вывод символов
Чтение:
-
each_char: читает каждый оставшийся символ и передаёт его блоку. -
getc: читает и возвращает следующий символ.
Запись:
Ввод-вывод байтов
Чтение:
-
each_byte: читает каждый оставшийся байт и передаёт его блоку. -
getbyte: читает и возвращает следующий байт.
Запись:
-
ungetbyte: помещает заданный байт перед текущей позицией.
Ввод-вывод кодовых точек
Чтение:
-
each_codepoint: читает каждую оставшуюся кодовую точку и передаёт её блоку.
Константы
- MAX_LENGTH
-
Максимальная длина, которую может иметь экземпляр
StringIO - VERSION
-
Строка версии
Открытые методы класса
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);
} Возвращает новый экземпляр StringIO, созданный на основе string и mode; экземпляр следует закрыть, когда он больше не нужен:
strio = StringIO.new strio.string # => "" strio.closed_read? # => false strio.closed_write? # => false strio.close
Если string заморожена, значением mode по умолчанию будет 'r':
strio = StringIO.new('foo'.freeze)
strio.string # => "foo"
strio.closed_read? # => false
strio.closed_write? # => true
strio.close
Аргумент mode должен быть допустимым режимом доступа — строкой или целочисленной константой:
StringIO.new('foo', 'w+')
StringIO.new('foo', File::RDONLY)
См. также: StringIO.open (передаёт объект StringIO блоку и автоматически закрывает объект по завершении блока).
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);
} Создаёт новый экземпляр StringIO, вызывая StringIO.new(string, mode).
Если блок не задан, возвращает новый экземпляр:
strio = StringIO.open # => #<StringIO>
Если блок задан, вызывает его с новым экземпляром и возвращает значение блока; по завершении блока экземпляр закрывается:
StringIO.open('foo') {|strio| strio.string.upcase } # => "FOO"
См. также: StringIO.new.
Общедоступные методы экземпляра
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;
} Устанавливает двоичный режим данных в self; см. раздел Режим данных.
static VALUE
strio_close(VALUE self)
{
StringIO(self);
RBASIC(self)->flags &= ~STRIO_READWRITE;
return Qnil;
} Закрывает self для чтения и записи; возвращает 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
Связанные методы: StringIO#close_read, StringIO#close_write, StringIO.closed?.
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;
} Закрывает self для чтения; настройка закрытия для записи остаётся без изменений; возвращает 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
Связанные методы: StringIO#close, StringIO#close_write.
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;
} Закрывает self для записи; настройка закрытия для чтения остаётся без изменений; возвращает 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
Связанные методы: StringIO#close, StringIO#close_read, StringIO#closed_write?.
static VALUE
strio_closed(VALUE self)
{
StringIO(self);
if (!CLOSED(self)) return Qfalse;
return Qtrue;
} Возвращает информацию о том, закрыт ли self для чтения и записи:
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.
Связанные методы: StringIO.closed_read?, StringIO.closed_write?.
static VALUE
strio_closed_read(VALUE self)
{
StringIO(self);
if (READABLE(self)) return Qfalse;
return Qtrue;
} Возвращает информацию о том, закрыт ли self для чтения:
strio = StringIO.new strio.closed_read? # => false strio.close_read strio.closed_read? # => true
Связанные методы: StringIO#closed?, StringIO#closed_write?, StringIO#close_read.
static VALUE
strio_closed_write(VALUE self)
{
StringIO(self);
if (WRITABLE(self)) return Qfalse;
return Qtrue;
} Возвращает информацию о том, закрыт ли self для записи:
strio = StringIO.new strio.closed_write? # => false strio.close_write strio.closed_write? # => true
Связанные методы: StringIO#close_write, StringIO#closed?, StringIO#closed_read?.
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;
} 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;
} Если передан блок, вызывает его для каждого оставшегося байта в потоке; перемещает позицию потока в конец файла; возвращает 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]
Позиция в потоке имеет значение:
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]
Если достигнут конец файла, блок не вызывается:
strio.eof? # => true
strio.each_byte {|byte| fail 'Boo!' }
strio.eof? # => true
Если блок не передан, возвращает новый Enumerator.
Связанные методы: StringIO#each_char, StringIO#each_codepoint, StringIO#each_line.
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;
} Если передан блок, вызывает его для каждого оставшегося символа в потоке; перемещает позицию потока в конец файла; возвращает 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 # => ["こ", "ん", "に", "ち", "は"]
Позиция потока имеет значение:
chars = []
strio = StringIO.new('こんにちは')
strio.getc # => "こ"
strio.pos # => 3 # 3-byte character was read.
strio.each_char {|char| chars.push(char) }
chars # => ["ん", "に", "ち", "は"]
Если достигнут конец потока, блок не вызывается:
strio.eof? # => true
strio.each_char {|char| fail 'Boo!' }
strio.eof? # => true
Если блок не передан, возвращает новый Enumerator.
Связанные методы: StringIO#each_byte, StringIO#each_codepoint, StringIO#each_line.
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;
} Если передан блок, вызывает его для каждой следующей кодовой точки из self; устанавливает позицию в конец потока; возвращает self.
Каждая кодовая точка — это целочисленное значение символа; возвращает 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]
Позиция в потоке имеет значение:
codepoints = []
strio = StringIO.new('こんにちは')
strio.getc # => "こ"
strio.pos # => 3
strio.each_codepoint {|codepoint| codepoints.push(codepoint) }
codepoints # => [12435, 12395, 12385, 12399]
Если достигнут конец потока, блок не вызывается:
strio.eof? # => true
strio.each_codepoint {|codepoint| fail 'Boo!' }
strio.eof? # => true
Если блок не передан, возвращает новый Enumerator.
Связанные методы: StringIO#each_byte, StringIO#each_char, StringIO#each_line.
Если передан блок, вызывает его для каждой оставшейся строки (см. раздел «Позиция» ниже) в потоке; возвращает self.
Оставляет позицию потока в конце потока.
Без аргументов
Если аргументы не переданы, читает строки, используя разделитель записей по умолчанию (глобальную переменную $/, начальное значение которой — "\n").
strio = StringIO.new(TEXT)
strio.each_line {|line| p line }
strio.eof? # => true
Вывод:
"First line\n" "Second line\n" "\n" "Fourth line\n" "Fifth line\n"
Аргумент sep
Если передан только строковый аргумент sep, читает строки, используя эту строку в качестве разделителя записей:
strio = StringIO.new(TEXT)
strio.each_line(' ') {|line| p line }
Вывод:
"First " "line\nSecond " "line\n\nFourth " "line\nFifth " "line\n"
Аргумент limit
Если передан только целочисленный аргумент limit, читает строки, используя разделитель записей по умолчанию; также ограничивает размер каждой строки (в символах) заданным пределом:
strio = StringIO.new(TEXT)
strio.each_line(10) {|line| p line }
Вывод:
"First line" "\n" "Second lin" "e\n" "\n" "Fourth lin" "e\n" "Fifth line" "\n"
Аргументы sep и limit
Если переданы оба аргумента — sep и limit, — учитываются оба:
strio = StringIO.new(TEXT)
strio.each_line(' ', 10) {|line| p line }
Вывод:
"First " "line\nSecon" "d " "line\n\nFour" "th " "line\nFifth" " " "line\n"
Позиция
Как указано выше, метод each оставшуюся строку в потоке.
В примерах выше позиция каждого объекта strio находится в начале потока; в других случаях позиция может быть любой (см. StringIO#pos):
strio = StringIO.new(TEXT)
strio.pos = 30 # Set stream position to character 30.
strio.each_line {|line| p line }
Вывод:
" line\n" "Fifth line\n"
Во всех приведённых выше примерах позиция потока находится в начале символа; в других случаях это не обязательно так:
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 }
Вывод:
"んにちは" "\x82\x93にちは" "\x93にちは"
Специальные разделители записей
Как и некоторые методы класса IO, StringIO.each обрабатывает два специальных разделителя записей; см. раздел Специальные значения разделителя строк.
strio = StringIO.new(TEXT)
strio.each_line('') {|line| p line } # Read as paragraphs (separated by blank lines).
Вывод:
"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.
Вывод:
"First line\nSecond line\n\nFourth line\nFifth line\n"
Именованный аргумент chomp
Если именованный аргумент chomp задан как true (значение по умолчанию — false), удаляет завершающий символ новой строки (если он есть) из каждой строки:
strio = StringIO.new(TEXT)
strio.each_line(chomp: true) {|line| p line }
Вывод:
"First line" "Second line" "" "Fourth line" "Fifth line"
Если блок не передан, возвращает новый перечислитель.
Связанные методы: StringIO.each_byte, StringIO.each_char, StringIO.each_codepoint.
static VALUE
strio_eof(VALUE self)
{
if (strio_to_read(self)) return Qfalse;
return Qtrue;
} Возвращает информацию о том, находится ли self в конце потока:
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
Связанный метод: StringIO#pos.
static VALUE
strio_external_encoding(VALUE self)
{
struct StringIO *ptr = StringIO(self);
return rb_enc_from_encoding(get_enc(ptr));
} static VALUE
strio_unimpl(int argc, VALUE *argv, VALUE self)
{
StringIO(self);
rb_notimplement();
UNREACHABLE;
} Вызывает исключение NotImplementedError.
static VALUE
strio_nil(VALUE self)
{
StringIO(self);
return Qnil;
} Возвращает nil; для совместимости с IO.
static VALUE
strio_self(VALUE self)
{
StringIO(self);
return self;
} Возвращает self; для совместимости с IO.
static VALUE
strio_0(VALUE self)
{
StringIO(self);
return INT2FIX(0);
} Возвращает 0; для совместимости с IO.
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);
} Читает и возвращает следующий целочисленный байт (не символ) из потока:
s = 'foo' s.bytes # => [102, 111, 111] strio = StringIO.new(s) strio.getbyte # => 102 strio.getbyte # => 111 strio.getbyte # => 111
Возвращает nil в конце потока:
strio.eof? # => true strio.getbyte # => nil
Возвращает байт, а не символ:
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
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);
} Читает и возвращает следующий символ (или байт; см. ниже) из потока:
strio = StringIO.new('foo')
strio.getc # => "f"
strio.getc # => "o"
strio.getc # => "o"
Возвращает nil в конце потока:
strio.eof? # => true strio.getc # => nil
Возвращает символы, а не байты:
strio = StringIO.new('Привет')
strio.getc # => "П"
strio.getc # => "р"
strio = StringIO.new('こんにちは')
strio.getc # => "こ"
strio.getc # => "ん"
В каждом из приведённых выше примеров поток находится в начале символа; в других случаях это не обязательно так:
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"
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;
} Читает и возвращает строку из потока; возвращает nil в конце потока.
Побочные эффекты:
-
Увеличивает позицию потока на число прочитанных байтов.
-
Присваивает возвращаемое значение глобальной переменной
$_.
Если аргументы не указаны, читает строку, используя разделитель записей по умолчанию (глобальная переменная $/,* начальное значение которой — "\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
Аргумент sep
Если указан только строковый аргумент sep, читает строку, используя эту строку в качестве разделителя записей:
strio = StringIO.new(TEXT)
strio.gets(' ') # => "First "
strio.gets(' ') # => "line\nSecond "
strio.gets(' ') # => "line\n\nFourth "
Аргумент limit
Если указан только целочисленный аргумент limit, читает строку, используя разделитель записей по умолчанию; ограничивает размер (в символах) каждой строки указанным значением:
strio = StringIO.new(TEXT) strio.gets(10) # => "First line" strio.gets(10) # => "\n" strio.gets(10) # => "Second lin" strio.gets(10) # => "e\n"
Аргументы sep и limit
Если указаны оба аргумента — sep и limit, учитываются оба:
strio = StringIO.new(TEXT)
strio.gets(' ', 10) # => "First "
strio.gets(' ', 10) # => "line\nSecon"
strio.gets(' ', 10) # => "d "
Позиция
Как указано выше, метод gets читает и возвращает следующую строку в потоке.
В примерах выше каждый объект strio начинается с позиции в начале потока; в других случаях позиция может находиться где угодно:
strio = StringIO.new(TEXT) strio.pos = 12 strio.gets # => "econd line\n"
Позиция не обязательно должна находиться на границе символа:
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ивет"
Специальные разделители записей
Как и некоторые методы класса IO, метод gets учитывает два специальных разделителя записей; см. Специальные значения разделителя строк:
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"
Именованный аргумент chomp
Если именованный аргумент chomp задан как true (значение по умолчанию — false), удаляет завершающий символ новой строки (если он есть) из возвращаемой строки:
strio = StringIO.new(TEXT) strio.gets # => "First line\n" strio.gets(chomp: true) # => "Second line"
Связанные методы: each_line, readlines, Kernel#puts.
static VALUE
strio_internal_encoding(VALUE self)
{
return Qnil;
} Возвращает nil; для совместимости с IO.
static VALUE
strio_false(VALUE self)
{
StringIO(self);
return Qfalse;
} Возвращает false; для совместимости с IO.
static VALUE
strio_get_lineno(VALUE self)
{
return LONG2NUM(StringIO(self)->lineno);
} Возвращает текущий номер строки в self; см. Номер строки.
static VALUE
strio_set_lineno(VALUE self, VALUE lineno)
{
StringIO(self)->lineno = NUM2LONG(lineno);
return lineno;
} Устанавливает текущий номер строки в self в заданное значение new_line_number; см. Номер строки.
static VALUE
strio_nil(VALUE self)
{
StringIO(self);
return Qnil;
} Возвращает nil; для совместимости с IO.
static VALUE
strio_get_pos(VALUE self)
{
return LONG2NUM(StringIO(self)->pos);
} Возвращает текущую позицию (в байтах); см. Позиция.
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;
} Устанавливает текущую позицию (в байтах); см. Позиция.
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;
} См. IO#pread.
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;
} См. IO#putc.
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;
} См. IO#read.
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;
} См. IO#readlines.
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);
} static VALUE
strio_rewind(VALUE self)
{
struct StringIO *ptr = StringIO(self);
ptr->pos = 0;
ptr->lineno = 0;
return INT2FIX(0);
} Устанавливает текущую позицию и номер строки в ноль; см. Позиция и Номер строки.
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);
} Устанавливает позицию на заданное целое число offset (в байтах) относительно заданной константы whence; см. IO#seek.
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;
} 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);
} Устанавливает кодировку в соответствии с BOM (маркером порядка байтов) в строке.
Возвращает self, если BOM найден, в противном случае — +nil.
static VALUE
strio_size(VALUE self)
{
VALUE string = StringIO(self)->string;
if (NIL_P(string)) {
return INT2FIX(0);
}
return ULONG2NUM(RSTRING_LEN(string));
} Возвращает количество байтов в строке в 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.
static VALUE
strio_get_string(VALUE self)
{
return StringIO(self)->string;
} Возвращает базовую строку:
StringIO.open('foo') do |strio|
p strio.string
strio.string = 'bar'
p strio.string
end
Вывод:
"foo" "bar"
Связанный метод: StringIO#string= (назначает базовую строку).
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;
} Заменяет сохраненную строку на other_string и устанавливает позицию в ноль; возвращает other_string:
StringIO.open('foo') do |strio|
p strio.string
strio.string = 'bar'
p strio.string
end
Вывод:
"foo" "bar"
Связанный метод: StringIO#string (возвращает сохраненную строку).
static VALUE
strio_get_sync(VALUE self)
{
StringIO(self);
return Qtrue;
} Возвращает true; реализован только для совместимости с другими классами потоков.
static VALUE
strio_first(VALUE self, VALUE arg)
{
StringIO(self);
return arg;
} Возвращает аргумент без изменений. Предусмотрен только для совместимости с IO.
static VALUE
strio_get_pos(VALUE self)
{
return LONG2NUM(StringIO(self)->pos);
} Возвращает текущую позицию (в байтах); см. Позиция.
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);
} Обрезает строку буфера до длины не более integer байтов. Поток должен быть открыт для записи.
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;
} Возвращает 8-битный байт в поток («сдвигает его обратно»); см. Ввод-вывод байтов.
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;
}
} Возвращает символ или целое число в поток («сдвигает его обратно»); см. Ввод-вывод символов.
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);
} Добавляет заданную строку в базовую строку буфера. Поток должен быть открыт для записи. Если аргумент не является строкой, он будет преобразован в строку с помощью to_s. Возвращает количество записанных байтов. См. IO#write.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.