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':只读 |
否 | 任何位置 | Error |
'w':只写 |
是 | Error | 任何位置 |
'a':仅追加 |
否 | Error | 仅末尾 |
'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>
位置
流有一个 *位置*,即流中的字节偏移量。流的初始位置为零。
获取和设置位置
以下每个方法都会初始化(设置为零)新流或重新打开的流的位置:
以下每个方法都会查询、获取或设置位置,而不会更改流的其他属性:
示例
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
以下每个方法都会在当前位置 *之前* 写入,并使位置递减,以便下一个读取的数据是刚刚写入的数据:
示例
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
打开/关闭的流
新流为读取或写入而打开,并且可以同时为两者打开;参见 读/写模式。
以下每个方法都会初始化新流或重新打开的流的读/写模式。
其他相关方法
-
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.
基本流 IO
基本读取
您可以使用以下实例方法从流中读取:
-
getbyte:读取并返回下一个字节。 -
getc:读取并返回下一个字符。 -
gets:读取并返回下一行或行的部分。 -
read:读取并返回流中剩余的全部或部分数据。 -
readlines:读取流的剩余数据并返回其行的数组。 -
Kernel#readline:与
gets类似,但在流末尾会引发异常。
您可以使用以下实例方法遍历流:
-
each_byte:读取每个剩余字节,并将其传递给块。 -
each_char:读取每个剩余字符,并将其传递给块。 -
each_codepoint:读取每个剩余代码点,并将其传递给块。 -
each_line:读取剩余行的全部或部分,并将读取的字符串传递给块。
此实例方法在多线程应用程序中很有用:
-
pread:读取并返回流的全部或部分。
基本写入
您可以使用以下实例方法向流写入,并使位置前进:
-
putc:写入给定的字符。 -
write:将给定的对象作为字符串写入。 -
Kernel#puts:将给定的对象作为字符串写入,每个对象后跟一个换行符。
您可以使用以下实例方法“取消入栈”到流中;每个方法都会在当前位置 *之前* 写入,并使位置递减,以便下一个读取的数据是刚刚写入的数据。
另一个写入方法
-
truncate:将流的字符串截断到给定大小。
行 IO
读取
-
gets:读取并返回下一行。 -
Kernel#readline:与
gets类似,但在流末尾会引发异常。 -
readlines:读取流的剩余数据并返回其行的数组。 -
each_line:读取每个剩余行,并将其传递给块。
写入
-
Kernel#puts:将给定的对象写入,每个对象后跟一个换行符。
字符 IO
读取
写入
字节 IO
读取
写入
-
ungetbyte:将给定的字节推回(“取消入栈”)。
代码点 IO
读取
-
each_codepoint:读取每个剩余代码点,并将其传递给块。
Constants
- MAX_LENGTH
-
StringIO实例可以容纳的最大长度。 - VERSION
-
版本字符串
Public Class Methods
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);
}
返回一个由 string 和 mode 组成的新 StringIO 实例;当不再需要时,应关闭该实例。
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 对象传递给块;块退出时自动关闭对象)。
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);
}
通过调用 StringIO.new(string, mode) 创建新的 StringIO 实例。
如果没有给出块,则返回新实例。
strio = StringIO.open # => #<StringIO>
给出了块,则使用新实例调用该块,并返回块的值;块退出时关闭实例。
StringIO.open('foo') {|strio| strio.string.upcase } # => "FOO"
相关:StringIO.new。
Public Instance Methods
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;
}
将 self 的数据模式设置为二进制模式;参见 数据模式。
Source
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?。
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;
}
关闭 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
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;
}
关闭 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?。
Source
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.
Source
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。
Source
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?。
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;
}
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;
}
给出了块,则将流中的每个剩余字节传递给块;将流定位到文件末尾;返回 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。
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;
}
给出了块,则将流中的每个剩余字符传递给块;将流定位到文件末尾;返回 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。
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;
}
给出了块,则将流中的每个连续代码点传递给块;将位置设置为流末尾;返回 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 时,使用默认记录分隔符读取行;还将每行的长度(以字符为单位)限制为给定的 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"
如果没有给出块,则返回一个新的 Enumerator。
相关:StringIO.each_byte、StringIO.each_char、StringIO.each_codepoint。
Source
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。
Source
static VALUE
strio_external_encoding(VALUE self)
{
struct StringIO *ptr = StringIO(self);
return rb_enc_from_encoding(get_enc(ptr));
}
Source
static VALUE
strio_unimpl(int argc, VALUE *argv, VALUE self)
{
StringIO(self);
rb_notimplement();
UNREACHABLE;
}
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);
}
从流中读取并返回下一个整数字节(不是字符)。
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
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);
}
从流中读取并返回下一个字符(或字节;见下文)。
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"
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;
}
从流中读取并返回一行;如果到达流末尾,则返回 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 时,使用默认记录分隔符读取一行;还将每行的长度(以字符为单位)限制为给定的 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"
Source
static VALUE
strio_false(VALUE self)
{
StringIO(self);
return Qfalse;
}
返回 false;为了与 IO 兼容。
Source
static VALUE
strio_get_lineno(VALUE self)
{
return LONG2NUM(StringIO(self)->lineno);
}
返回 self 的当前行号;参见 行号。
Source
static VALUE
strio_set_lineno(VALUE self, VALUE lineno)
{
StringIO(self)->lineno = NUM2LONG(lineno);
return lineno;
}
将 self 的当前行号设置为给定的 new_line_number;参见 行号。
Source
static VALUE
strio_get_pos(VALUE self)
{
return LONG2NUM(StringIO(self)->pos);
}
返回当前位置(以字节为单位);参见 位置。
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;
}
设置当前位置(以字节为单位);参见 位置。
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;
}
参见 IO#pread。
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;
}
参见 IO#putc。
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;
}
参见 IO#read。
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;
}
参见 IO#readlines。
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);
}
Source
static VALUE
strio_rewind(VALUE self)
{
struct StringIO *ptr = StringIO(self);
ptr->pos = 0;
ptr->lineno = 0;
return INT2FIX(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);
}
根据给定的常量 whence 将位置设置为给定的整数 offset(以字节为单位);参见 IO#seek。
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;
}
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);
}
根据字符串中的 BOM(字节顺序标记)设置编码。
如果找到 BOM,则返回 self,否则返回 +nil+。
Source
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.
Source
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=(赋值底层字符串)。
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;
}
用 other_string 替换存储的字符串,并将位置设置为零;返回 other_string。
StringIO.open('foo') do |strio| p strio.string strio.string = 'bar' p strio.string end
输出
"foo" "bar"
相关:StringIO#string(返回存储的字符串)。
Source
static VALUE
strio_get_sync(VALUE self)
{
StringIO(self);
return Qtrue;
}
返回 true;仅为与其他流类兼容而实现。
Source
static VALUE
strio_first(VALUE self, VALUE arg)
{
StringIO(self);
return arg;
}
返回参数不变。仅为了与 IO 兼容。
Source
static VALUE
strio_get_pos(VALUE self)
{
return LONG2NUM(StringIO(self)->pos);
}
返回当前位置(以字节为单位);参见 位置。
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);
}
将缓冲区字符串截断为最多 integer 字节。流必须以写入模式打开。
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;
}
将一个 8 位字节推回到流中(“取消入栈”);参见 字节 IO。
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;
}
}
将一个字符或整数推回到流中(“取消入栈”);参见 字符 IO。
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);
}
将给定的字符串追加到底层缓冲区字符串。流必须以写入模式打开。如果参数不是字符串,它将使用 to_s 转换为字符串。返回写入的字节数。参见 IO#write。