类 IO

类 IO 的实例(通常称为)表示底层操作系统中的输入/输出流。类 IO 是 Ruby 中输入和输出的基础。

File 是 Ruby 核心库中唯一一个 IO 的子类。Ruby 标准库中的一些类也是 IO 的子类;其中包括 TCPSocketUDPSocket

全局常量 ARGF(也可以通过 $< 访问)提供一个类似 IO 的流,允许访问 ARGV 中找到的所有文件路径(如果 ARGV 为空,则在 STDIN 中找到)。ARGF 本身不是 IO 的子类。

StringIO 提供一个类似 IO 的流,用于处理 StringStringIO 本身不是 IO 的子类。

基于 IO 的重要对象包括

可以使用以下方式创建 IO 实例

File 流类似,IO 流具有

与其他 IO 流一样,它具有

扩展 io/console

扩展 io/console 提供了大量与控制台交互的方法;需要它为 IO 类添加大量方法。

示例文件

此处许多示例使用这些变量

# English text with newlines.
text = <<~EOT
  First line
  Second line

  Fourth line
  Fifth line
EOT

# Russian text.
russian = "\u{442 435 441 442}" # => "тест"

# Binary data.
data = "\u9990\u9991\u9992\u9993\u9994"

# Text file.
File.write('t.txt', text)

# File with Russian text.
File.write('t.rus', russian)

# File with binary data.
f = File.new('t.dat', 'wb:UTF-16')
f.write(data)
f.close

打开选项

许多 IO 方法接受可选关键字参数,这些参数确定如何打开新流

还可以使用 String#encode 中提供的选项,这些选项可以控制外部和内部编码之间的转换。

基本 IO

您可以使用这些方法执行基本的流 IO,这些方法通常在多字节字符串上操作

位置

IO 流具有一个非负整数位置,它是下一次读取或写入将发生处的字节偏移量。新流的位置为零(行号也为零);方法 rewind 将位置(和行号)重置为零。

相关方法

打开和关闭的流

新的 IO 流可以打开以供读取、打开以供写入或同时打开。

当垃圾回收器声明时,流会自动关闭。

尝试在关闭的流上读取或写入会引发异常。

相关方法

流结束

您可以查询流是否位于其末尾

您可以使用 IO#seek 方法重新定位到流结束

f = File.new('t.txt')
f.eof? # => false
f.seek(0, :END)
f.eof? # => true
f.close

或通过读取所有流内容(比使用 IO#seek 慢)

f.rewind
f.eof? # => false
f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.eof? # => true

行 IO

您可以使用这些方法逐行读取 IO 流

每个读取方法都接受以下内容

对于每个读取方法,读取可能从行中开始,具体取决于流的位置;请参阅 位置

f = File.new('t.txt')
f.pos = 27
f.each_line {|line| p line }
f.close

输出

"rth line\n"
"Fifth line\n"

您可以使用此方法逐行写入 IO 流

行分隔符

每个方法都使用行分隔符,它是分隔行的字符串

默认行分隔符由全局变量 $/ 给出,其默认值为 "\n"。要读取的下一行是从当前位置到下一行分隔符的所有数据

f = File.new('t.txt')
f.gets # => "First line\n"
f.gets # => "Second line\n"
f.gets # => "\n"
f.gets # => "Fourth line\n"
f.gets # => "Fifth line\n"
f.close

您可以指定不同的行分隔符

f = File.new('t.txt')
f.gets('l')   # => "First l"
f.gets('li')  # => "ine\nSecond li"
f.gets('lin') # => "ne\n\nFourth lin"
f.gets        # => "e\n"
f.close

有两个特殊行分隔符

行限制

每个方法都使用行限制,它指定返回的字节数可能不会比给定的 limit 长(多);

不会拆分多字节字符,因此一行可能略长于给定的限制。

如果未给出 limit,则行仅由 sep 确定。

# Text with 1-byte characters.
File.open('t.txt') {|f| f.gets(1) }  # => "F"
File.open('t.txt') {|f| f.gets(2) }  # => "Fi"
File.open('t.txt') {|f| f.gets(3) }  # => "Fir"
File.open('t.txt') {|f| f.gets(4) }  # => "Firs"
# No more than one line.
File.open('t.txt') {|f| f.gets(10) } # => "First line"
File.open('t.txt') {|f| f.gets(11) } # => "First line\n"
File.open('t.txt') {|f| f.gets(12) } # => "First line\n"

# Text with 2-byte characters, which will not be split.
File.open('t.rus') {|f| f.gets(1).size } # => 1
File.open('t.rus') {|f| f.gets(2).size } # => 1
File.open('t.rus') {|f| f.gets(3).size } # => 2
File.open('t.rus') {|f| f.gets(4).size } # => 2

行分隔符和行限制

给定参数 seplimit,合并两种行为

示例

File.open('t.txt') {|f| f.gets('li', 20) } # => "First li"
File.open('t.txt') {|f| f.gets('li', 2) }  # => "Fi"

行号

可读 IO 流具有非负整数行号

相关方法

除非通过调用方法 IO#lineno= 进行修改,否则行号是根据给定的行分隔符 sep 由某些面向行的读取方法读取的行数

新流最初的行号为零(位置为零);方法 rewind 将行号(和位置)重置为零

f = File.new('t.txt')
f.lineno # => 0
f.gets   # => "First line\n"
f.lineno # => 1
f.rewind
f.lineno # => 0
f.close

从流中读取行通常会更改其行号

f = File.new('t.txt', 'r')
f.lineno   # => 0
f.readline # => "This is line one.\n"
f.lineno   # => 1
f.readline # => "This is the second line.\n"
f.lineno   # => 2
f.readline # => "Here's the third line.\n"
f.lineno   # => 3
f.eof?     # => true
f.close

在流中迭代行通常会更改其行号

File.open('t.txt') do |f|
  f.each_line do |line|
    p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}"
  end
end

输出

"position=11 eof?=false lineno=1"
"position=23 eof?=false lineno=2"
"position=24 eof?=false lineno=3"
"position=36 eof?=false lineno=4"
"position=47 eof?=true lineno=5"

与流的 位置 不同,行号不会影响下次读取或写入的位置

f = File.new('t.txt')
f.lineno = 1000
f.lineno # => 1000
f.gets   # => "First line\n"
f.lineno # => 1001
f.close

与行号关联的是全局变量 $.

字符 IO

可以使用这些方法逐个字符处理 IO 流

字节 IO

可以使用这些方法逐字节处理 IO 流

码点 IO

可以逐码点处理 IO 流

此处内容

首先,其他内容。类 IO

此处,类 IO 提供对以下内容有用的方法

创建

读取

写入

定位

迭代

设置

查询

缓冲

低级访问

其他

常量

EWOULDBLOCKWaitReadable

IO::EAGAINWaitReadable 相同

EWOULDBLOCKWaitWritable

IO::EAGAINWaitWritable 相同

PRIORITY

用于 IO#wait 的优先级事件掩码。

READABLE

用于 IO#wait 的可读事件掩码。

SEEK_CUR

Set 从当前位置设置 I/O 位置

SEEK_DATA

Set 将 I/O 位置设置为包含数据的下一个位置

SEEK_END

Set 从末尾设置 I/O 位置

SEEK_HOLE

Set 将 I/O 位置设置为下一个空洞

SEEK_SET

Set 从开头设置 I/O 位置

WRITABLE

用于 IO#wait 的可写事件掩码。

公共类方法

binread(path, length = nil, offset = 0) → string or nil 点击切换源代码

行为类似于 IO.read,不同之处在于流以二进制模式打开,并使用 ASCII-8BIT 编码。

如果从类 IO(而非 IO 的子类)调用此方法,则如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入

static VALUE
rb_io_s_binread(int argc, VALUE *argv, VALUE io)
{
    VALUE offset;
    struct foreach_arg arg;
    enum {
        fmode = FMODE_READABLE|FMODE_BINMODE,
        oflags = O_RDONLY
#ifdef O_BINARY
                |O_BINARY
#endif
    };
    struct rb_io_encoding convconfig = {NULL, NULL, 0, Qnil};

    rb_scan_args(argc, argv, "12", NULL, NULL, &offset);
    FilePathValue(argv[0]);
    convconfig.enc = rb_ascii8bit_encoding();
    arg.io = rb_io_open_generic(io, argv[0], oflags, fmode, &convconfig, 0);
    if (NIL_P(arg.io)) return Qnil;
    arg.argv = argv+1;
    arg.argc = (argc > 1) ? 1 : 0;
    if (!NIL_P(offset)) {
        struct seek_arg sarg;
        int state = 0;
        sarg.io = arg.io;
        sarg.offset = offset;
        sarg.mode = SEEK_SET;
        rb_protect(seek_before_access, (VALUE)&sarg, &state);
        if (state) {
            rb_io_close(arg.io);
            rb_jump_tag(state);
        }
    }
    return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}
binwrite(path, string, offset = 0) → integer 点击切换源代码

行为类似于 IO.write,不同之处在于流以二进制模式打开,并使用 ASCII-8BIT 编码。

如果从类 IO(而非 IO 的子类)调用此方法,则如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入

static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
    return io_s_write(argc, argv, io, 1);
}
console → #<File:/dev/tty> 点击切换源代码
console(sym, *args)

返回打开控制台的 File 实例。

如果给定了 sym,则会使用 args 将其发送到打开的控制台,并将返回结果,而不是控制台 IO 本身。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_dev(int argc, VALUE *argv, VALUE klass)
{
    VALUE con = 0;
    VALUE sym = 0;

    rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);

    if (argc) {
        Check_Type(sym = argv[0], T_SYMBOL);
    }

    // Force the class to be File.
    if (klass == rb_cIO) klass = rb_cFile;

    if (rb_const_defined(klass, id_console)) {
        con = rb_const_get(klass, id_console);
        if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) {
            rb_const_remove(klass, id_console);
            con = 0;
        }
    }

    if (sym) {
        if (sym == ID2SYM(id_close) && argc == 1) {
            if (con) {
                rb_io_close(con);
                rb_const_remove(klass, id_console);
                con = 0;
            }
            return Qnil;
        }
    }

    if (!con) {
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H
# define CONSOLE_DEVICE "/dev/tty"
#elif defined _WIN32
# define CONSOLE_DEVICE "con$"
# define CONSOLE_DEVICE_FOR_READING "conin$"
# define CONSOLE_DEVICE_FOR_WRITING "conout$"
#endif
#ifndef CONSOLE_DEVICE_FOR_READING
# define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE
#endif
#ifdef CONSOLE_DEVICE_FOR_WRITING
        VALUE out;
        rb_io_t *ofptr;
#endif
        int fd;
        VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));

#ifdef CONSOLE_DEVICE_FOR_WRITING
        fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
        if (fd < 0) return Qnil;
        out = rb_io_open_descriptor(klass, fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL);
#endif
        fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
        if (fd < 0) {
#ifdef CONSOLE_DEVICE_FOR_WRITING
            rb_io_close(out);
#endif
            return Qnil;
        }

        con = rb_io_open_descriptor(klass, fd, FMODE_READWRITE | FMODE_SYNC, path, Qnil, NULL);
#ifdef CONSOLE_DEVICE_FOR_WRITING
        rb_io_set_write_io(con, out);
#endif
        rb_const_set(klass, id_console, con);
    }

    if (sym) {
        return rb_f_send(argc, argv, con);
    }

    return con;
}
copy_stream(src, dst, src_length = nil, src_offset = 0) → integer 点击切换源代码

从给定的 src 复制到给定的 dst,返回复制的字节数。

  • 给定的 src 必须是以下之一

    • 可读文件的路径,从中读取源数据。

    • 类似 IO 的对象,已打开以供读取,并且能够响应方法 :readpartial 或方法 :read

  • 给定的 dst 必须是以下之一

    • 可写文件的路径,将数据写入其中。

    • 类似 IO 的对象,已打开以供写入,并且能够响应方法 :write

此处的示例使用文件 t.txt 作为源

File.read('t.txt')
# => "First line\nSecond line\n\nThird line\nFourth line\n"
File.read('t.txt').size # => 47

如果仅给定参数 srcdst,则复制整个源流

# Paths.
IO.copy_stream('t.txt', 't.tmp')  # => 47

# IOs (recall that a File is also an IO).
src_io = File.open('t.txt', 'r') # => #<File:t.txt>
dst_io = File.open('t.tmp', 'w') # => #<File:t.tmp>
IO.copy_stream(src_io, dst_io)   # => 47
src_io.close
dst_io.close

使用参数 src_length(非负整数),最多复制该数量的字节

IO.copy_stream('t.txt', 't.tmp', 10) # => 10
File.read('t.tmp')                   # => "First line"

如果还给定了参数 src_offset,则从该偏移量开始读取源流

IO.copy_stream('t.txt', 't.tmp', 11, 11) # => 11
IO.read('t.tmp')                         # => "Second line"
static VALUE
rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
{
    VALUE src, dst, length, src_offset;
    struct copy_stream_struct st;

    MEMZERO(&st, struct copy_stream_struct, 1);

    rb_scan_args(argc, argv, "22", &src, &dst, &length, &src_offset);

    st.src = src;
    st.dst = dst;

    st.src_fptr = NULL;
    st.dst_fptr = NULL;

    if (NIL_P(length))
        st.copy_length = (rb_off_t)-1;
    else
        st.copy_length = NUM2OFFT(length);

    if (NIL_P(src_offset))
        st.src_offset = (rb_off_t)-1;
    else
        st.src_offset = NUM2OFFT(src_offset);

    rb_ensure(copy_stream_body, (VALUE)&st, copy_stream_finalize, (VALUE)&st);

    return OFFT2NUM(st.total);
}
for_fd(fd, mode = 'r', **opts) → io 单击以切换源

IO.new 的同义词。

static VALUE
rb_io_s_for_fd(int argc, VALUE *argv, VALUE klass)
{
    VALUE io = rb_obj_alloc(klass);
    rb_io_initialize(argc, argv, io);
    return io;
}
foreach(path, sep = $/, **opts) {|line| block } → nil 单击以切换源
foreach(path, limit, **opts) {|line| block } → nil
foreach(path, sep, limit, **opts) {|line| block } → nil
foreach(...) → an_enumerator

使用从流中读取的每一行连续调用该块。

如果从类 IO(而非 IO 的子类)调用此方法,则如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入

第一个参数必须是文件的路径字符串。

仅给定参数 path 时,根据默认行分隔符解析给定 path 处的文件中的行,并使用每一行连续调用该块

File.foreach('t.txt') {|line| p line }

输出:与上述相同。

对于命令和路径这两种形式,其余参数相同。

给定参数 sep 时,根据该行分隔符解析行(请参阅 行分隔符

File.foreach('t.txt', 'li') {|line| p line }

输出

"First li"
"ne\nSecond li"
"ne\n\nThird li"
"ne\nFourth li"
"ne\n"

每一段

File.foreach('t.txt', '') {|paragraph| p paragraph }

输出

"First line\nSecond line\n\n"
"Third line\nFourth line\n"

给定参数 limit 时,根据默认行分隔符和给定的行长限制解析行(请参阅 行限制

File.foreach('t.txt', 7) {|line| p line }

输出

"First l"
"ine\n"
"Second "
"line\n"
"\n"
"Third l"
"ine\n"
"Fourth l"
"line\n"

给定参数 seplimit 时,根据给定的行分隔符和给定的行长限制解析行(请参阅 行分隔符和行限制

可选关键字参数 opts 指定

如果未给定块,则返回 Enumerator

static VALUE
rb_io_s_foreach(int argc, VALUE *argv, VALUE self)
{
    VALUE opt;
    int orig_argc = argc;
    struct foreach_arg arg;
    struct getline_arg garg;

    argc = rb_scan_args(argc, argv, "12:", NULL, NULL, NULL, &opt);
    RETURN_ENUMERATOR(self, orig_argc, argv);
    extract_getline_args(argc-1, argv+1, &garg);
    open_key_args(self, argc, argv, opt, &arg);
    if (NIL_P(arg.io)) return Qnil;
    extract_getline_opts(opt, &garg);
    check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
    return rb_ensure(io_s_foreach, (VALUE)&garg, rb_io_close, arg.io);
}
new(fd, mode = 'r', **opts) → io 单击以切换源

从文件描述符创建并返回一个新的 IO 对象(文件流)。

IO.new 可能对与低级库进行交互很有用。对于高级交互,使用 File.open 创建文件流可能更简单。

参数 fd 必须是有效的文件描述符(整数)

path = 't.tmp'
fd = IO.sysopen(path) # => 3
IO.new(fd)            # => #<IO:fd 3>

新 IO 对象不会继承编码(因为整数文件描述符没有编码)

fd = IO.sysopen('t.rus', 'rb')
io = IO.new(fd)
io.external_encoding # => #<Encoding:UTF-8> # Not ASCII-8BIT.

可选参数 mode(默认为“r”)必须指定有效模式;请参阅 访问模式

IO.new(fd, 'w')         # => #<IO:fd 3>
IO.new(fd, File::WRONLY) # => #<IO:fd 3>

可选关键字参数 opts 指定

示例

IO.new(fd, internal_encoding: nil) # => #<IO:fd 3>
IO.new(fd, autoclose: true)        # => #<IO:fd 3>
static VALUE
rb_io_initialize(int argc, VALUE *argv, VALUE io)
{
    VALUE fnum, vmode;
    rb_io_t *fp;
    int fd, fmode, oflags = O_RDONLY;
    struct rb_io_encoding convconfig;
    VALUE opt;
#if defined(HAVE_FCNTL) && defined(F_GETFL)
    int ofmode;
#else
    struct stat st;
#endif


    argc = rb_scan_args(argc, argv, "11:", &fnum, &vmode, &opt);
    rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &fmode, &convconfig);

    fd = NUM2INT(fnum);
    if (rb_reserved_fd_p(fd)) {
        rb_raise(rb_eArgError, "The given fd is not accessible because RubyVM reserves it");
    }
#if defined(HAVE_FCNTL) && defined(F_GETFL)
    oflags = fcntl(fd, F_GETFL);
    if (oflags == -1) rb_sys_fail(0);
#else
    if (fstat(fd, &st) < 0) rb_sys_fail(0);
#endif
    rb_update_max_fd(fd);
#if defined(HAVE_FCNTL) && defined(F_GETFL)
    ofmode = rb_io_oflags_fmode(oflags);
    if (NIL_P(vmode)) {
        fmode = ofmode;
    }
    else if ((~ofmode & fmode) & FMODE_READWRITE) {
        VALUE error = INT2FIX(EINVAL);
        rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
    }
#endif
    VALUE path = Qnil;

    if (!NIL_P(opt)) {
        if (rb_hash_aref(opt, sym_autoclose) == Qfalse) {
            fmode |= FMODE_EXTERNAL;
        }

        path = rb_hash_aref(opt, RB_ID2SYM(idPath));
        if (!NIL_P(path)) {
            StringValue(path);
            path = rb_str_new_frozen(path);
        }
    }

    MakeOpenFile(io, fp);
    fp->self = io;
    fp->fd = fd;
    fp->mode = fmode;
    fp->encs = convconfig;
    fp->pathv = path;
    fp->timeout = Qnil;
    clear_codeconv(fp);
    io_check_tty(fp);
    if (fileno(stdin) == fd)
        fp->stdio_file = stdin;
    else if (fileno(stdout) == fd)
        fp->stdio_file = stdout;
    else if (fileno(stderr) == fd)
        fp->stdio_file = stderr;

    if (fmode & FMODE_SETENC_BY_BOM) io_set_encoding_by_bom(io);
    return io;
}
open(fd, mode = 'r', **opts) → io 单击以切换源
open(fd, mode = 'r', **opts) {|io| ... } → object

通过 IO.new 使用给定参数创建一个新的 IO 对象。

如果没有给定块,则返回 IO 对象。

如果给定块,则使用 IO 对象调用块并返回块的值。

static VALUE
rb_io_s_open(int argc, VALUE *argv, VALUE klass)
{
    VALUE io = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);

    if (rb_block_given_p()) {
        return rb_ensure(rb_yield, io, io_close, io);
    }

    return io;
}
pipe(**opts) → [read_io, write_io] 单击以切换源
pipe(enc, **opts) → [read_io, write_io]
pipe(ext_enc, int_enc, **opts) → [read_io, write_io]
pipe(**opts) {|read_io, write_io| ...} → object
pipe(enc, **opts) {|read_io, write_io| ...} → object
pipe(ext_enc, int_enc, **opts) {|read_io, write_io| ...} → object

创建一对管道端点 read_iowrite_io,它们相互连接。

如果给定参数 enc_string,它必须是一个包含以下内容之一的字符串

  • 用作外部编码的编码名称。

  • 用作外部和内部编码的两个编码的冒号分隔名称。

如果给定参数 int_enc,它必须是指定要使用的内部编码的 Encoding 对象或编码名称字符串;如果也给定参数 ext_enc,它必须是指定要使用的外部编码的 Encoding 对象或编码名称字符串。

read_io 读取的字符串会标记为外部编码;如果还指定了内部编码,则字符串将被转换为该编码并标记为该编码。

如果指定了任何编码,则可选哈希参数指定转换选项。

可选关键字参数 opts 指定

如果没有给定块,则在数组中返回两个端点

IO.pipe # => [#<IO:fd 4>, #<IO:fd 5>]

如果给定块,则使用两个端点调用该块;关闭两个端点并返回该块的值

IO.pipe {|read_io, write_io| p read_io; p write_io }

输出

#<IO:fd 6>
#<IO:fd 7>

并非在所有平台上都可用。

在下面的示例中,两个进程关闭了它们未使用的管道端点。这不仅仅是表面上的细微差别。如果管道中仍有写入器打开,则管道的读取端不会生成文件结束条件。对于父进程,如果它不首先发出 wr.close,则 rd.read 永远不会返回

rd, wr = IO.pipe

if fork
  wr.close
  puts "Parent got: <#{rd.read}>"
  rd.close
  Process.wait
else
  rd.close
  puts 'Sending message to parent'
  wr.write "Hi Dad"
  wr.close
end

生成

Sending message to parent
Parent got: <Hi Dad>
static VALUE
rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
{
    int pipes[2], state;
    VALUE r, w, args[3], v1, v2;
    VALUE opt;
    rb_io_t *fptr, *fptr2;
    struct io_encoding_set_args ies_args;
    int fmode = 0;
    VALUE ret;

    argc = rb_scan_args(argc, argv, "02:", &v1, &v2, &opt);
    if (rb_pipe(pipes) < 0)
        rb_sys_fail(0);

    args[0] = klass;
    args[1] = INT2NUM(pipes[0]);
    args[2] = INT2FIX(O_RDONLY);
    r = rb_protect(io_new_instance, (VALUE)args, &state);
    if (state) {
        close(pipes[0]);
        close(pipes[1]);
        rb_jump_tag(state);
    }
    GetOpenFile(r, fptr);

    ies_args.fptr = fptr;
    ies_args.v1 = v1;
    ies_args.v2 = v2;
    ies_args.opt = opt;
    rb_protect(io_encoding_set_v, (VALUE)&ies_args, &state);
    if (state) {
        close(pipes[1]);
        io_close(r);
        rb_jump_tag(state);
    }

    args[1] = INT2NUM(pipes[1]);
    args[2] = INT2FIX(O_WRONLY);
    w = rb_protect(io_new_instance, (VALUE)args, &state);
    if (state) {
        close(pipes[1]);
        if (!NIL_P(r)) rb_io_close(r);
        rb_jump_tag(state);
    }
    GetOpenFile(w, fptr2);
    rb_io_synchronized(fptr2);

    extract_binmode(opt, &fmode);

    if ((fmode & FMODE_BINMODE) && NIL_P(v1)) {
        rb_io_ascii8bit_binmode(r);
        rb_io_ascii8bit_binmode(w);
    }

#if DEFAULT_TEXTMODE
    if ((fptr->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
        fptr->mode &= ~FMODE_TEXTMODE;
        setmode(fptr->fd, O_BINARY);
    }
#if RUBY_CRLF_ENVIRONMENT
    if (fptr->encs.ecflags & ECONV_DEFAULT_NEWLINE_DECORATOR) {
        fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
    }
#endif
#endif
    fptr->mode |= fmode;
#if DEFAULT_TEXTMODE
    if ((fptr2->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
        fptr2->mode &= ~FMODE_TEXTMODE;
        setmode(fptr2->fd, O_BINARY);
    }
#endif
    fptr2->mode |= fmode;

    ret = rb_assoc_new(r, w);
    if (rb_block_given_p()) {
        VALUE rw[2];
        rw[0] = r;
        rw[1] = w;
        return rb_ensure(rb_yield, ret, pipe_pair_close, (VALUE)rw);
    }
    return ret;
}
popen(env = {}, cmd, mode = 'r', **opts) → io 单击以切换源
popen(env = {}, cmd, mode = 'r', **opts) {|io| ... } → object

将给定的命令 cmd 作为子进程执行,其 $stdin 和 $stdout 连接到新的流 io

如果使用不受信任的输入调用此方法,则可能存在安全漏洞;请参阅 命令注入

如果没有给定块,则返回新流,该流根据给定的 mode 可能已打开以进行读取、写入或两者。应显式关闭流(最终)以避免资源泄漏。

如果给定块,则流将传递给块(同样,打开以进行读取、写入或两者);当块退出时,流将关闭,并且块的值将分配给全局变量 $? 并返回。

可选参数 mode 可以是任何有效的 IO 模式。请参阅 访问模式

必需参数 cmd 确定以下哪一项发生

  • 进程分叉。

  • 指定程序在 shell 中运行。

  • 指定程序使用指定参数运行。

  • 指定程序使用指定参数和指定的 argv0 运行。

以下详细介绍了其中的每一项。

可选哈希参数 env 指定要添加到子进程的环境变量中的名称/值对

IO.popen({'FOO' => 'bar'}, 'ruby', 'r+') do |pipe|
  pipe.puts 'puts ENV["FOO"]'
  pipe.close_write
  pipe.gets
end => "bar\n"

可选关键字参数 opts 指定

派生进程

当参数 cmd 为 1 个字符的字符串 '-' 时,导致进程派生

IO.popen('-') do |pipe|
  if pipe
    $stderr.puts "In parent, child pid is #{pipe.pid}\n"
  else
    $stderr.puts "In child, pid is #{$$}\n"
  end
end

输出

In parent, child pid is 26253
In child, pid is 26253

请注意,并非所有平台都支持此操作。

Shell 子进程

当参数 cmd 为单个字符串(但不是 '-')时,名为 cmd 的程序将作为 shell 命令运行

IO.popen('uname') do |pipe|
  pipe.readlines
end

输出

["Linux\n"]

另一个示例

IO.popen('/bin/sh', 'r+') do |pipe|
  pipe.puts('ls')
  pipe.close_write
  $stderr.puts pipe.readlines.size
end

输出

213

程序子进程

当参数 cmd 为字符串数组时,名为 cmd[0] 的程序将使用 cmd 的所有元素作为其参数运行

IO.popen(['du', '..', '.']) do |pipe|
  $stderr.puts pipe.readlines.size
end

输出

1111

带有 argv0 的程序子进程

当参数 cmd 为数组,其第一个元素为 2 元素字符串数组,其剩余元素(如果有)为字符串时

  • cmd[0][0](嵌套数组中的第一个字符串)是要运行的程序的名称。

  • cmd[0][1](嵌套数组中的第二个字符串)被设置为程序的 argv[0]

  • cmd[1..-1](外部数组中的字符串)是程序的参数。

示例(将 $0 设置为“foo”)

IO.popen([['/bin/sh', 'foo'], '-c', 'echo $0']).read # => "foo\n"

一些特殊示例

# Set IO encoding.
IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io|
  euc_jp_string = nkf_io.read
}

# Merge standard output and standard error using Kernel#spawn option. See Kernel#spawn.
IO.popen(["ls", "/", :err=>[:child, :out]]) do |io|
  ls_result_with_error = io.read
end

# Use mixture of spawn options and IO options.
IO.popen(["ls", "/"], :err=>[:child, :out]) do |io|
  ls_result_with_error = io.read
end

 f = IO.popen("uname")
 p f.readlines
 f.close
 puts "Parent is #{Process.pid}"
 IO.popen("date") {|f| puts f.gets }
 IO.popen("-") {|f| $stderr.puts "#{Process.pid} is here, f is #{f.inspect}"}
 p $?
 IO.popen(%w"sed -e s|^|<foo>| -e s&$&;zot;&", "r+") {|f|
   f.puts "bar"; f.close_write; puts f.gets
 }

输出(来自上一部分)

["Linux\n"]
Parent is 21346
Thu Jan 15 22:41:19 JST 2009
21346 is here, f is #<IO:fd 3>
21352 is here, f is nil
#<Process::Status: pid 21352 exit 0>
<foo>bar;zot;

引发 IO.pipeKernel.spawn 引发的异常。

static VALUE
rb_io_s_popen(int argc, VALUE *argv, VALUE klass)
{
    VALUE pname, pmode = Qnil, opt = Qnil, env = Qnil;

    if (argc > 1 && !NIL_P(opt = rb_check_hash_type(argv[argc-1]))) --argc;
    if (argc > 1 && !NIL_P(env = rb_check_hash_type(argv[0]))) --argc, ++argv;
    switch (argc) {
      case 2:
        pmode = argv[1];
      case 1:
        pname = argv[0];
        break;
      default:
        {
            int ex = !NIL_P(opt);
            rb_error_arity(argc + ex, 1 + ex, 2 + ex);
        }
    }
    return popen_finish(rb_io_popen(pname, pmode, env, opt), klass);
}
read(path, length = nil, offset = 0, **opts) → string or nil 单击以切换源

打开流,读取并返回其部分或全部内容,然后关闭流;如果没有读取任何字节,则返回 nil

如果从类 IO(而非 IO 的子类)调用此方法,则如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入

第一个参数必须是文件的路径字符串。

仅给定参数 path 时,以文本模式读取并返回给定路径处文件的全部内容

IO.read('t.txt')
# => "First line\nSecond line\n\nThird line\nFourth line\n"

在 Windows 上,文本模式在遇到某些特殊字节时可以终止读取并使文件中的字节未读。如果要读取文件中的所有字节,请考虑使用 IO.binread

使用参数 length 时,如果可用,则返回 length 个字节

IO.read('t.txt', 7) # => "First l"
IO.read('t.txt', 700)
# => "First line\r\nSecond line\r\n\r\nFourth line\r\nFifth line\r\n"

使用参数 lengthoffset 时,如果可用,则从给定的 offset 开始返回 length 个字节

IO.read('t.txt', 10, 2)   # => "rst line\nS"
IO.read('t.txt', 10, 200) # => nil

可选关键字参数 opts 指定

static VALUE
rb_io_s_read(int argc, VALUE *argv, VALUE io)
{
    VALUE opt, offset;
    long off;
    struct foreach_arg arg;

    argc = rb_scan_args(argc, argv, "13:", NULL, NULL, &offset, NULL, &opt);
    if (!NIL_P(offset) && (off = NUM2LONG(offset)) < 0) {
        rb_raise(rb_eArgError, "negative offset %ld given", off);
    }
    open_key_args(io, argc, argv, opt, &arg);
    if (NIL_P(arg.io)) return Qnil;
    if (!NIL_P(offset)) {
        struct seek_arg sarg;
        int state = 0;
        sarg.io = arg.io;
        sarg.offset = offset;
        sarg.mode = SEEK_SET;
        rb_protect(seek_before_access, (VALUE)&sarg, &state);
        if (state) {
            rb_io_close(arg.io);
            rb_jump_tag(state);
        }
        if (arg.argc == 2) arg.argc = 1;
    }
    return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}
readlines(path, sep = $/, **opts) → array 单击以切换源
readlines(path, limit, **opts) → array
readlines(path, sep, limit, **opts) → array

返回从流中读取的所有行的数组。

如果从类 IO(而非 IO 的子类)调用此方法,则如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入

第一个参数必须是文件的路径字符串。

仅给定参数 path 时,根据默认行分隔符解析给定 path 处文件中的行,并在数组中返回这些行

IO.readlines('t.txt')
# => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]

给定参数 sep 时,根据该行分隔符解析行(请参阅 行分隔符

# Ordinary separator.
IO.readlines('t.txt', 'li')
# =>["First li", "ne\nSecond li", "ne\n\nThird li", "ne\nFourth li", "ne\n"]
# Get-paragraphs separator.
IO.readlines('t.txt', '')
# => ["First line\nSecond line\n\n", "Third line\nFourth line\n"]
# Get-all separator.
IO.readlines('t.txt', nil)
# => ["First line\nSecond line\n\nThird line\nFourth line\n"]

给定参数 limit 时,根据默认行分隔符和给定的行长限制解析行(请参阅 行限制

IO.readlines('t.txt', 7)
# => ["First l", "ine\n", "Second ", "line\n", "\n", "Third l", "ine\n", "Fourth ", "line\n"]

给定参数 seplimit 时,根据给定的行分隔符和给定的行长限制解析行(请参阅 行分隔符和行限制

可选关键字参数 opts 指定

static VALUE
rb_io_s_readlines(int argc, VALUE *argv, VALUE io)
{
    VALUE opt;
    struct foreach_arg arg;
    struct getline_arg garg;

    argc = rb_scan_args(argc, argv, "12:", NULL, NULL, NULL, &opt);
    extract_getline_args(argc-1, argv+1, &garg);
    open_key_args(io, argc, argv, opt, &arg);
    if (NIL_P(arg.io)) return Qnil;
    extract_getline_opts(opt, &garg);
    check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
    return rb_ensure(io_s_readlines, (VALUE)&garg, rb_io_close, arg.io);
}
select(read_ios, write_ios = [], error_ios = [], timeout = nil) → array or nil 单击以切换源

调用系统调用 select(2),该调用监视多个文件描述符,等待一个或多个文件描述符准备好进行某种类别的 I/O 操作。

并非在所有平台上都已实现。

每个参数 read_ioswrite_ioserror_ios 都是 IO 对象的数组。

参数 timeout 是以秒为单位的整数超时间隔。

该方法监视所有三个数组中给出的 IO 对象,等待某些对象就绪;返回一个 3 元素数组,其元素为

  • 准备就绪的 read_ios 中的对象数组。

  • 准备就绪的 write_ios 中的对象数组。

  • error_ios 中有待处理异常的对象数组。

如果在给定的 timeout 内没有对象就绪,则返回 nil

IO.select 将 IO 对象的缓冲区用于测试可读性。如果 IO 缓冲区不为空,则 IO.select 会立即通知可读性。此“窥视”仅适用于 IO 对象。它不适用于类似 IO 的对象,例如 OpenSSL::SSL::SSLSocket

使用 IO.select 的最佳方法是在调用 read_nonblockwrite_nonblock 等非阻塞方法后调用它。这些方法会引发一个异常,该异常由 IO::WaitReadableIO::WaitWritable 扩展。这些模块会通知调用者应如何使用 IO.select 进行等待。如果引发 IO::WaitReadable,则调用者应等待读取。如果引发 IO::WaitWritable,则调用者应等待写入。

因此,可以使用 read_nonblock 和 IO.select 仿真阻塞读取 (readpartial),如下所示

begin
  result = io_like.read_nonblock(maxlen)
rescue IO::WaitReadable
  IO.select([io_like])
  retry
rescue IO::WaitWritable
  IO.select(nil, [io_like])
  retry
end

尤其是,对于类似 IO 的对象(例如 OpenSSL::SSL::SSLSocket),首选非阻塞方法和 IO.select 的组合。它具有 to_io 方法,用于返回底层的 IO 对象。IO.select 调用 to_io 以获取要等待的文件描述符。

这意味着由 IO.select 通知的可读性并不意味着 OpenSSL::SSL::SSLSocket 对象的可读性。

最可能的情况是 OpenSSL::SSL::SSLSocket 缓冲了一些数据。IO.select 不会看到该缓冲区。因此,当 OpenSSL::SSL::SSLSocket#readpartial 不阻塞时,IO.select 可能会阻塞。

然而,还有更多复杂的情况。

SSL 是一个由记录序列组成的协议。记录由多个字节组成。因此,SSL 的远程端发送了一个部分记录,IO.select 通知可读性,但 OpenSSL::SSL::SSLSocket 无法解密一个字节,而 OpenSSL::SSL::SSLSocket#readpartial 将被阻塞。

此外,远程端可以请求 SSL 重新协商,这会强制本地 SSL 引擎写入一些数据。这意味着 OpenSSL::SSL::SSLSocket#readpartial 可能会调用 write 系统调用,并且它可能会被阻塞。在这种情况下,OpenSSL::SSL::SSLSocket#read_nonblock 会引发 IO::WaitWritable 而不是阻塞。因此,调用者应该像上面的示例一样等待可写性准备就绪。

非阻塞方法和 IO.select 的组合对于流(例如 tty、管道套接字套接字)也很有用,当多个进程从流中读取时。

最后,Linux 内核开发人员不保证 select(2) 的可读性意味着即使对于单个进程,后续 read(2) 的可读性;请参阅 select(2)

IO#readpartial 之前调用 IO.select 通常工作得很好。然而,这不是使用 IO.select 的最佳方式。

select(2) 通知的可写性不会显示有多少字节可写。 IO#write 方法会阻塞,直到写入整个字符串。因此,在 IO.select 通知可写性之后,IO#write(两个或更多字节) 可能会被阻塞。需要 IO#write_nonblock 来避免阻塞。

可以使用 write_nonblockIO.select 模拟阻塞写入 (write),如下所示: IO::WaitReadable 也应该在 OpenSSL::SSL::SSLSocket 中为 SSL 重新协商进行救援。

while 0 < string.bytesize
  begin
    written = io_like.write_nonblock(string)
  rescue IO::WaitReadable
    IO.select([io_like])
    retry
  rescue IO::WaitWritable
    IO.select(nil, [io_like])
    retry
  end
  string = string.byteslice(written..-1)
end

示例

rp, wp = IO.pipe
mesg = "ping "
100.times {
  # IO.select follows IO#read.  Not the best way to use IO.select.
  rs, ws, = IO.select([rp], [wp])
  if r = rs[0]
    ret = r.read(5)
    print ret
    case ret
    when /ping/
      mesg = "pong\n"
    when /pong/
      mesg = "ping "
    end
  end
  if w = ws[0]
    w.write(mesg)
  end
}

输出

ping pong
ping pong
ping pong
(snipped)
ping
static VALUE
rb_f_select(int argc, VALUE *argv, VALUE obj)
{
    VALUE scheduler = rb_fiber_scheduler_current();
    if (scheduler != Qnil) {
        // It's optionally supported.
        VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv);
        if (!UNDEF_P(result)) return result;
    }

    VALUE timeout;
    struct select_args args;
    struct timeval timerec;
    int i;

    rb_scan_args(argc, argv, "13", &args.read, &args.write, &args.except, &timeout);
    if (NIL_P(timeout)) {
        args.timeout = 0;
    }
    else {
        timerec = rb_time_interval(timeout);
        args.timeout = &timerec;
    }

    for (i = 0; i < numberof(args.fdsets); ++i)
        rb_fd_init(&args.fdsets[i]);

    return rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
}
sysopen(path, mode = 'r', perm = 0666) → integer 点击切换源代码

使用给定的模式和权限打开给定路径的文件;返回整数文件描述符。

如果文件可读,则它必须存在;如果文件可写且不存在,则使用给定的权限创建它

File.write('t.tmp', '')  # => 0
IO.sysopen('t.tmp')      # => 8
IO.sysopen('t.tmp', 'w') # => 9
static VALUE
rb_io_s_sysopen(int argc, VALUE *argv, VALUE _)
{
    VALUE fname, vmode, vperm;
    VALUE intmode;
    int oflags, fd;
    mode_t perm;

    rb_scan_args(argc, argv, "12", &fname, &vmode, &vperm);
    FilePathValue(fname);

    if (NIL_P(vmode))
        oflags = O_RDONLY;
    else if (!NIL_P(intmode = rb_check_to_integer(vmode, "to_int")))
        oflags = NUM2INT(intmode);
    else {
        SafeStringValue(vmode);
        oflags = rb_io_modestr_oflags(StringValueCStr(vmode));
    }
    if (NIL_P(vperm)) perm = 0666;
    else              perm = NUM2MODET(vperm);

    RB_GC_GUARD(fname) = rb_str_new4(fname);
    fd = rb_sysopen(fname, oflags, perm);
    return INT2NUM(fd);
}
try_convert(object) → new_io or nil 点击切换源代码

尝试通过方法 to_ioobject 转换为 IO 对象;如果成功,则返回新的 IO 对象,否则返回 nil

IO.try_convert(STDOUT)   # => #<IO:<STDOUT>>
IO.try_convert(ARGF)     # => #<IO:<STDIN>>
IO.try_convert('STDOUT') # => nil
static VALUE
rb_io_s_try_convert(VALUE dummy, VALUE io)
{
    return rb_io_check_io(io);
}
write(path, data, offset = 0, **opts) → integer 点击切换源代码

打开流,将给定的 data 写入其中,然后关闭流;返回写入的字节数。

如果从类 IO(而非 IO 的子类)调用此方法,则如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入

第一个参数必须是文件的路径字符串。

仅给定参数 path 时,将给定的 data 写入该路径的文件

IO.write('t.tmp', 'abc')    # => 3
File.read('t.tmp')          # => "abc"

如果 offset 为零(默认值),则覆盖文件

IO.write('t.tmp', 'A')      # => 1
File.read('t.tmp')          # => "A"

如果 offset 在文件内容中,则部分覆盖文件

IO.write('t.tmp', 'abcdef') # => 3
File.read('t.tmp')          # => "abcdef"
# Offset within content.
IO.write('t.tmp', '012', 2) # => 3
File.read('t.tmp')          # => "ab012f"

如果 offset 在文件内容之外,则使用空字符 "\u0000" 填充文件

IO.write('t.tmp', 'xyz', 10) # => 3
File.read('t.tmp')           # => "ab012f\u0000\u0000\u0000\u0000xyz"

可选关键字参数 opts 指定

static VALUE
rb_io_s_write(int argc, VALUE *argv, VALUE io)
{
    return io_s_write(argc, argv, io, 0);
}

公共实例方法

self << object → self 单击切换源代码

将给定的 object 写入 self,该对象必须已打开以供写入(请参阅 访问模式);返回 self;如果 object 不是字符串,则通过方法 to_s 进行转换

$stdout << 'Hello' << ', ' << 'World!' << "\n"
$stdout << 'foo' << :bar << 2 << "\n"

输出

Hello, World!
foobar2
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
    rb_io_write(io, str);
    return io;
}
advise(advice, offset = 0, len = 0) → nil 单击切换源代码

调用 Posix 系统调用 posix_fadvise(2),该调用以特定方式宣布访问当前文件数据的意图。

参数和结果取决于平台。

相关数据由以下内容指定

  • offset:第一个数据字节的偏移量。

  • len:要访问的字节数;如果 len 为零或大于剩余字节数,则将访问所有剩余字节。

参数 advice 是以下符号之一

  • :normal:应用程序没有关于指定数据的访问模式的建议。如果未针对打开的文件提供建议,则这是默认假设。

  • :sequential:应用程序期望按顺序访问指定的数据(先读取较低的偏移量,再读取较高的偏移量)。

  • :random:将以随机顺序访问指定的数据。

  • :noreuse:指定的数据仅访问一次。

  • :willneed:指定的数据将在不久的将来访问。

  • :dontneed:指定的数据将在不久的将来不会访问。

并非在所有平台上都已实现。

static VALUE
rb_io_advise(int argc, VALUE *argv, VALUE io)
{
    VALUE advice, offset, len;
    rb_off_t off, l;
    rb_io_t *fptr;

    rb_scan_args(argc, argv, "12", &advice, &offset, &len);
    advice_arg_check(advice);

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    off = NIL_P(offset) ? 0 : NUM2OFFT(offset);
    l   = NIL_P(len)    ? 0 : NUM2OFFT(len);

#ifdef HAVE_POSIX_FADVISE
    return do_io_advise(fptr, advice, off, l);
#else
    ((void)off, (void)l);       /* Ignore all hint */
    return Qnil;
#endif
}
autoclose = bool → true or false 单击切换源代码

设置自动关闭标志。

f = File.open(File::NULL)
IO.for_fd(f.fileno).close
f.gets # raises Errno::EBADF

f = File.open(File::NULL)
g = IO.for_fd(f.fileno)
g.autoclose = false
g.close
f.gets # won't cause Errno::EBADF
static VALUE
rb_io_set_autoclose(VALUE io, VALUE autoclose)
{
    rb_io_t *fptr;
    GetOpenFile(io, fptr);
    if (!RTEST(autoclose))
        fptr->mode |= FMODE_EXTERNAL;
    else
        fptr->mode &= ~FMODE_EXTERNAL;
    return autoclose;
}
autoclose? → true or false 单击切换源代码

如果在最终确定或调用 close 时关闭 ios 的底层文件描述符,则返回 true,否则返回 false

static VALUE
rb_io_autoclose_p(VALUE io)
{
    rb_io_t *fptr = RFILE(io)->fptr;
    rb_io_check_closed(fptr);
    return RBOOL(!(fptr->mode & FMODE_EXTERNAL));
}
beep() 单击切换源代码
static VALUE
console_beep(VALUE io)
{
#ifdef _WIN32
    MessageBeep(0);
#else
    int fd = GetWriteFD(io);
    if (write(fd, "\a", 1) < 0) sys_fail(io);
#endif
    return io;
}
binmode → self 单击切换源代码

将流的数据模式设置为二进制(请参阅 数据模式)。

流的数据模式不能从二进制更改为文本。

static VALUE
rb_io_binmode_m(VALUE io)
{
    VALUE write_io;

    rb_io_ascii8bit_binmode(io);

    write_io = GetWriteIO(io);
    if (write_io != io)
        rb_io_ascii8bit_binmode(write_io);
    return io;
}
binmode? → true or false 单击切换源代码

如果流处于二进制模式,则返回 true,否则返回 false。请参阅 数据模式

static VALUE
rb_io_binmode_p(VALUE io)
{
    rb_io_t *fptr;
    GetOpenFile(io, fptr);
    return RBOOL(fptr->mode & FMODE_BINMODE);
}
check_winsize_changed() 点击切换源代码
_WIN32
static VALUE
console_check_winsize_changed(VALUE io)
{
    HANDLE h;
    DWORD num;

    h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
    while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) {
        INPUT_RECORD rec;
        if (ReadConsoleInput(h, &rec, 1, &num)) {
            if (rec.EventType == WINDOW_BUFFER_SIZE_EVENT) {
                rb_yield(Qnil);
            }
        }
    }
    return io;
}
clear_screen() 点击切换源代码
static VALUE
console_clear_screen(VALUE io)
{
    console_erase_screen(io, INT2FIX(2));
    console_goto(io, INT2FIX(0), INT2FIX(0));
    return io;
}
close → nil 点击切换源代码

如果流已打开进行读写,则关闭流;返回nil。请参见打开和关闭流

如果流已打开进行写入,则在关闭之前将任何缓冲写入刷新到操作系统。

如果流是由IO.popen打开的,则设置全局变量$?(子进程退出状态)。

示例

IO.popen('ruby', 'r+') do |pipe|
  puts pipe.closed?
  pipe.close
  puts $?
  puts pipe.closed?
end

输出

false
pid 13760 exit 0
true

相关:IO#close_readIO#close_writeIO#closed?

static VALUE
rb_io_close_m(VALUE io)
{
    rb_io_t *fptr = rb_io_get_fptr(io);
    if (fptr->fd < 0) {
        return Qnil;
    }
    rb_io_close(io);
    return Qnil;
}
close_on_exec = bool → true or false 点击切换源代码

设置关闭时执行标志。

f = File.open(File::NULL)
f.close_on_exec = true
system("cat", "/proc/self/fd/#{f.fileno}") # cat: /proc/self/fd/3: No such file or directory
f.closed?                #=> false

自 Ruby 2.0.0 起,Ruby 默认设置所有文件描述符的关闭时执行标志。因此,您无需自行设置。此外,如果另一个线程使用 fork() 和 exec()(例如,通过 system() 方法),则取消设置关闭时执行标志可能会导致文件描述符泄漏。如果您确实需要将文件描述符继承给子进程,请使用 spawn() 的参数,例如 fd=>fd。

static VALUE
rb_io_set_close_on_exec(VALUE io, VALUE arg)
{
    int flag = RTEST(arg) ? FD_CLOEXEC : 0;
    rb_io_t *fptr;
    VALUE write_io;
    int fd, ret;

    write_io = GetWriteIO(io);
    if (io != write_io) {
        GetOpenFile(write_io, fptr);
        if (fptr && 0 <= (fd = fptr->fd)) {
            if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
            if ((ret & FD_CLOEXEC) != flag) {
                ret = (ret & ~FD_CLOEXEC) | flag;
                ret = fcntl(fd, F_SETFD, ret);
                if (ret != 0) rb_sys_fail_path(fptr->pathv);
            }
        }

    }

    GetOpenFile(io, fptr);
    if (fptr && 0 <= (fd = fptr->fd)) {
        if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
        if ((ret & FD_CLOEXEC) != flag) {
            ret = (ret & ~FD_CLOEXEC) | flag;
            ret = fcntl(fd, F_SETFD, ret);
            if (ret != 0) rb_sys_fail_path(fptr->pathv);
        }
    }
    return Qnil;
}
close_on_exec? → true or false 点击切换源代码

如果流将在执行时关闭,则返回true,否则返回false

f = File.open('t.txt')
f.close_on_exec? # => true
f.close_on_exec = false
f.close_on_exec? # => false
f.close
static VALUE
rb_io_close_on_exec_p(VALUE io)
{
    rb_io_t *fptr;
    VALUE write_io;
    int fd, ret;

    write_io = GetWriteIO(io);
    if (io != write_io) {
        GetOpenFile(write_io, fptr);
        if (fptr && 0 <= (fd = fptr->fd)) {
            if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
            if (!(ret & FD_CLOEXEC)) return Qfalse;
        }
    }

    GetOpenFile(io, fptr);
    if (fptr && 0 <= (fd = fptr->fd)) {
        if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
        if (!(ret & FD_CLOEXEC)) return Qfalse;
    }
    return Qtrue;
}
close_read → nil 点击切换源代码

如果流已打开进行读取,则关闭流;返回nil。请参见打开和关闭流

如果流是由IO.popen打开的,并且也已关闭进行写入,则设置全局变量$?(子进程退出状态)。

示例

IO.popen('ruby', 'r+') do |pipe|
  puts pipe.closed?
  pipe.close_write
  puts pipe.closed?
  pipe.close_read
  puts $?
  puts pipe.closed?
end

输出

false
false
pid 14748 exit 0
true

相关:IO#closeIO#close_writeIO#closed?

static VALUE
rb_io_close_read(VALUE io)
{
    rb_io_t *fptr;
    VALUE write_io;

    fptr = rb_io_get_fptr(rb_io_taint_check(io));
    if (fptr->fd < 0) return Qnil;
    if (is_socket(fptr->fd, fptr->pathv)) {
#ifndef SHUT_RD
# define SHUT_RD 0
#endif
        if (shutdown(fptr->fd, SHUT_RD) < 0)
            rb_sys_fail_path(fptr->pathv);
        fptr->mode &= ~FMODE_READABLE;
        if (!(fptr->mode & FMODE_WRITABLE))
            return rb_io_close(io);
        return Qnil;
    }

    write_io = GetWriteIO(io);
    if (io != write_io) {
        rb_io_t *wfptr;
        wfptr = rb_io_get_fptr(rb_io_taint_check(write_io));
        wfptr->pid = fptr->pid;
        fptr->pid = 0;
        RFILE(io)->fptr = wfptr;
        /* bind to write_io temporarily to get rid of memory/fd leak */
        fptr->tied_io_for_writing = 0;
        RFILE(write_io)->fptr = fptr;
        rb_io_fptr_cleanup(fptr, FALSE);
        /* should not finalize fptr because another thread may be reading it */
        return Qnil;
    }

    if ((fptr->mode & (FMODE_DUPLEX|FMODE_WRITABLE)) == FMODE_WRITABLE) {
        rb_raise(rb_eIOError, "closing non-duplex IO for reading");
    }
    return rb_io_close(io);
}
close_write → nil 点击切换源代码

如果打开用于写入,则关闭写入流;返回 nil。请参阅 打开和关闭流

在关闭之前将所有缓冲写入刷新到操作系统。

如果流是由 IO.popen 打开的,并且也关闭了读取,则设置全局变量 $?(子进程退出状态)。

IO.popen('ruby', 'r+') do |pipe|
  puts pipe.closed?
  pipe.close_read
  puts pipe.closed?
  pipe.close_write
  puts $?
  puts pipe.closed?
end

输出

false
false
pid 15044 exit 0
true

相关信息:IO#closeIO#close_readIO#closed?

static VALUE
rb_io_close_write(VALUE io)
{
    rb_io_t *fptr;
    VALUE write_io;

    write_io = GetWriteIO(io);
    fptr = rb_io_get_fptr(rb_io_taint_check(write_io));
    if (fptr->fd < 0) return Qnil;
    if (is_socket(fptr->fd, fptr->pathv)) {
#ifndef SHUT_WR
# define SHUT_WR 1
#endif
        if (shutdown(fptr->fd, SHUT_WR) < 0)
            rb_sys_fail_path(fptr->pathv);
        fptr->mode &= ~FMODE_WRITABLE;
        if (!(fptr->mode & FMODE_READABLE))
            return rb_io_close(write_io);
        return Qnil;
    }

    if ((fptr->mode & (FMODE_DUPLEX|FMODE_READABLE)) == FMODE_READABLE) {
        rb_raise(rb_eIOError, "closing non-duplex IO for writing");
    }

    if (io != write_io) {
        fptr = rb_io_get_fptr(rb_io_taint_check(io));
        fptr->tied_io_for_writing = 0;
    }
    rb_io_close(write_io);
    return Qnil;
}
closed? → true 或 false 单击以切换源

如果流同时关闭了读取和写入,则返回 true,否则返回 false。请参阅 打开和关闭流

IO.popen('ruby', 'r+') do |pipe|
  puts pipe.closed?
  pipe.close_read
  puts pipe.closed?
  pipe.close_write
  puts pipe.closed?
end

输出

false
false
true

相关信息:IO#close_readIO#close_writeIO#close

VALUE
rb_io_closed_p(VALUE io)
{
    rb_io_t *fptr;
    VALUE write_io;
    rb_io_t *write_fptr;

    write_io = GetWriteIO(io);
    if (io != write_io) {
        write_fptr = RFILE(write_io)->fptr;
        if (write_fptr && 0 <= write_fptr->fd) {
            return Qfalse;
        }
    }

    fptr = rb_io_get_fptr(io);
    return RBOOL(0 > fptr->fd);
}
console_mode → 模式 单击以切换源

返回表示当前控制台模式的数据。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_conmode_get(VALUE io)
{
    conmode t;
    int fd = GetReadFD(io);

    if (!getattr(fd, &t)) sys_fail(io);

    return conmode_new(cConmode, &t);
}
console_mode = 模式 单击以切换源

将控制台模式设置为 mode

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_conmode_set(VALUE io, VALUE mode)
{
    conmode *t, r;
    int fd = GetReadFD(io);

    TypedData_Get_Struct(mode, conmode, &conmode_type, t);
    r = *t;

    if (!setattr(fd, &r)) sys_fail(io);

    return mode;
}
cooked {|io| } 单击以切换源

在 cooked 模式下产生 self

STDIN.cooked(&:gets)

将读取并返回一行,并回显和行编辑。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_cooked(VALUE io)
{
    return ttymode(io, rb_yield, io, set_cookedmode, NULL);
}
cooked! 单击以切换源

启用 cooked 模式。

如果需要返回终端模式,请使用 io.cooked { … }。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_set_cooked(VALUE io)
{
    conmode t;
    int fd = GetReadFD(io);
    if (!getattr(fd, &t)) sys_fail(io);
    set_cookedmode(&t, NULL);
    if (!setattr(fd, &t)) sys_fail(io);
    return io;
}
cursor → [行,列] 单击以切换源

将当前光标位置作为整数的两个元素数组 (行,列) 返回

io.cursor # => [3, 5]

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_cursor_pos(VALUE io)
{
#ifdef _WIN32
    rb_console_size_t ws;
    int fd = GetWriteFD(io);
    if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
        rb_syserr_fail(LAST_ERROR, 0);
    }
    return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
#else
    static const struct query_args query = {"\033[6n", 0};
    VALUE resp = console_vt_response(0, 0, io, &query);
    VALUE row, column, term;
    unsigned int r, c;
    if (!RB_TYPE_P(resp, T_ARRAY) || RARRAY_LEN(resp) != 3) return Qnil;
    term = RARRAY_AREF(resp, 2);
    if (!RB_TYPE_P(term, T_STRING) || RSTRING_LEN(term) != 1) return Qnil;
    if (RSTRING_PTR(term)[0] != 'R') return Qnil;
    row = RARRAY_AREF(resp, 0);
    column = RARRAY_AREF(resp, 1);
    rb_ary_resize(resp, 2);
    r = NUM2UINT(row) - 1;
    c = NUM2UINT(column) - 1;
    RARRAY_ASET(resp, 0, INT2NUM(r));
    RARRAY_ASET(resp, 1, INT2NUM(c));
    return resp;
#endif
}
cursor=(p1) 单击以切换源
static VALUE
console_cursor_set(VALUE io, VALUE cpos)
{
    cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary");
    if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate");
    return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
}
cursor_down(p1) 单击以切换源
static VALUE
console_cursor_down(VALUE io, VALUE val)
{
    return console_move(io, +NUM2INT(val), 0);
}
cursor_left(p1) 单击以切换源
static VALUE
console_cursor_left(VALUE io, VALUE val)
{
    return console_move(io, 0, -NUM2INT(val));
}
cursor_right(p1) 单击以切换源
static VALUE
console_cursor_right(VALUE io, VALUE val)
{
    return console_move(io, 0, +NUM2INT(val));
}
cursor_up(p1) 单击以切换源
static VALUE
console_cursor_up(VALUE io, VALUE val)
{
    return console_move(io, -NUM2INT(val), 0);
}
each
别名:each_line
each_byte {|byte| ... } → self 单击以切换源
each_byte → 枚举器

使用流中的每个字节 (0..255) 调用给定的块;返回 self。请参阅 字节 IO

f = File.new('t.rus')
a = []
f.each_byte {|b| a << b }
a # => [209, 130, 208, 181, 209, 129, 209, 130]
f.close

如果未给定块,则返回 Enumerator

相关:IO#each_charIO#each_codepoint

static VALUE
rb_io_each_byte(VALUE io)
{
    rb_io_t *fptr;

    RETURN_ENUMERATOR(io, 0, 0);
    GetOpenFile(io, fptr);

    do {
        while (fptr->rbuf.len > 0) {
            char *p = fptr->rbuf.ptr + fptr->rbuf.off++;
            fptr->rbuf.len--;
            rb_yield(INT2FIX(*p & 0xff));
            rb_io_check_byte_readable(fptr);
            errno = 0;
        }
        READ_CHECK(fptr);
    } while (io_fillbuf(fptr) >= 0);
    return io;
}
each_char {|c| ... } → self 单击以切换源代码
each_char → 枚举器

使用流中的每个字符调用给定的块;返回 self。请参阅 字符 IO

f = File.new('t.rus')
a = []
f.each_char {|c| a << c.ord }
a # => [1090, 1077, 1089, 1090]
f.close

如果未给定块,则返回 Enumerator

相关:IO#each_byteIO#each_codepoint

static VALUE
rb_io_each_char(VALUE io)
{
    rb_io_t *fptr;
    rb_encoding *enc;
    VALUE c;

    RETURN_ENUMERATOR(io, 0, 0);
    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);

    enc = io_input_encoding(fptr);
    READ_CHECK(fptr);
    while (!NIL_P(c = io_getc(fptr, enc))) {
        rb_yield(c);
    }
    return io;
}
each_codepoint {|c| ... } → self 单击以切换源代码
each_codepoint → 枚举器

使用流中的每个代码点调用给定的块;返回 self

f = File.new('t.rus')
a = []
f.each_codepoint {|c| a << c }
a # => [1090, 1077, 1089, 1090]
f.close

如果未给定块,则返回 Enumerator

相关:IO#each_byteIO#each_char

static VALUE
rb_io_each_codepoint(VALUE io)
{
    rb_io_t *fptr;
    rb_encoding *enc;
    unsigned int c;
    int r, n;

    RETURN_ENUMERATOR(io, 0, 0);
    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);

    READ_CHECK(fptr);
    if (NEED_READCONV(fptr)) {
        SET_BINARY_MODE(fptr);
        r = 1;          /* no invalid char yet */
        for (;;) {
            make_readconv(fptr, 0);
            for (;;) {
                if (fptr->cbuf.len) {
                    if (fptr->encs.enc)
                        r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
                                                  fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
                                                  fptr->encs.enc);
                    else
                        r = ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1);
                    if (!MBCLEN_NEEDMORE_P(r))
                        break;
                    if (fptr->cbuf.len == fptr->cbuf.capa) {
                        rb_raise(rb_eIOError, "too long character");
                    }
                }
                if (more_char(fptr) == MORE_CHAR_FINISHED) {
                    clear_readconv(fptr);
                    if (!MBCLEN_CHARFOUND_P(r)) {
                        enc = fptr->encs.enc;
                        goto invalid;
                    }
                    return io;
                }
            }
            if (MBCLEN_INVALID_P(r)) {
                enc = fptr->encs.enc;
                goto invalid;
            }
            n = MBCLEN_CHARFOUND_LEN(r);
            if (fptr->encs.enc) {
                c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
                                     fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
                                     fptr->encs.enc);
            }
            else {
                c = (unsigned char)fptr->cbuf.ptr[fptr->cbuf.off];
            }
            fptr->cbuf.off += n;
            fptr->cbuf.len -= n;
            rb_yield(UINT2NUM(c));
            rb_io_check_byte_readable(fptr);
        }
    }
    NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
    enc = io_input_encoding(fptr);
    while (io_fillbuf(fptr) >= 0) {
        r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off,
                                  fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
        if (MBCLEN_CHARFOUND_P(r) &&
            (n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
            c = rb_enc_codepoint(fptr->rbuf.ptr+fptr->rbuf.off,
                                 fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
            fptr->rbuf.off += n;
            fptr->rbuf.len -= n;
            rb_yield(UINT2NUM(c));
        }
        else if (MBCLEN_INVALID_P(r)) {
            goto invalid;
        }
        else if (MBCLEN_NEEDMORE_P(r)) {
            char cbuf[8], *p = cbuf;
            int more = MBCLEN_NEEDMORE_LEN(r);
            if (more > numberof(cbuf)) goto invalid;
            more += n = fptr->rbuf.len;
            if (more > numberof(cbuf)) goto invalid;
            while ((n = (int)read_buffered_data(p, more, fptr)) > 0 &&
                   (p += n, (more -= n) > 0)) {
                if (io_fillbuf(fptr) < 0) goto invalid;
                if ((n = fptr->rbuf.len) > more) n = more;
            }
            r = rb_enc_precise_mbclen(cbuf, p, enc);
            if (!MBCLEN_CHARFOUND_P(r)) goto invalid;
            c = rb_enc_codepoint(cbuf, p, enc);
            rb_yield(UINT2NUM(c));
        }
        else {
            continue;
        }
        rb_io_check_byte_readable(fptr);
    }
    return io;

  invalid:
    rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
    UNREACHABLE_RETURN(Qundef);
}
each_line(sep = $/, chomp: false) {|line| ... } → self
each_line(limit, chomp: false) {|line| ... } → self
each_line(sep, limit, chomp: false) {|line| ... } → self
each_line → 枚举器

使用从流中读取的每一行调用块;返回 self。如果已在流的末尾,则不执行任何操作;请参阅 行 IO

如果没有给出任何参数,则根据行分隔符 $/ 读取行

f = File.new('t.txt')
f.each_line {|line| p line }
f.each_line {|line| fail 'Cannot happen' }
f.close

输出

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

仅给出字符串参数 sep 时,根据行分隔符 sep 读取行;请参阅 行分隔符

f = File.new('t.txt')
f.each_line('li') {|line| p line }
f.close

输出

"First li"
"ne\nSecond li"
"ne\n\nFourth li"
"ne\nFifth li"
"ne\n"

尊重 sep 的两个特殊值

f = File.new('t.txt')
# Get all into one string.
f.each_line(nil) {|line| p line }
f.close

输出

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

f.rewind
# Get paragraphs (up to two line separators).
f.each_line('') {|line| p line }

输出

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

仅给出整数参数 limit 时,限制每行中的字节数;请参阅 行限制

f = File.new('t.txt')
f.each_line(8) {|line| p line }
f.close

输出

"First li"
"ne\n"
"Second l"
"ine\n"
"\n"
"Fourth l"
"ine\n"
"Fifth li"
"ne\n"

给定参数 seplimit,合并两种行为

  • 使用由行分隔符 sep 确定的下一行调用。

  • 但返回的字节数不得超过限制。

可选关键字参数 chomp 指定是否省略行分隔符

f = File.new('t.txt')
f.each_line(chomp: true) {|line| p line }
f.close

输出

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

如果未给定块,则返回 Enumerator

别名:each
echo = flag 单击以切换源代码

启用/禁用回显。在某些平台上,此标志和原始/已煮模式的所有组合可能无效。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_set_echo(VALUE io, VALUE f)
{
    conmode t;
    int fd = GetReadFD(io);

    if (!getattr(fd, &t)) sys_fail(io);

    if (RTEST(f))
        set_echo(&t, NULL);
    else
        set_noecho(&t, NULL);

    if (!setattr(fd, &t)) sys_fail(io);

    return io;
}
echo? → true 或 false 单击以切换源代码

如果回显已启用,则返回 true

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_echo_p(VALUE io)
{
    conmode t;
    int fd = GetReadFD(io);

    if (!getattr(fd, &t)) sys_fail(io);
    return echo_p(&t) ? Qtrue : Qfalse;
}
eof → true 或 false 单击以切换源

如果流位于其末尾,则返回 true,否则返回 false;请参阅 位置

f = File.open('t.txt')
f.eof           # => false
f.seek(0, :END) # => 0
f.eof           # => true
f.close

除非流打开用于读取,否则引发异常;请参阅 模式

如果 self 是管道或套接字等流,则此方法将阻塞,直到另一端发送一些数据或将其关闭

r, w = IO.pipe
Thread.new { sleep 1; w.close }
r.eof? # => true # After 1-second wait.

r, w = IO.pipe
Thread.new { sleep 1; w.puts "a" }
r.eof?  # => false # After 1-second wait.

r, w = IO.pipe
r.eof?  # blocks forever

请注意,此方法将数据读入输入字节缓冲区。因此,IO#sysread 可能不会按照您使用 IO#eof? 的意图进行处理,除非您首先调用 IO#rewind(某些流不可用)。

VALUE
rb_io_eof(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);

    if (READ_CHAR_PENDING(fptr)) return Qfalse;
    if (READ_DATA_PENDING(fptr)) return Qfalse;
    READ_CHECK(fptr);
#if RUBY_CRLF_ENVIRONMENT
    if (!NEED_READCONV(fptr) && NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
        return RBOOL(eof(fptr->fd));;
    }
#endif
    return RBOOL(io_fillbuf(fptr) < 0);
}
别名也为:eof?
eof?
别名:eof
erase_line(p1) 单击以切换源
static VALUE
console_erase_line(VALUE io, VALUE val)
{
    int mode = mode_in_range(val, 2, "line erase");
#ifdef _WIN32
    HANDLE h;
    rb_console_size_t ws;
    COORD *pos = &ws.dwCursorPosition;
    DWORD w;

    h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
    if (!GetConsoleScreenBufferInfo(h, &ws)) {
        rb_syserr_fail(LAST_ERROR, 0);
    }
    w = winsize_col(&ws);
    switch (mode) {
      case 0:                   /* after cursor */
        w -= pos->X;
        break;
      case 1:                   /* before *and* cursor */
        w = pos->X + 1;
        pos->X = 0;
        break;
      case 2:                   /* entire line */
        pos->X = 0;
        break;
    }
    constat_clear(h, ws.wAttributes, w, *pos);
    return io;
#else
    rb_io_write(io, rb_sprintf("\x1b[%dK", mode));
#endif
    return io;
}
erase_screen(p1) 单击以切换源
static VALUE
console_erase_screen(VALUE io, VALUE val)
{
    int mode = mode_in_range(val, 3, "screen erase");
#ifdef _WIN32
    HANDLE h;
    rb_console_size_t ws;
    COORD *pos = &ws.dwCursorPosition;
    DWORD w;

    h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
    if (!GetConsoleScreenBufferInfo(h, &ws)) {
        rb_syserr_fail(LAST_ERROR, 0);
    }
    w = winsize_col(&ws);
    switch (mode) {
      case 0:   /* erase after cursor */
        w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
        break;
      case 1:   /* erase before *and* cursor */
        w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
        pos->X = 0;
        pos->Y = ws.srWindow.Top;
        break;
      case 2:   /* erase entire screen */
        w = (w * winsize_row(&ws));
        pos->X = 0;
        pos->Y = ws.srWindow.Top;
        break;
      case 3:   /* erase entire screen */
        w = (w * ws.dwSize.Y);
        pos->X = 0;
        pos->Y = 0;
        break;
    }
    constat_clear(h, ws.wAttributes, w, *pos);
#else
    rb_io_write(io, rb_sprintf("\x1b[%dJ", mode));
#endif
    return io;
}
IO#expect(pattern,timeout=9999999) → 数组 单击以切换源
IO#expect(pattern,timeout=9999999) { |result| ... } → nil

expect 库添加了实例方法 IO#expect,它类似于 TCL expect 扩展

要使用此方法,您必须需要 expect

require 'expect'

IO 中读取,直到给定的 pattern 匹配或 timeout 结束。

它返回一个数组,其中包含读取缓冲区,后跟匹配项。如果给出了一个块,则结果将生成到块中并返回 nil。

如果未调用块,它将等待从 IO 获得与给定 pattern 匹配的输入,或超时时间已过。从 IO 获得模式时,将返回一个数组。IO 中获得的整个字符串是数组的第一个元素,直到模式匹配,然后是指示模式与正则表达式中锚匹配的元素。

可选的超时参数(以秒为单位)定义等待模式的总时间。如果超时或找到 eof,则返回或产生 nil。但是,超时会话中的缓冲区将保留用于下一次 expect 调用。默认超时为 9999999 秒。

# File ext/pty/lib/expect.rb, line 33
def expect(pat,timeout=9999999)
  buf = ''.dup
  case pat
  when String
    e_pat = Regexp.new(Regexp.quote(pat))
  when Regexp
    e_pat = pat
  else
    raise TypeError, "unsupported pattern class: #{pat.class}"
  end
  @unusedBuf ||= ''
  while true
    if not @unusedBuf.empty?
      c = @unusedBuf.slice!(0)
    elsif !IO.select([self],nil,nil,timeout) or eof? then
      result = nil
      @unusedBuf = buf
      break
    else
      c = getc
    end
    buf << c
    if $expect_verbose
      STDOUT.print c
      STDOUT.flush
    end
    if mat=e_pat.match(buf) then
      result = [buf,*mat.captures]
      break
    end
  end
  if block_given? then
    yield result
  else
    return result
  end
  nil
end
external_encoding → 编码或 nil 单击以切换源

返回表示流编码的 Encoding 对象,如果流处于写入模式且未指定编码,则返回 nil

参见 编码

static VALUE
rb_io_external_encoding(VALUE io)
{
    rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;

    if (fptr->encs.enc2) {
        return rb_enc_from_encoding(fptr->encs.enc2);
    }
    if (fptr->mode & FMODE_WRITABLE) {
        if (fptr->encs.enc)
            return rb_enc_from_encoding(fptr->encs.enc);
        return Qnil;
    }
    return rb_enc_from_encoding(io_read_encoding(fptr));
}
fcntl(integer_cmd, argument) → integer 单击以切换源

调用 Posix 系统调用 fcntl(2),它提供了一种机制来发出低级命令以控制或查询面向文件的 I/O 流。参数和结果取决于平台。

如果 argument 是一个数字,则直接传递其值;如果它是一个字符串,则将其解释为一个二进制字节序列。(Array#pack 可能是一种构建此字符串的有用方法。)

并非在所有平台上都已实现。

static VALUE
rb_io_fcntl(int argc, VALUE *argv, VALUE io)
{
    VALUE req, arg;

    rb_scan_args(argc, argv, "11", &req, &arg);
    return rb_fcntl(io, req, arg);
}
fdatasync → 0 单击以切换源

通过操作系统的 fdatasync(2)(如果支持)立即将流中缓冲的所有数据写入磁盘,否则通过 fsync(2)(如果支持)写入磁盘;否则引发异常。

static VALUE
rb_io_fdatasync(VALUE io)
{
    rb_io_t *fptr;

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    if (io_fflush(fptr) < 0)
        rb_sys_fail_on_write(fptr);

    if ((int)rb_thread_io_blocking_region(nogvl_fdatasync, fptr, fptr->fd) == 0)
        return INT2FIX(0);

    /* fall back */
    return rb_io_fsync(io);
}
fileno → integer 单击以切换源

返回流的整数文件描述符

$stdin.fileno             # => 0
$stdout.fileno            # => 1
$stderr.fileno            # => 2
File.open('t.txt').fileno # => 10
f.close
static VALUE
rb_io_fileno(VALUE io)
{
    rb_io_t *fptr = RFILE(io)->fptr;
    int fd;

    rb_io_check_closed(fptr);
    fd = fptr->fd;
    return INT2FIX(fd);
}
别名也为: to_i
flush → self 单击以切换源

self 中缓冲的数据刷新到操作系统(但不一定刷新操作系统中缓冲的数据)

$stdout.print 'no newline' # Not necessarily flushed.
$stdout.flush              # Flushed.
VALUE
rb_io_flush(VALUE io)
{
    return rb_io_flush_raw(io, 1);
}
fsync → 0 单击以切换源

通过操作系统的 fsync(2) 立即将流中缓冲的所有数据写入磁盘。

注意此差异

  • IO#sync=:确保数据从流的内部缓冲区中刷新,但不保证操作系统实际上将数据写入磁盘。

  • IO#fsync:确保数据从内部缓冲区中刷新,并且数据写入磁盘。

如果操作系统不支持 fsync(2),则引发异常。

static VALUE
rb_io_fsync(VALUE io)
{
    rb_io_t *fptr;

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    if (io_fflush(fptr) < 0)
        rb_sys_fail_on_write(fptr);
    if ((int)rb_thread_io_blocking_region(nogvl_fsync, fptr, fptr->fd) < 0)
        rb_sys_fail_path(fptr->pathv);
    return INT2FIX(0);
}
getbyte → integer 或 nil 单击以切换源

从流中读取并返回下一个字节(范围 0..255);如果已在流的末尾,则返回 nil。请参阅 字节 IO

f = File.open('t.txt')
f.getbyte # => 70
f.close
f = File.open('t.rus')
f.getbyte # => 209
f.close

相关:IO#readbyte(可能会引发 EOFError)。

VALUE
rb_io_getbyte(VALUE io)
{
    rb_io_t *fptr;
    int c;

    GetOpenFile(io, fptr);
    rb_io_check_byte_readable(fptr);
    READ_CHECK(fptr);
    VALUE r_stdout = rb_ractor_stdout();
    if (fptr->fd == 0 && (fptr->mode & FMODE_TTY) && RB_TYPE_P(r_stdout, T_FILE)) {
        rb_io_t *ofp;
        GetOpenFile(r_stdout, ofp);
        if (ofp->mode & FMODE_TTY) {
            rb_io_flush(r_stdout);
        }
    }
    if (io_fillbuf(fptr) < 0) {
        return Qnil;
    }
    fptr->rbuf.off++;
    fptr->rbuf.len--;
    c = (unsigned char)fptr->rbuf.ptr[fptr->rbuf.off-1];
    return INT2FIX(c & 0xff);
}
getc → 字符或 nil 单击以切换源

从流中读取并返回下一个 1 个字符的字符串;如果已在流的末尾,则返回 nil。请参阅 字符 IO

f = File.open('t.txt')
f.getc     # => "F"
f.close
f = File.open('t.rus')
f.getc.ord # => 1090
f.close

相关:IO#readchar(可能会引发 EOFError)。

static VALUE
rb_io_getc(VALUE io)
{
    rb_io_t *fptr;
    rb_encoding *enc;

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);

    enc = io_input_encoding(fptr);
    READ_CHECK(fptr);
    return io_getc(fptr, enc);
}
getch(min: nil, time: nil, intr: nil) → 字符 单击以切换源

在原始模式下读取并返回一个字符。

有关参数的详细信息,请参阅 IO#raw

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_getch(int argc, VALUE *argv, VALUE io)
{
    rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
#ifndef _WIN32
    return ttymode(io, getc_call, io, set_rawmode, optp);
#else
    rb_io_t *fptr;
    VALUE str;
    wint_t c;
    int len;
    char buf[8];
    wint_t wbuf[2];
# ifndef HAVE_RB_IO_WAIT
    struct timeval *to = NULL, tv;
# else
    VALUE timeout = Qnil;
# endif

    GetOpenFile(io, fptr);
    if (optp) {
        if (optp->vtime) {
# ifndef HAVE_RB_IO_WAIT
            to = &tv;
# else
            struct timeval tv;
# endif
            tv.tv_sec = optp->vtime / 10;
            tv.tv_usec = (optp->vtime % 10) * 100000;
# ifdef HAVE_RB_IO_WAIT
            timeout = rb_fiber_scheduler_make_timeout(&tv);
# endif
        }
        switch (optp->vmin) {
          case 1: /* default */
            break;
          case 0: /* return nil when timed out */
            if (optp->vtime) break;
            /* fallthru */
          default:
            rb_warning("min option larger than 1 ignored");
        }
        if (optp->intr) {
# ifndef HAVE_RB_IO_WAIT
            int w = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, to);
            if (w < 0) rb_eof_error();
            if (!(w & RB_WAITFD_IN)) return Qnil;
# else
            VALUE result = rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), timeout);
            if (!RTEST(result)) return Qnil;
# endif
        }
        else if (optp->vtime) {
            rb_warning("Non-zero vtime option ignored if intr flag is unset");
        }
    }
    len = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getch, wbuf, RUBY_UBF_IO, 0);
    switch (len) {
      case 0:
        return Qnil;
      case 2:
        buf[0] = (char)wbuf[0];
        c = wbuf[1];
        len = 1;
        do {
            buf[len++] = (unsigned char)c;
        } while ((c >>= CHAR_BIT) && len < (int)sizeof(buf));
        return rb_str_new(buf, len);
      default:
        c = wbuf[0];
        len = rb_uv_to_utf8(buf, c);
        str = rb_utf8_str_new(buf, len);
        return rb_str_conv_enc(str, NULL, rb_default_external_encoding());
    }
#endif
}
getpass(prompt=nil) → 字符串 单击以切换源

读取并返回一行,不回显。打印 prompt,除非它是 nil

终止读取行的换行符从返回的字符串中删除,请参阅 String#chomp!

必须要求 ‘io/console’ 才能使用此方法。

require 'io/console'
IO::console.getpass("Enter password:")
Enter password:
# => "mypassword"
static VALUE
console_getpass(int argc, VALUE *argv, VALUE io)
{
    VALUE str, wio;

    rb_check_arity(argc, 0, 1);
    wio = rb_io_get_write_io(io);
    if (wio == io && io == rb_stdin) wio = rb_stderr;
    prompt(argc, argv, wio);
    rb_io_flush(wio);
    str = rb_ensure(getpass_call, io, puts_call, wio);
    return str_chomp(str);
}
gets(sep = $/, chomp: false) → 字符串或 nil 单击以切换源
gets(limit, chomp: false) → 字符串或 nil
gets(sep, limit, chomp: false) → 字符串或 nil

从流中读取并返回一行;将返回值分配给 $_。请参阅 行 IO

如果没有给出参数,则返回由行分隔符 $/ 确定的下一行,如果没有,则返回 nil

f = File.open('t.txt')
f.gets # => "First line\n"
$_     # => "First line\n"
f.gets # => "\n"
f.gets # => "Fourth line\n"
f.gets # => "Fifth line\n"
f.gets # => nil
f.close

仅给出字符串参数 sep 时,返回由行分隔符 sep 确定的下一行,如果没有,则返回 nil;请参阅 行分隔符

f = File.new('t.txt')
f.gets('l')   # => "First l"
f.gets('li')  # => "ine\nSecond li"
f.gets('lin') # => "ne\n\nFourth lin"
f.gets        # => "e\n"
f.close

尊重 sep 的两个特殊值

f = File.new('t.txt')
# Get all.
f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
# Get paragraph (up to two line separators).
f.gets('')  # => "First line\nSecond line\n\n"
f.close

仅给出整数参数 limit 时,限制行中的字节数;请参阅 行限制

# No more than one line.
File.open('t.txt') {|f| f.gets(10) } # => "First line"
File.open('t.txt') {|f| f.gets(11) } # => "First line\n"
File.open('t.txt') {|f| f.gets(12) } # => "First line\n"

给定参数 seplimit,合并两种行为

  • 返回由行分隔符 sep 确定的下一行,如果没有,则返回 nil

  • 但返回的字节数不得超过限制。

可选关键字参数 chomp 指定是否省略行分隔符

f = File.open('t.txt')
# Chomp the lines.
f.gets(chomp: true) # => "First line"
f.gets(chomp: true) # => "Second line"
f.gets(chomp: true) # => ""
f.gets(chomp: true) # => "Fourth line"
f.gets(chomp: true) # => "Fifth line"
f.gets(chomp: true) # => nil
f.close
static VALUE
rb_io_gets_m(int argc, VALUE *argv, VALUE io)
{
    VALUE str;

    str = rb_io_getline(argc, argv, io);
    rb_lastline_set(str);

    return str;
}
goto(p1, p2) 点击切换源代码
static VALUE
console_goto(VALUE io, VALUE y, VALUE x)
{
#ifdef _WIN32
    COORD pos;
    int fd = GetWriteFD(io);
    pos.X = NUM2UINT(x);
    pos.Y = NUM2UINT(y);
    if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
        rb_syserr_fail(LAST_ERROR, 0);
    }
#else
    rb_io_write(io, rb_sprintf("\x1b[%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
#endif
    return io;
}
goto_column(p1) 点击切换源代码
static VALUE
console_goto_column(VALUE io, VALUE val)
{
#ifdef _WIN32
    HANDLE h;
    rb_console_size_t ws;
    COORD *pos = &ws.dwCursorPosition;

    h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
    if (!GetConsoleScreenBufferInfo(h, &ws)) {
        rb_syserr_fail(LAST_ERROR, 0);
    }
    pos->X = NUM2INT(val);
    if (!SetConsoleCursorPosition(h, *pos)) {
        rb_syserr_fail(LAST_ERROR, 0);
    }
#else
    rb_io_write(io, rb_sprintf("\x1b[%dG", NUM2UINT(val)+1));
#endif
    return io;
}
iflush 点击切换源代码

刷新内核中的输入缓冲区。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_iflush(VALUE io)
{
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
    int fd = GetReadFD(io);
    if (tcflush(fd, TCIFLUSH)) sys_fail(io);
#endif

    return io;
}
inspect → string 点击切换源代码

返回 self 的字符串表示形式

f = File.open('t.txt')
f.inspect # => "#<File:t.txt>"
f.close
static VALUE
rb_io_inspect(VALUE obj)
{
    rb_io_t *fptr;
    VALUE result;
    static const char closed[] = " (closed)";

    fptr = RFILE(obj)->fptr;
    if (!fptr) return rb_any_to_s(obj);
    result = rb_str_new_cstr("#<");
    rb_str_append(result, rb_class_name(CLASS_OF(obj)));
    rb_str_cat2(result, ":");
    if (NIL_P(fptr->pathv)) {
        if (fptr->fd < 0) {
            rb_str_cat(result, closed+1, strlen(closed)-1);
        }
        else {
            rb_str_catf(result, "fd %d", fptr->fd);
        }
    }
    else {
        rb_str_append(result, fptr->pathv);
        if (fptr->fd < 0) {
            rb_str_cat(result, closed, strlen(closed));
        }
    }
    return rb_str_cat2(result, ">");
}
internal_encoding → encoding or nil 点击切换源代码

返回表示内部字符串编码的 Encoding 对象(如果指定了转换)或 nil(否则)。

参见 编码

static VALUE
rb_io_internal_encoding(VALUE io)
{
    rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;

    if (!fptr->encs.enc2) return Qnil;
    return rb_enc_from_encoding(io_read_encoding(fptr));
}
ioctl(integer_cmd, argument) → integer 点击切换源代码

调用 Posix 系统调用 ioctl(2),向 I/O 设备发出低级命令。

向 I/O 设备发出低级命令。参数和返回值取决于平台。调用的效果取决于平台。

如果参数 argument 是整数,则直接传递;如果它是一个字符串,则解释为二进制字节序列。

并非在所有平台上都已实现。

static VALUE
rb_io_ioctl(int argc, VALUE *argv, VALUE io)
{
    VALUE req, arg;

    rb_scan_args(argc, argv, "11", &req, &arg);
    return rb_ioctl(io, req, arg);
}
ioflush 点击切换源代码

刷新内核中的输入和输出缓冲区。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_ioflush(VALUE io)
{
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
    int fd1 = GetReadFD(io);
    int fd2 = GetWriteFD(io);

    if (fd2 != -1 && fd1 != fd2) {
        if (tcflush(fd1, TCIFLUSH)) sys_fail(io);
        if (tcflush(fd2, TCOFLUSH)) sys_fail(io);
    }
    else {
        if (tcflush(fd1, TCIOFLUSH)) sys_fail(io);
    }
#endif

    return io;
}
isatty → true or false 点击切换源代码

如果流与终端设备 (tty) 关联,则返回 true,否则返回 false

f = File.new('t.txt').isatty    #=> false
f.close
f = File.new('/dev/tty').isatty #=> true
f.close
static VALUE
rb_io_isatty(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    return RBOOL(isatty(fptr->fd) != 0);
}
别名:tty?
lineno → integer 点击切换源代码

返回流的当前行号;请参阅 行号

static VALUE
rb_io_lineno(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);
    return INT2NUM(fptr->lineno);
}
lineno = integer → integer 点击切换源代码

设置并返回流的行号;请参阅 行号

static VALUE
rb_io_set_lineno(VALUE io, VALUE lineno)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);
    fptr->lineno = NUM2INT(lineno);
    return lineno;
}
noecho {|io| } 点击切换源代码

在禁用回显的情况下生成 self

STDIN.noecho(&:gets)

将读取并返回一行,而不会回显。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_noecho(VALUE io)
{
    return ttymode(io, rb_yield, io, set_noecho, NULL);
}
nonblock {|io| } → object 点击切换源代码
nonblock(boolean) {|io| } → object

在非阻塞模式下生成 self

当提供 false 作为参数时,将在阻塞模式下生成 self。在执行块后,将恢复原始模式。

static VALUE
rb_io_nonblock_block(int argc, VALUE *argv, VALUE self)
{
    int nb = 1;

    int descriptor = rb_io_descriptor(self);

    if (argc > 0) {
        VALUE v;
        rb_scan_args(argc, argv, "01", &v);
        nb = RTEST(v);
    }

    int current_flags = get_fcntl_flags(descriptor);
    int restore[2] = {descriptor, current_flags};

    if (!io_nonblock_set(descriptor, current_flags, nb))
        return rb_yield(self);

    return rb_ensure(rb_yield, self, io_nonblock_restore, (VALUE)restore);
}
nonblock = boolean → boolean 单击切换源

将流的非阻塞模式设置为 true 时启用,将阻塞模式设置为 false 时禁用。

此方法为 ios 中的文件描述符设置或清除 O_NONBLOCK 标志。

大多数 IO 方法的行为不受此标志影响,因为它们在 EAGAIN 和部分读/写后重试系统调用以完成其任务。(例外是 IO#syswrite,它不会重试。)

此方法可用于清除标准 I/O 的非阻塞模式。由于非阻塞方法(read_nonblock 等)设置非阻塞模式,但不会清除它,因此此方法可用如下所示。

END { STDOUT.nonblock = false }
STDOUT.write_nonblock("foo")

由于该标志在进程之间共享,并且许多非 Ruby 命令不期望标准 I/O 处于非阻塞模式,因此在 Ruby 程序退出之前清除该标志是安全的。

例如,以下 Ruby 程序使 STDIN/STDOUT/STDER 退出非阻塞模式。(STDIN、STDOUT 和 STDERR 连接到终端。因此,使其中一个处于非阻塞模式会影响其他两个。)因此,cat 命令尝试从标准输入读取,并导致“资源暂时不可用”错误 (EAGAIN)。

% ruby -e '
STDOUT.write_nonblock("foo\n")'; cat
foo
cat: -: Resource temporarily unavailable

清除标志使 cat 命令的行为正常。(cat 命令等待标准输入的输入。)

% ruby -rio/nonblock -e '
END { STDOUT.nonblock = false }
STDOUT.write_nonblock("foo")
'; cat
foo
static VALUE
rb_io_nonblock_set(VALUE self, VALUE value)
{
    if (RTEST(value)) {
        rb_io_t *fptr;
        GetOpenFile(self, fptr);
        rb_io_set_nonblock(fptr);
    }
    else {
        int descriptor = rb_io_descriptor(self);
        io_nonblock_set(descriptor, get_fcntl_flags(descriptor), RTEST(value));
    }

    return self;
}
nonblock? → boolean 单击切换源

如果 IO 对象处于非阻塞模式,则返回 true

static VALUE
rb_io_nonblock_p(VALUE io)
{
    if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK)
        return Qtrue;
    return Qfalse;
}
nread → int 单击切换源

返回可以在不阻塞的情况下读取的字节数。如果没有可用信息,则返回零。

您必须需要“io/wait”才能使用此方法。

static VALUE
io_nread(VALUE io)
{
    rb_io_t *fptr;
    int len;
    ioctl_arg n;

    GetOpenFile(io, fptr);
    rb_io_check_readable(fptr);
    len = rb_io_read_pending(fptr);
    if (len > 0) return INT2FIX(len);

#ifdef HAVE_RB_IO_DESCRIPTOR
    int fd = rb_io_descriptor(io);
#else
    int fd = fptr->fd;
#endif

    if (!FIONREAD_POSSIBLE_P(fd)) return INT2FIX(0);
    if (ioctl(fd, FIONREAD, &n)) return INT2FIX(0);
    if (n > 0) return ioctl_arg2num(n);
    return INT2FIX(0);
}
oflush 单击切换源

刷新内核中的输出缓冲区。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_oflush(VALUE io)
{
    int fd = GetWriteFD(io);
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
    if (tcflush(fd, TCOFLUSH)) sys_fail(io);
#endif
    (void)fd;
    return io;
}
path → string or nil 单击切换源

返回与 IO 关联的路径,如果 IO 没有关联的路径,则返回 nil。不能保证该路径存在于文件系统中。

$stdin.path # => "<STDIN>"

File.open("testfile") {|f| f.path} # => "testfile"
VALUE
rb_io_path(VALUE io)
{
    rb_io_t *fptr = RFILE(io)->fptr;

    if (!fptr)
        return Qnil;

    return rb_obj_dup(fptr->pathv);
}
别名:to_path
pathconf(p1) 单击切换源

使用 fpathconf() 返回路径名配置变量。

name 应为以 PC_ 开头的 Etc 下的常量。

返回值为整数或 nil。nil 表示不确定限制。(fpathconf() 返回 -1 但 errno 未设置。)

require 'etc'
IO.pipe {|r, w|
  p w.pathconf(Etc::PC_PIPE_BUF) #=> 4096
}
static VALUE
io_pathconf(VALUE io, VALUE arg)
{
    int name;
    long ret;

    name = NUM2INT(arg);

    errno = 0;
    ret = fpathconf(rb_io_descriptor(io), name);
    if (ret == -1) {
        if (errno == 0) /* no limit */
            return Qnil;
        rb_sys_fail("fpathconf");
    }
    return LONG2NUM(ret);
}
pid → integer or nil 单击以切换源

返回与流关联的子进程的进程 ID,该进程将由 IO#popen 设置,或者如果流不是由 IO#popen 创建的,则返回 nil

pipe = IO.popen("-")
if pipe
  $stderr.puts "In parent, child pid is #{pipe.pid}"
else
  $stderr.puts "In child, pid is #{$$}"
end

输出

In child, pid is 26209
In parent, child pid is 26209
static VALUE
rb_io_pid(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    if (!fptr->pid)
        return Qnil;
    return PIDT2NUM(fptr->pid);
}
pos
别名:tell
pos = new_position → new_position 单击以切换源

查找给定的 new_position(以字节为单位);请参见 位置

f = File.open('t.txt')
f.tell     # => 0
f.pos = 20 # => 20
f.tell     # => 20
f.close

相关:IO#seekIO#tell

static VALUE
rb_io_set_pos(VALUE io, VALUE offset)
{
    rb_io_t *fptr;
    rb_off_t pos;

    pos = NUM2OFFT(offset);
    GetOpenFile(io, fptr);
    pos = io_seek(fptr, pos, SEEK_SET);
    if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);

    return OFFT2NUM(pos);
}
pread(maxlen, offset) → string 单击以切换源
pread(maxlen, offset, out_string) → string

行为类似于 IO#readpartial,但它

  • 在给定的 offset(以字节为单位)处读取。

  • 忽略流的位置(请参见 位置),并且不修改该位置。

  • 绕过流中的任何用户空间缓冲。

由于此方法不会干扰流的状态(尤其是其位置),因此 pread 允许多个线程和进程使用同一 IO 对象在各个偏移处进行读取。

f = File.open('t.txt')
f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.pos  # => 52
# Read 12 bytes at offset 0.
f.pread(12, 0) # => "First line\n"
# Read 9 bytes at offset 8.
f.pread(9, 8)  # => "ne\nSecon"
f.close

在某些平台上不可用。

static VALUE
rb_io_pread(int argc, VALUE *argv, VALUE io)
{
    VALUE len, offset, str;
    rb_io_t *fptr;
    ssize_t n;
    struct prdwr_internal_arg arg = {.io = io};
    int shrinkable;

    rb_scan_args(argc, argv, "21", &len, &offset, &str);
    arg.count = NUM2SIZET(len);
    arg.offset = NUM2OFFT(offset);

    shrinkable = io_setstrbuf(&str, (long)arg.count);
    if (arg.count == 0) return str;
    arg.buf = RSTRING_PTR(str);

    GetOpenFile(io, fptr);
    rb_io_check_byte_readable(fptr);

    arg.fd = fptr->fd;
    rb_io_check_closed(fptr);

    rb_str_locktmp(str);
    n = (ssize_t)rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);

    if (n < 0) {
        rb_sys_fail_path(fptr->pathv);
    }
    io_set_read_length(str, n, shrinkable);
    if (n == 0 && arg.count > 0) {
        rb_eof_error();
    }

    return str;
}
pressed?(p1) 单击以切换源
static VALUE
console_key_pressed_p(VALUE io, VALUE k)
{
    int vk = -1;

    if (FIXNUM_P(k)) {
        vk = NUM2UINT(k);
    }
    else {
        const struct vktable *t;
        const char *kn;
        if (SYMBOL_P(k)) {
            k = rb_sym2str(k);
            kn = RSTRING_PTR(k);
        }
        else {
            kn = StringValuePtr(k);
        }
        t = console_win32_vk(kn, RSTRING_LEN(k));
        if (!t || (vk = (short)t->vk) == -1) {
            rb_raise(rb_eArgError, "unknown virtual key code: % "PRIsVALUE, k);
        }
    }
    return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse;
}
print(*objects) → nil 单击以切换源

将给定的对象写入流;返回 nil。追加输出记录分隔符 $OUTPUT_RECORD_SEPARATOR ($\),如果它不为 nil。请参见 行 IO

如果给出了参数 objects,则对于每个对象

  • 如果它不是字符串,则通过其方法 to_s 进行转换。

  • 写入流。

  • 如果不是最后一个对象,则如果输出字段分隔符 $OUTPUT_FIELD_SEPARATOR ($,) 不为 nil,则写入该分隔符。

使用默认分隔符

f = File.open('t.tmp', 'w+')
objects = [0, 0.0, Rational(0, 1), Complex(0, 0), :zero, 'zero']
p $OUTPUT_RECORD_SEPARATOR
p $OUTPUT_FIELD_SEPARATOR
f.print(*objects)
f.rewind
p f.read
f.close

输出

nil
nil
"00.00/10+0izerozero"

使用指定的分隔符

$\ = "\n"
$, = ','
f.rewind
f.print(*objects)
f.rewind
p f.read

输出

"0,0.0,0/1,0+0i,zero,zero\n"

如果没有给出参数,则写入 $_ 的内容(通常是最近的用户输入)

f = File.open('t.tmp', 'w+')
gets # Sets $_ to the most recent user input.
f.print
f.close
printf(format_string, *objects) → nil 单击以切换源

格式化并向流中写入 objects

有关 format_string 的详细信息,请参阅 格式规范

VALUE
rb_io_printf(int argc, const VALUE *argv, VALUE out)
{
    rb_io_write(out, rb_f_sprintf(argc, argv));
    return Qnil;
}
putc(object) → object 单击以切换源

向流中写入一个字符。请参阅 字符 IO

如果 object 是数字,则在必要时转换为整数,然后写入其代码为最低有效字节的字符;如果 object 是字符串,则写入第一个字符

$stdout.putc "A"
$stdout.putc 65

输出

AA
static VALUE
rb_io_putc(VALUE io, VALUE ch)
{
    VALUE str;
    if (RB_TYPE_P(ch, T_STRING)) {
        str = rb_str_substr(ch, 0, 1);
    }
    else {
        char c = NUM2CHR(ch);
        str = rb_str_new(&c, 1);
    }
    rb_io_write(io, str);
    return ch;
}
puts(*objects) → nil 单击以切换源

将给定的 objects 写入流中,该流必须打开以进行写入;返回 nil。在每个不以换行序列结尾的序列后写入一个换行符。如果在不带参数的情况下调用,则写入一个换行符。请参阅 行 IO

请注意,每个添加的换行符都是字符 "\n"<//tt>,而不是输出记录分隔符 (<tt>$\)。

对每个对象的处理

  • 字符串:写入字符串。

  • 既不是字符串也不是数组:写入 object.to_s

  • 数组:写入数组的每个元素;数组可以嵌套。

为了使这些示例简短,我们定义此帮助器方法

def show(*objects)
  # Puts objects to file.
  f = File.new('t.tmp', 'w+')
  f.puts(objects)
  # Return file content.
  f.rewind
  p f.read
  f.close
end

# Strings without newlines.
show('foo', 'bar', 'baz')     # => "foo\nbar\nbaz\n"
# Strings, some with newlines.
show("foo\n", 'bar', "baz\n") # => "foo\nbar\nbaz\n"

# Neither strings nor arrays:
show(0, 0.0, Rational(0, 1), Complex(9, 0), :zero)
# => "0\n0.0\n0/1\n9+0i\nzero\n"

# Array of strings.
show(['foo', "bar\n", 'baz']) # => "foo\nbar\nbaz\n"
# Nested arrays.
show([[[0, 1], 2, 3], 4, 5])  # => "0\n1\n2\n3\n4\n5\n"
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
    VALUE line, args[2];

    /* if no argument given, print newline. */
    if (argc == 0) {
        rb_io_write(out, rb_default_rs);
        return Qnil;
    }
    for (int i = 0; i < argc; i++) {
        // Convert the argument to a string:
        if (RB_TYPE_P(argv[i], T_STRING)) {
            line = argv[i];
        }
        else if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
            continue;
        }
        else {
            line = rb_obj_as_string(argv[i]);
        }

        // Write the line:
        int n = 0;
        if (RSTRING_LEN(line) == 0) {
            args[n++] = rb_default_rs;
        }
        else {
            args[n++] = line;
            if (!rb_str_end_with_asciichar(line, '\n')) {
                args[n++] = rb_default_rs;
            }
        }

        rb_io_writev(out, n, args);
    }

    return Qnil;
}
pwrite(object, offset) → integer 单击以切换源

行为类似于 IO#write,但它

  • 在给定的 offset(以字节为单位)处写入。

  • 忽略流的位置(请参见 位置),并且不修改该位置。

  • 绕过流中的任何用户空间缓冲。

由于此方法不会干扰流的状态(特别是其位置),因此 pwrite 允许多个线程和进程使用同一 IO 对象在各个偏移量处进行写入。

f = File.open('t.tmp', 'w+')
# Write 6 bytes at offset 3.
f.pwrite('ABCDEF', 3) # => 6
f.rewind
f.read # => "\u0000\u0000\u0000ABCDEF"
f.close

在某些平台上不可用。

static VALUE
rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
{
    rb_io_t *fptr;
    ssize_t n;
    struct prdwr_internal_arg arg = {.io = io};
    VALUE tmp;

    if (!RB_TYPE_P(str, T_STRING))
        str = rb_obj_as_string(str);

    arg.offset = NUM2OFFT(offset);

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);
    arg.fd = fptr->fd;

    tmp = rb_str_tmp_frozen_acquire(str);
    arg.buf = RSTRING_PTR(tmp);
    arg.count = (size_t)RSTRING_LEN(tmp);

    n = (ssize_t)rb_thread_io_blocking_call(internal_pwrite_func, &arg, fptr->fd, RB_WAITFD_OUT);
    if (n < 0) rb_sys_fail_path(fptr->pathv);
    rb_str_tmp_frozen_release(str, tmp);

    return SSIZET2NUM(n);
}
raw(min: nil, time: nil, intr: nil) {|io| } 单击以切换源

在原始模式下生成 self,并返回块的结果。

STDIN.raw(&:gets)

将读取并返回一行,而无需回显和行编辑。

参数 min 指定在执行读取操作时应接收的最小字节数。(默认值:1)

参数 time 以 1/10 秒的精度指定超时时间(以为单位)。(默认值:0)

如果参数 intrtrue,则启用中断、中断、退出和挂起特殊字符。

有关更多详细信息,请参阅 termios 手册页。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_raw(int argc, VALUE *argv, VALUE io)
{
    rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
    return ttymode(io, rb_yield, io, set_rawmode, optp);
}
raw!(min: nil, time: nil, intr: nil) → io 单击以切换源

启用原始模式,并返回 io

如果需要返回终端模式,请使用 io.raw { ... }

有关参数的详细信息,请参阅 IO#raw

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_set_raw(int argc, VALUE *argv, VALUE io)
{
    conmode t;
    rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
    int fd = GetReadFD(io);
    if (!getattr(fd, &t)) sys_fail(io);
    set_rawmode(&t, optp);
    if (!setattr(fd, &t)) sys_fail(io);
    return io;
}
read(maxlen = nil, out_string = nil) → new_string, out_string, or nil 单击以切换源

从流中读取字节;该流必须打开以供读取(参见访问模式

  • 如果maxlennil,则使用流的数据模式读取所有字节。

  • 否则以二进制模式读取最多maxlen字节。

返回一个字符串(一个新字符串或给定的out_string),其中包含读取的字节。字符串的编码取决于maxLenout_string

  • maxlennil:使用self的内部编码(无论是否给出了out_string)。

  • maxlen不为nil

    • 给出了out_string:不修改out_string的编码。

    • 未给出out_string:使用 ASCII-8BIT。

不带参数out_string

当省略参数out_string时,返回值是一个新字符串

f = File.new('t.txt')
f.read
# => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
f.read(30) # => "First line\r\nSecond line\r\n\r\nFou"
f.read(30) # => "rth line\r\nFifth line\r\n"
f.read(30) # => nil
f.close

如果maxlen为零,则返回一个空字符串。

带参数out_string

当给出了参数out_string时,返回值是out_string,其内容被替换

f = File.new('t.txt')
s = 'foo'      # => "foo"
f.read(nil, s) # => "First line\nSecond line\n\nFourth line\nFifth line\n"
s              # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
s = 'bar'
f.read(30, s)  # => "First line\r\nSecond line\r\n\r\nFou"
s              # => "First line\r\nSecond line\r\n\r\nFou"
s = 'baz'
f.read(30, s)  # => "rth line\r\nFifth line\r\n"
s              # => "rth line\r\nFifth line\r\n"
s = 'bat'
f.read(30, s)  # => nil
s              # => ""
f.close

请注意,此方法的行为类似于 C 中的 fread() 函数。这意味着它会重试调用 read(2) 系统调用以读取指定 maxlen 的数据(或直到 EOF)。

即使流处于非阻塞模式,此行为也会保留。(此方法与其他方法一样,对非阻塞标志不敏感。)

如果您需要类似于单个 read(2) 系统调用的行为,请考虑readpartialread_nonblocksysread

相关:IO#write

static VALUE
io_read(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
    long n, len;
    VALUE length, str;
    int shrinkable;
#if RUBY_CRLF_ENVIRONMENT
    int previous_mode;
#endif

    rb_scan_args(argc, argv, "02", &length, &str);

    if (NIL_P(length)) {
        GetOpenFile(io, fptr);
        rb_io_check_char_readable(fptr);
        return read_all(fptr, remain_size(fptr), str);
    }
    len = NUM2LONG(length);
    if (len < 0) {
        rb_raise(rb_eArgError, "negative length %ld given", len);
    }

    shrinkable = io_setstrbuf(&str,len);

    GetOpenFile(io, fptr);
    rb_io_check_byte_readable(fptr);
    if (len == 0) {
        io_set_read_length(str, 0, shrinkable);
        return str;
    }

    READ_CHECK(fptr);
#if RUBY_CRLF_ENVIRONMENT
    previous_mode = set_binary_mode_with_seek_cur(fptr);
#endif
    n = io_fread(str, 0, len, fptr);
    io_set_read_length(str, n, shrinkable);
#if RUBY_CRLF_ENVIRONMENT
    if (previous_mode == O_TEXT) {
        setmode(fptr->fd, O_TEXT);
    }
#endif
    if (n == 0) return Qnil;

    return str;
}
read_nonblock(maxlen [, options]) → string 单击以切换源
read_nonblock(maxlen, outbuf [, options]) → outbuf

在为底层文件描述符设置 O_NONBLOCK 之后,使用 read(2) 系统调用从ios读取最多maxlen字节。

如果存在可选的outbuf参数,则它必须引用一个String,该字符串将接收数据。即使outbuf在开始时不为空,在方法调用后它也将只包含接收到的数据。

read_nonblock只调用 read(2) 系统调用。它会导致 read(2) 系统调用导致的所有错误:Errno::EWOULDBLOCK、Errno::EINTR 等。调用者应注意此类错误。

如果异常是 Errno::EWOULDBLOCK 或 Errno::EAGAIN,则它将由IO::WaitReadable扩展。因此,IO::WaitReadable可用于拯救异常以重试 read_nonblock。

read_nonblock在 EOF 时导致EOFError

在某些平台(例如 Windows)上,非阻塞模式不受套接字以外的 IO 对象支持。在这种情况下,将引发 Errno::EBADF。

如果读取字节缓冲区不为空,read_nonblock 将从缓冲区读取内容,就像 readpartial 一样。在这种情况下,不会调用 read(2) 系统调用。

read_nonblock 引发 IO::WaitReadable 类型的异常时,read_nonblock 不应被调用,直到 io 可读以避免繁忙循环。这可按如下方式完成。

# emulates blocking read (readpartial).
begin
  result = io.read_nonblock(maxlen)
rescue IO::WaitReadable
  IO.select([io])
  retry
end

虽然 IO#read_nonblock 不会引发 IO::WaitWritable,但 OpenSSL::Buffering#read_nonblock 可以引发 IO::WaitWritable。如果 IO 和 SSL 应多态使用,则 IO::WaitWritable 也应被捕获。有关示例代码,请参阅 OpenSSL::Buffering#read_nonblock 文档。

请注意,此方法与 readpartial 相同,只是设置了非阻塞标志。

通过将关键字参数 exception 指定为 false,您可以指示 read_nonblock 不应引发 IO::WaitReadable 异常,而应返回符号 :wait_readable。在 EOF 时,它将返回 nil,而不是引发 EOFError

# File io.rb, line 62
def read_nonblock(len, buf = nil, exception: true)
  Primitive.io_read_nonblock(len, buf, exception)
end
readbyte → integer 单击以切换源

从流中读取并返回下一个字节(范围 0..255);如果已在流末尾,则引发 EOFError。请参阅 字节 IO

f = File.open('t.txt')
f.readbyte # => 70
f.close
f = File.open('t.rus')
f.readbyte # => 209
f.close

相关:IO#getbyte(不会引发 EOFError)。

static VALUE
rb_io_readbyte(VALUE io)
{
    VALUE c = rb_io_getbyte(io);

    if (NIL_P(c)) {
        rb_eof_error();
    }
    return c;
}
readchar → string 单击以切换源

从流中读取并返回下一个 1 个字符的字符串;如果已在流末尾,则引发 EOFError。请参阅 字符 IO

f = File.open('t.txt')
f.readchar     # => "F"
f.close
f = File.open('t.rus')
f.readchar.ord # => 1090
f.close

相关:IO#getc(不会引发 EOFError)。

static VALUE
rb_io_readchar(VALUE io)
{
    VALUE c = rb_io_getc(io);

    if (NIL_P(c)) {
        rb_eof_error();
    }
    return c;
}
readline(sep = $/, chomp: false) → string 单击以切换源
readline(limit, chomp: false) → string
readline(sep, limit, chomp: false) → string

使用 IO#gets 读取一行,但如果已在流末尾,则引发 EOFError

可选关键字参数 chomp 指定是否省略行分隔符。

# File io.rb, line 133
def readline(sep = $/, limit = nil, chomp: false)
  Primitive.io_readline(sep, limit, chomp)
end
readlines(sep = $/, chomp: false) → array 单击以切换源
readlines(limit, chomp: false) → array
readlines(sep, limit, chomp: false) → array

读取并返回流中的所有剩余行;不修改 $_。请参见 行 IO

如果没有给出参数,则返回由行分隔符 $/ 确定的行,如果没有,则返回 nil

f = File.new('t.txt')
f.readlines
# => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
f.readlines # => []
f.close

仅给出字符串参数 sep 时,返回由行分隔符 sep 确定的行,如果没有,则返回 nil;请参见 行分隔符

f = File.new('t.txt')
f.readlines('li')
# => ["First li", "ne\nSecond li", "ne\n\nFourth li", "ne\nFifth li", "ne\n"]
f.close

尊重 sep 的两个特殊值

f = File.new('t.txt')
# Get all into one string.
f.readlines(nil)
# => ["First line\nSecond line\n\nFourth line\nFifth line\n"]
# Get paragraphs (up to two line separators).
f.rewind
f.readlines('')
# => ["First line\nSecond line\n\n", "Fourth line\nFifth line\n"]
f.close

仅给出整数参数 limit 时,限制每行中的字节数;请参阅 行限制

f = File.new('t.txt')
f.readlines(8)
# => ["First li", "ne\n", "Second l", "ine\n", "\n", "Fourth l", "ine\n", "Fifth li", "ne\n"]
f.close

给定参数 seplimit,合并两种行为

  • 返回由行分隔符 sep 确定的行。

  • 但返回的行中的字节数不会超过限制允许的字节数。

可选关键字参数 chomp 指定是否省略行分隔符

f = File.new('t.txt')
f.readlines(chomp: true)
# => ["First line", "Second line", "", "Fourth line", "Fifth line"]
f.close
static VALUE
rb_io_readlines(int argc, VALUE *argv, VALUE io)
{
    struct getline_arg args;

    prepare_getline_args(argc, argv, &args, io);
    return io_readlines(&args, io);
}
readpartial(maxlen) → string 单击以切换源
readpartial(maxlen, out_string) → out_string

从流中读取最多 maxlen 个字节;返回一个字符串(一个新字符串或给定的 out_string)。其编码为

  • 如果给定了 out_string,则为 out_string 的未更改编码。

  • 否则为 ASCII-8BIT。

  • 如果可用,则包含流中的 maxlen 个字节。

  • 否则包含所有可用的字节(如果有可用字节)。

  • 否则为空字符串。

如果给定了单个非负整数参数 maxlen,则返回一个新字符串

f = File.new('t.txt')
f.readpartial(20) # => "First line\nSecond l"
f.readpartial(20) # => "ine\n\nFourth line\n"
f.readpartial(20) # => "Fifth line\n"
f.readpartial(20) # Raises EOFError.
f.close

如果给定了参数 maxlen 和字符串参数 out_string,则返回修改后的 out_string

f = File.new('t.txt')
s = 'foo'
f.readpartial(20, s) # => "First line\nSecond l"
s = 'bar'
f.readpartial(0, s)  # => ""
f.close

此方法对于管道、套接字或 tty 等流非常有用。它仅在没有数据立即可用时阻塞。这意味着它仅在以下所有条件都为真时才阻塞

  • 流中的字节缓冲区为空。

  • 流的内容为空。

  • 流未处于 EOF。

当被阻塞时,此方法等待流上的更多数据或 EOF

  • 如果读取更多数据,则此方法返回数据。

  • 如果达到 EOF,则此方法引发 EOFError

当未被阻塞时,此方法立即响应

  • 如果有任何数据,则从缓冲区返回数据。

  • 否则,如果有任何数据,则从流中返回数据。

  • 否则,如果流已达到 EOF,则引发 EOFError

请注意,此方法类似于 sysread。不同之处在于

  • 如果字节缓冲区不为空,则从字节缓冲区读取,而不是“对缓冲的 IO (IOError) 执行 sysread”。

  • 它不会导致 Errno::EWOULDBLOCK 和 Errno::EINTR。当 readpartial 通过 read 系统调用遇到 EWOULDBLOCK 和 EINTR 时,readpartial 会重试系统调用。

后者意味着 readpartial 对非阻塞标志不敏感。在 IO#sysread 导致 Errno::EWOULDBLOCK 的情况下,它会阻塞,就像 fd 处于阻塞模式一样。

示例

#                        # Returned      Buffer Content    Pipe Content
r, w = IO.pipe           #
w << 'abc'               #               ""                "abc".
r.readpartial(4096)      # => "abc"      ""                ""
r.readpartial(4096)      # (Blocks because buffer and pipe are empty.)

#                        # Returned      Buffer Content    Pipe Content
r, w = IO.pipe           #
w << 'abc'               #               ""                "abc"
w.close                  #               ""                "abc" EOF
r.readpartial(4096)      # => "abc"      ""                 EOF
r.readpartial(4096)      # raises EOFError

#                        # Returned      Buffer Content    Pipe Content
r, w = IO.pipe           #
w << "abc\ndef\n"        #               ""                "abc\ndef\n"
r.gets                   # => "abc\n"    "def\n"           ""
w << "ghi\n"             #               "def\n"           "ghi\n"
r.readpartial(4096)      # => "def\n"    ""                "ghi\n"
r.readpartial(4096)      # => "ghi\n"    ""                ""
static VALUE
io_readpartial(int argc, VALUE *argv, VALUE io)
{
    VALUE ret;

    ret = io_getpartial(argc, argv, io, Qnil, 0);
    if (NIL_P(ret))
        rb_eof_error();
    return ret;
}
ready? → 真值或假值 单击以切换源

如果可以在不阻塞的情况下获得输入,则返回真值,否则返回假值。

您必须需要“io/wait”才能使用此方法。

static VALUE
io_ready_p(VALUE io)
{
    rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
    struct timeval tv = {0, 0};
#endif

    GetOpenFile(io, fptr);
    rb_io_check_readable(fptr);
    if (rb_io_read_pending(fptr)) return Qtrue;

#ifndef HAVE_RB_IO_WAIT
    return wait_for_single_fd(fptr, RB_WAITFD_IN, &tv) ? Qtrue : Qfalse;
#else
    return io_wait_event(io, RUBY_IO_READABLE, RB_INT2NUM(0), 1);
#endif
}
reopen(other_io) → self 单击以切换源
reopen(path, mode = 'r', **opts) → self

将流重新关联到另一个流,该流可能属于不同的类。此方法可用于将现有流重定向到新目标。

如果给出了参数 other_io,则重新关联到该流

# Redirect $stdin from a file.
f = File.open('t.txt')
$stdin.reopen(f)
f.close

# Redirect $stdout to a file.
f = File.open('t.tmp', 'w')
$stdout.reopen(f)
f.close

如果给出了参数 path,则重新关联到该文件路径的新流

$stdin.reopen('t.txt')
$stdout.reopen('t.tmp', 'w')

可选关键字参数 opts 指定

static VALUE
rb_io_reopen(int argc, VALUE *argv, VALUE file)
{
    VALUE fname, nmode, opt;
    int oflags;
    rb_io_t *fptr;

    if (rb_scan_args(argc, argv, "11:", &fname, &nmode, &opt) == 1) {
        VALUE tmp = rb_io_check_io(fname);
        if (!NIL_P(tmp)) {
            return io_reopen(file, tmp);
        }
    }

    FilePathValue(fname);
    rb_io_taint_check(file);
    fptr = RFILE(file)->fptr;
    if (!fptr) {
        fptr = RFILE(file)->fptr = ZALLOC(rb_io_t);
    }

    if (!NIL_P(nmode) || !NIL_P(opt)) {
        int fmode;
        struct rb_io_encoding convconfig;

        rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
        if (RUBY_IO_EXTERNAL_P(fptr) &&
            ((fptr->mode & FMODE_READWRITE) & (fmode & FMODE_READWRITE)) !=
            (fptr->mode & FMODE_READWRITE)) {
            rb_raise(rb_eArgError,
                     "%s can't change access mode from \"%s\" to \"%s\"",
                     PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
                     rb_io_fmode_modestr(fmode));
        }
        fptr->mode = fmode;
        fptr->encs = convconfig;
    }
    else {
        oflags = rb_io_fmode_oflags(fptr->mode);
    }

    fptr->pathv = fname;
    if (fptr->fd < 0) {
        fptr->fd = rb_sysopen(fptr->pathv, oflags, 0666);
        fptr->stdio_file = 0;
        return file;
    }

    if (fptr->mode & FMODE_WRITABLE) {
        if (io_fflush(fptr) < 0)
            rb_sys_fail_on_write(fptr);
    }
    fptr->rbuf.off = fptr->rbuf.len = 0;

    if (fptr->stdio_file) {
        int e = rb_freopen(rb_str_encode_ospath(fptr->pathv),
                           rb_io_oflags_modestr(oflags),
                           fptr->stdio_file);
        if (e) rb_syserr_fail_path(e, fptr->pathv);
        fptr->fd = fileno(fptr->stdio_file);
        rb_fd_fix_cloexec(fptr->fd);
#ifdef USE_SETVBUF
        if (setvbuf(fptr->stdio_file, NULL, _IOFBF, 0) != 0)
            rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
#endif
        if (fptr->stdio_file == stderr) {
            if (setvbuf(fptr->stdio_file, NULL, _IONBF, BUFSIZ) != 0)
                rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
        }
        else if (fptr->stdio_file == stdout && isatty(fptr->fd)) {
            if (setvbuf(fptr->stdio_file, NULL, _IOLBF, BUFSIZ) != 0)
                rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
        }
    }
    else {
        int tmpfd = rb_sysopen(fptr->pathv, oflags, 0666);
        int err = 0;
        if (rb_cloexec_dup2(tmpfd, fptr->fd) < 0)
            err = errno;
        (void)close(tmpfd);
        if (err) {
            rb_syserr_fail_path(err, fptr->pathv);
        }
    }

    return file;
}
rewind → 0 单击以切换源

将流重新定位到其开头,将位置和行号都设置为零;请参见 位置行号

f = File.open('t.txt')
f.tell     # => 0
f.lineno   # => 0
f.gets     # => "First line\n"
f.tell     # => 12
f.lineno   # => 1
f.rewind   # => 0
f.tell     # => 0
f.lineno   # => 0
f.close

请注意,此方法不能与管道、tty 和套接字等流一起使用。

static VALUE
rb_io_rewind(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    if (io_seek(fptr, 0L, 0) < 0 && errno) rb_sys_fail_path(fptr->pathv);
    if (io == ARGF.current_file) {
        ARGF.lineno -= fptr->lineno;
    }
    fptr->lineno = 0;
    if (fptr->readconv) {
        clear_readconv(fptr);
    }

    return INT2FIX(0);
}
scroll_backward(p1) 单击以切换源
static VALUE
console_scroll_backward(VALUE io, VALUE val)
{
    return console_scroll(io, -NUM2INT(val));
}
scroll_forward(p1) 单击以切换源
static VALUE
console_scroll_forward(VALUE io, VALUE val)
{
    return console_scroll(io, +NUM2INT(val));
}
seek(offset, whence = IO::SEEK_SET) → 0 单击以切换源

查找由整数 offset(请参见 位置)和常量 whence 给出的位置,后者为以下之一

  • :CURIO::SEEK_CUR:将流重新定位到其当前位置加上给定的 offset

    f = File.open('t.txt')
    f.tell            # => 0
    f.seek(20, :CUR)  # => 0
    f.tell            # => 20
    f.seek(-10, :CUR) # => 0
    f.tell            # => 10
    f.close
    
  • :ENDIO::SEEK_END:将流重新定位到其末尾加上给定的 offset

    f = File.open('t.txt')
    f.tell            # => 0
    f.seek(0, :END)   # => 0  # Repositions to stream end.
    f.tell            # => 52
    f.seek(-20, :END) # => 0
    f.tell            # => 32
    f.seek(-40, :END) # => 0
    f.tell            # => 12
    f.close
    
  • :SETIO:SEEK_SET:将流重新定位到给定的 offset

    f = File.open('t.txt')
    f.tell            # => 0
    f.seek(20, :SET) # => 0
    f.tell           # => 20
    f.seek(40, :SET) # => 0
    f.tell           # => 40
    f.close
    

相关:IO#pos=IO#tell

static VALUE
rb_io_seek_m(int argc, VALUE *argv, VALUE io)
{
    VALUE offset, ptrname;
    int whence = SEEK_SET;

    if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
        whence = interpret_seek_whence(ptrname);
    }

    return rb_io_seek(io, offset, whence);
}
set_encoding(ext_enc) → self 点击切换源代码
set_encoding(ext_enc, int_enc, **enc_opts) → self
set_encoding('ext_enc:int_enc', **enc_opts) → self

参见 编码

如果给出了参数 ext_enc,它必须是 Encoding 对象或带有编码名称的 String;它被指定为流的编码。

如果给出了参数 int_enc,它必须是 Encoding 对象或带有编码名称的 String;它被指定为内部字符串的编码。

如果给出了参数 'ext_enc:int_enc',它是一个包含两个用冒号分隔的编码名称的字符串;相应的 Encoding 对象被指定为流的外部和内部编码。

如果字符串的外部编码是二进制/ASCII-8BIT,则字符串的内部编码被设置为 nil,因为不需要转码。

可选关键字参数 enc_opts 指定 编码选项

static VALUE
rb_io_set_encoding(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
    VALUE v1, v2, opt;

    if (!RB_TYPE_P(io, T_FILE)) {
        return forward(io, id_set_encoding, argc, argv);
    }

    argc = rb_scan_args(argc, argv, "11:", &v1, &v2, &opt);
    GetOpenFile(io, fptr);
    io_encoding_set(fptr, v1, v2, opt);
    return io;
}
set_encoding_by_bom → 编码或 nil 点击切换源代码

如果流以 BOM(字节顺序标记)开头,则使用 BOM 并相应设置外部编码;如果找到,则返回结果编码,否则返回 nil

File.write('t.tmp', "\u{FEFF}abc")
io = File.open('t.tmp', 'rb')
io.set_encoding_by_bom # => #<Encoding:UTF-8>
io.close

File.write('t.tmp', 'abc')
io = File.open('t.tmp', 'rb')
io.set_encoding_by_bom # => nil
io.close

如果流不是 binmode 或其编码已设置,则引发异常。

static VALUE
rb_io_set_encoding_by_bom(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    if (!(fptr->mode & FMODE_BINMODE)) {
        rb_raise(rb_eArgError, "ASCII incompatible encoding needs binmode");
    }
    if (fptr->encs.enc2) {
        rb_raise(rb_eArgError, "encoding conversion is set");
    }
    else if (fptr->encs.enc && fptr->encs.enc != rb_ascii8bit_encoding()) {
        rb_raise(rb_eArgError, "encoding is set to %s already",
                 rb_enc_name(fptr->encs.enc));
    }
    if (!io_set_encoding_by_bom(io)) return Qnil;
    return rb_enc_from_encoding(fptr->encs.enc);
}
stat → stat 点击切换源代码

返回类型为 File::Stat 的对象,作为 ios 的状态信息。

f = File.new("testfile")
s = f.stat
"%o" % s.mode   #=> "100644"
s.blksize       #=> 4096
s.atime         #=> Wed Apr 09 08:53:54 CDT 2003
static VALUE
rb_io_stat(VALUE obj)
{
    rb_io_t *fptr;
    struct stat st;

    GetOpenFile(obj, fptr);
    if (fstat(fptr->fd, &st) == -1) {
        rb_sys_fail_path(fptr->pathv);
    }
    return rb_stat_new(&st);
}
sync → true 或 false 点击切换源代码

返回流的当前同步模式。当同步模式为 true 时,所有输出都会立即刷新到底层操作系统,并且不会在 Ruby 内部进行缓冲。另请参见 fsync

f = File.open('t.tmp', 'w')
f.sync # => false
f.sync = true
f.sync # => true
f.close
static VALUE
rb_io_sync(VALUE io)
{
    rb_io_t *fptr;

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    return RBOOL(fptr->mode & FMODE_SYNC);
}
sync = 布尔值 → 布尔值 点击切换源代码

为流设置同步模式为给定值;返回给定值。

同步模式的值

  • true:所有输出立即刷新到底层操作系统,并且在内部未缓冲。

  • false:输出可能会在内部缓冲。

示例;

f = File.open('t.tmp', 'w')
f.sync # => false
f.sync = true
f.sync # => true
f.close

相关:IO#fsync

static VALUE
rb_io_set_sync(VALUE io, VALUE sync)
{
    rb_io_t *fptr;

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    if (RTEST(sync)) {
        fptr->mode |= FMODE_SYNC;
    }
    else {
        fptr->mode &= ~FMODE_SYNC;
    }
    return sync;
}
sysread(maxlen) → string 单击以切换源
sysread(maxlen, out_string) → string

行为类似于IO#readpartial,但它使用低级系统函数。

此方法不应与其他流读取器方法一起使用。

static VALUE
rb_io_sysread(int argc, VALUE *argv, VALUE io)
{
    VALUE len, str;
    rb_io_t *fptr;
    long n, ilen;
    struct io_internal_read_struct iis;
    int shrinkable;

    rb_scan_args(argc, argv, "11", &len, &str);
    ilen = NUM2LONG(len);

    shrinkable = io_setstrbuf(&str, ilen);
    if (ilen == 0) return str;

    GetOpenFile(io, fptr);
    rb_io_check_byte_readable(fptr);

    if (READ_DATA_BUFFERED(fptr)) {
        rb_raise(rb_eIOError, "sysread for buffered IO");
    }

    rb_io_check_closed(fptr);

    io_setstrbuf(&str, ilen);
    iis.th = rb_thread_current();
    iis.fptr = fptr;
    iis.nonblock = 0;
    iis.fd = fptr->fd;
    iis.buf = RSTRING_PTR(str);
    iis.capa = ilen;
    iis.timeout = NULL;
    n = io_read_memory_locktmp(str, &iis);

    if (n < 0) {
        rb_sys_fail_path(fptr->pathv);
    }

    io_set_read_length(str, n, shrinkable);

    if (n == 0 && ilen > 0) {
        rb_eof_error();
    }

    return str;
}
sysseek(offset, whence = IO::SEEK_SET) → integer 单击以切换源

行为类似于IO#seek,但它

  • 使用低级系统函数。

  • 返回新位置。

static VALUE
rb_io_sysseek(int argc, VALUE *argv, VALUE io)
{
    VALUE offset, ptrname;
    int whence = SEEK_SET;
    rb_io_t *fptr;
    rb_off_t pos;

    if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
        whence = interpret_seek_whence(ptrname);
    }
    pos = NUM2OFFT(offset);
    GetOpenFile(io, fptr);
    if ((fptr->mode & FMODE_READABLE) &&
        (READ_DATA_BUFFERED(fptr) || READ_CHAR_PENDING(fptr))) {
        rb_raise(rb_eIOError, "sysseek for buffered IO");
    }
    if ((fptr->mode & FMODE_WRITABLE) && fptr->wbuf.len) {
        rb_warn("sysseek for buffered IO");
    }
    errno = 0;
    pos = lseek(fptr->fd, pos, whence);
    if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);

    return OFFT2NUM(pos);
}
syswrite(object) → integer 单击以切换源

将给定的object写入到 self,该对象必须打开以进行写入(请参阅模式);返回写入的字节数。如果object不是字符串,则通过方法 to_s 进行转换

f = File.new('t.tmp', 'w')
f.syswrite('foo') # => 3
f.syswrite(30)    # => 2
f.syswrite(:foo)  # => 3
f.close

此方法不应与其他流写入器方法一起使用。

static VALUE
rb_io_syswrite(VALUE io, VALUE str)
{
    VALUE tmp;
    rb_io_t *fptr;
    long n, len;
    const char *ptr;

    if (!RB_TYPE_P(str, T_STRING))
        str = rb_obj_as_string(str);

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);

    if (fptr->wbuf.len) {
        rb_warn("syswrite for buffered IO");
    }

    tmp = rb_str_tmp_frozen_acquire(str);
    RSTRING_GETMEM(tmp, ptr, len);
    n = rb_io_write_memory(fptr, ptr, len);
    if (n < 0) rb_sys_fail_path(fptr->pathv);
    rb_str_tmp_frozen_release(str, tmp);

    return LONG2FIX(n);
}
tell → integer 单击以切换源

返回self中的当前位置(以字节为单位)(请参阅位置

f = File.open('t.txt')
f.tell # => 0
f.gets # => "First line\n"
f.tell # => 12
f.close

相关:IO#pos=IO#seek

static VALUE
rb_io_tell(VALUE io)
{
    rb_io_t *fptr;
    rb_off_t pos;

    GetOpenFile(io, fptr);
    pos = io_tell(fptr);
    if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);
    pos -= fptr->rbuf.len;
    return OFFT2NUM(pos);
}
别名也为:pos
timeout → duration or nil 单击以切换源

获取内部超时持续时间,如果未设置,则为 nil。

VALUE
rb_io_timeout(VALUE self)
{
    rb_io_t *fptr = rb_io_get_fptr(self);

    return fptr->timeout;
}
timeout = duration → duration 单击以切换源
timeout = nil → nil

将内部超时设置为指定的持续时间或 nil。超时适用于所有可能的阻塞操作。

当操作执行时间超过设置的超时时间时,将引发 IO::TimeoutError

这会影响以下方法(但不限于): getsputsreadwritewait_readablewait_writable。这也影响阻塞套接字操作,如 Socket#acceptSocket#connect

某些操作(如 File#openIO#close)不受超时影响。写入操作期间超时可能会使 IO 处于不一致状态,例如数据已部分写入。一般来说,超时是防止应用程序挂起在缓慢的 I/O 操作(如在 slowloris 攻击期间发生的那些操作)上的最后手段。

VALUE
rb_io_set_timeout(VALUE self, VALUE timeout)
{
    // Validate it:
    if (RTEST(timeout)) {
        rb_time_interval(timeout);
    }

    rb_io_t *fptr = rb_io_get_fptr(self);

    fptr->timeout = timeout;

    return self;
}
to_i
别名:fileno
to_io → self 单击以切换源

返回 self

static VALUE
rb_io_to_io(VALUE io)
{
    return io;
}
to_path
别名:path
tty?
别名:isatty
ungetbyte(integer) → nil 单击以切换源
ungetbyte(string) → nil

将给定数据推回到流的缓冲区上(“取消移位”),将数据放置在要读取的下一个位置;返回 nil。请参阅 字节 IO

请注意

  • 对于无缓冲读取(如 IO#sysread),调用该方法无效。

  • 对流调用 rewind 会丢弃已推回的数据。

当给定参数 integer 时,仅使用其低位字节

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetbyte(0x41)   # => nil
f.read              # => "A012"
f.rewind
f.ungetbyte(0x4243) # => nil
f.read              # => "C012"
f.close

当给定参数 string 时,使用所有字节

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetbyte('A')    # => nil
f.read              # => "A012"
f.rewind
f.ungetbyte('BCDE') # => nil
f.read              # => "BCDE012"
f.close
VALUE
rb_io_ungetbyte(VALUE io, VALUE b)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    rb_io_check_byte_readable(fptr);
    switch (TYPE(b)) {
      case T_NIL:
        return Qnil;
      case T_FIXNUM:
      case T_BIGNUM: ;
        VALUE v = rb_int_modulo(b, INT2FIX(256));
        unsigned char c = NUM2INT(v) & 0xFF;
        b = rb_str_new((const char *)&c, 1);
        break;
      default:
        SafeStringValue(b);
    }
    io_ungetbyte(b, fptr);
    return Qnil;
}
ungetc(integer) → nil 单击以切换源
ungetc(string) → nil

将给定数据推回到流的缓冲区中(“取消移位”),将数据放置在下一个要读取的位置;返回nil。请参见字符 IO

请注意

  • 对于无缓冲读取(如 IO#sysread),调用该方法无效。

  • 对流调用 rewind 会丢弃已推回的数据。

当给定参数integer时,将整数解释为字符

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetc(0x41)     # => nil
f.read             # => "A012"
f.rewind
f.ungetc(0x0442)   # => nil
f.getc.ord         # => 1090
f.close

当给定参数string时,使用所有字符

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetc('A')      # => nil
f.read      # => "A012"
f.rewind
f.ungetc("\u0442\u0435\u0441\u0442") # => nil
f.getc.ord      # => 1090
f.getc.ord      # => 1077
f.getc.ord      # => 1089
f.getc.ord      # => 1090
f.close
VALUE
rb_io_ungetc(VALUE io, VALUE c)
{
    rb_io_t *fptr;
    long len;

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);
    if (FIXNUM_P(c)) {
        c = rb_enc_uint_chr(FIX2UINT(c), io_read_encoding(fptr));
    }
    else if (RB_BIGNUM_TYPE_P(c)) {
        c = rb_enc_uint_chr(NUM2UINT(c), io_read_encoding(fptr));
    }
    else {
        SafeStringValue(c);
    }
    if (NEED_READCONV(fptr)) {
        SET_BINARY_MODE(fptr);
        len = RSTRING_LEN(c);
#if SIZEOF_LONG > SIZEOF_INT
        if (len > INT_MAX)
            rb_raise(rb_eIOError, "ungetc failed");
#endif
        make_readconv(fptr, (int)len);
        if (fptr->cbuf.capa - fptr->cbuf.len < len)
            rb_raise(rb_eIOError, "ungetc failed");
        if (fptr->cbuf.off < len) {
            MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.capa-fptr->cbuf.len,
                    fptr->cbuf.ptr+fptr->cbuf.off,
                    char, fptr->cbuf.len);
            fptr->cbuf.off = fptr->cbuf.capa-fptr->cbuf.len;
        }
        fptr->cbuf.off -= (int)len;
        fptr->cbuf.len += (int)len;
        MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.off, RSTRING_PTR(c), char, len);
    }
    else {
        NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
        io_ungetbyte(c, fptr);
    }
    return Qnil;
}
wait(events, timeout) → 事件掩码、false 或 nil 单击以切换源
wait(timeout = nil, mode = :read) → self、true 或 false

等到IO准备好进行指定事件,并返回已准备好的事件子集,或在超时时返回错误值。

事件可以是IO::READABLEIO::WRITABLEIO::PRIORITY的位掩码。

当有缓冲数据可用时,立即返回真值。

可选参数mode:read:write:read_write之一。

您必须需要“io/wait”才能使用此方法。

static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
#ifndef HAVE_RB_IO_WAIT
    rb_io_t *fptr;
    struct timeval timerec;
    struct timeval *tv = NULL;
    int event = 0;
    int i;

    GetOpenFile(io, fptr);
    for (i = 0; i < argc; ++i) {
        if (SYMBOL_P(argv[i])) {
            event |= wait_mode_sym(argv[i]);
        }
        else {
            *(tv = &timerec) = rb_time_interval(argv[i]);
        }
    }
    /* rb_time_interval() and might_mode() might convert the argument */
    rb_io_check_closed(fptr);
    if (!event) event = RB_WAITFD_IN;
    if ((event & RB_WAITFD_IN) && rb_io_read_pending(fptr))
        return Qtrue;
    if (wait_for_single_fd(fptr, event, tv))
        return io;
    return Qnil;
#else
    VALUE timeout = Qundef;
    rb_io_event_t events = 0;
    int i, return_io = 0;

    /* The documented signature for this method is actually incorrect.
     * A single timeout is allowed in any position, and multiple symbols can be given.
     * Whether this is intentional or not, I don't know, and as such I consider this to
     * be a legacy/slow path. */
    if (argc != 2 || (RB_SYMBOL_P(argv[0]) || RB_SYMBOL_P(argv[1]))) {
        /* We'd prefer to return the actual mask, but this form would return the io itself: */
        return_io = 1;

        /* Slow/messy path: */
        for (i = 0; i < argc; i += 1) {
            if (RB_SYMBOL_P(argv[i])) {
                events |= wait_mode_sym(argv[i]);
            }
            else if (timeout == Qundef) {
                rb_time_interval(timeout = argv[i]);
            }
            else {
                rb_raise(rb_eArgError, "timeout given more than once");
            }
        }

        if (timeout == Qundef) timeout = Qnil;

        if (events == 0) {
            events = RUBY_IO_READABLE;
        }
    }
    else /* argc == 2 and neither are symbols */ {
        /* This is the fast path: */
        events = io_event_from_value(argv[0]);
        timeout = argv[1];
    }

    if (events & RUBY_IO_READABLE) {
        rb_io_t *fptr = NULL;
        RB_IO_POINTER(io, fptr);

        if (rb_io_read_pending(fptr)) {
            /* This was the original behaviour: */
            if (return_io) return Qtrue;
            /* New behaviour always returns an event mask: */
            else return RB_INT2NUM(RUBY_IO_READABLE);
        }
    }

    return io_wait_event(io, events, timeout, return_io);
#endif
}
别名也为:wait
wait_priority → 真值或假值 单击以切换源
wait_priority(timeout) → 真值或假值

等到IO具有优先级,并返回真值,或在超时时返回假值。优先级数据使用 Socket::MSG_OOB 标志发送和接收,通常仅限于流。

您必须需要“io/wait”才能使用此方法。

static VALUE
io_wait_priority(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr = NULL;

    RB_IO_POINTER(io, fptr);
    rb_io_check_readable(fptr);

    if (rb_io_read_pending(fptr)) return Qtrue;

    rb_check_arity(argc, 0, 1);
    VALUE timeout = argc == 1 ? argv[0] : Qnil;

    return io_wait_event(io, RUBY_IO_PRIORITY, timeout, 1);
}
别名也为:wait_priority
wait_readable → 真值或假值 单击以切换源
wait_readable(timeout) → 真值或假值

等到IO可读,并返回真值,或在超时时返回假值。当有缓冲数据可用时,立即返回真值。

您必须需要“io/wait”才能使用此方法。

static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
    struct timeval timerec;
    struct timeval *tv;
#endif

    GetOpenFile(io, fptr);
    rb_io_check_readable(fptr);

#ifndef HAVE_RB_IO_WAIT
    tv = get_timeout(argc, argv, &timerec);
#endif
    if (rb_io_read_pending(fptr)) return Qtrue;

#ifndef HAVE_RB_IO_WAIT
    if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) {
        return io;
    }
    return Qnil;
#else
    rb_check_arity(argc, 0, 1);
    VALUE timeout = (argc == 1 ? argv[0] : Qnil);

    return io_wait_event(io, RUBY_IO_READABLE, timeout, 1);
#endif
}
别名也为:wait_readable
wait_writable → 真值或假值 单击以切换源
wait_writable(timeout) → 真值或假值

等到IO可写,并返回真值,或在超时时返回假值。

您必须需要“io/wait”才能使用此方法。

static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
    struct timeval timerec;
    struct timeval *tv;
#endif

    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);

#ifndef HAVE_RB_IO_WAIT
    tv = get_timeout(argc, argv, &timerec);
    if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) {
        return io;
    }
    return Qnil;
#else
    rb_check_arity(argc, 0, 1);
    VALUE timeout = (argc == 1 ? argv[0] : Qnil);

    return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1);
#endif
}
别名也为:wait_writable
winsize → [行,列] 单击以切换源

返回控制台大小。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_winsize(VALUE io)
{
    rb_console_size_t ws;
    int fd = GetWriteFD(io);
    if (!getwinsize(fd, &ws)) sys_fail(io);
    return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
}
winsize = [行,列] 单击以切换源

尝试设置控制台大小。效果取决于平台和运行环境。

必须要求 ‘io/console’ 才能使用此方法。

static VALUE
console_set_winsize(VALUE io, VALUE size)
{
    rb_console_size_t ws;
#if defined _WIN32
    HANDLE wh;
    int newrow, newcol;
    BOOL ret;
#endif
    VALUE row, col, xpixel, ypixel;
    const VALUE *sz;
    long sizelen;
    int fd;

    size = rb_Array(size);
    if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
        rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen);
    }
    sz = RARRAY_CONST_PTR(size);
    row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
    if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
    fd = GetWriteFD(io);
#if defined TIOCSWINSZ
    ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
#define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
    SET(row);
    SET(col);
    SET(xpixel);
    SET(ypixel);
#undef SET
    if (!setwinsize(fd, &ws)) sys_fail(io);
#elif defined _WIN32
    wh = (HANDLE)rb_w32_get_osfhandle(fd);
#define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
    SET(row);
    SET(col);
#undef SET
    if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel);
    if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel);
    if (!GetConsoleScreenBufferInfo(wh, &ws)) {
        rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
    }
    ws.dwSize.X = newcol;
    ret = SetConsoleScreenBufferSize(wh, ws.dwSize);
    ws.srWindow.Left = 0;
    ws.srWindow.Top = 0;
    ws.srWindow.Right = newcol-1;
    ws.srWindow.Bottom = newrow-1;
    if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
        rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
    }
    /* retry when shrinking buffer after shrunk window */
    if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) {
        rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
    }
    /* remove scrollbar if possible */
    if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
        rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
    }
#endif
    return io;
}
write(*objects) → integer 单击以切换源

将给定的每个objects写入self,它必须打开用于写入(参见访问模式);返回写入的总字节数;每个不是字符串的objects都通过方法to_s转换

$stdout.write('Hello', ', ', 'World!', "\n") # => 14
$stdout.write('foo', :bar, 2, "\n")          # => 8

输出

Hello, World!
foobar2

相关:IO#read

static VALUE
io_write_m(int argc, VALUE *argv, VALUE io)
{
    if (argc != 1) {
        return io_writev(argc, argv, io);
    }
    else {
        VALUE str = argv[0];
        return io_write(io, str, 0);
    }
}
write_nonblock(string) → integer 单击以切换源
write_nonblock(string [, options]) → integer

在为底层文件描述符设置 O_NONBLOCK 后,使用 write(2) 系统调用将给定的字符串写入ios

它返回写入的字节数。

write_nonblock 只调用 write(2) 系统调用。它导致 write(2) 系统调用导致的所有错误:Errno::EWOULDBLOCK、Errno::EINTR 等。结果也可能小于 string.length(部分写入)。调用者应注意此类错误和部分写入。

如果异常是 Errno::EWOULDBLOCK 或 Errno::EAGAIN,则它由IO::WaitWritable扩展。因此,IO::WaitWritable可用于救援异常以重试 write_nonblock。

# Creates a pipe.
r, w = IO.pipe

# write_nonblock writes only 65536 bytes and return 65536.
# (The pipe size is 65536 bytes on this environment.)
s = "a" * 100000
p w.write_nonblock(s)     #=> 65536

# write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
p w.write_nonblock("b")   # Resource temporarily unavailable (Errno::EAGAIN)

如果写入缓冲区不为空,则首先刷新它。

write_nonblock引发IO::WaitWritable类型的异常时,write_nonblock不应在 io 可写之前调用,以避免繁忙循环。这可以按如下方式完成。

begin
  result = io.write_nonblock(string)
rescue IO::WaitWritable, Errno::EINTR
  IO.select(nil, [io])
  retry
end

请注意,这并不能保证写入字符串中的所有数据。写入的长度报告为结果,应稍后检查。

在某些平台(例如 Windows)上,根据IO对象的类型,不支持write_nonblock。在这种情况下,write_nonblock引发Errno::EBADF

通过将关键字参数exception指定为false,你可以指示write_nonblock不应引发IO::WaitWritable异常,而是返回符号:wait_writable

# File io.rb, line 120
def write_nonblock(buf, exception: true)
  Primitive.io_write_nonblock(buf, exception)
end