class IO
IO 类的一个实例(通常称为*流*)代表底层操作系统中的一个输入/输出流。IO 类是 Ruby 中输入和输出的基础。
类 File 是 Ruby 核心中唯一一个 IO 的子类。Ruby 标准库中的一些类也是 IO 的子类;这些类包括 TCPSocket 和 UDPSocket。
全局常量 ARGF(也可通过 $< 访问)提供了一个类似 IO 的流,允许访问 ARGV 中找到的所有文件路径(或者如果 ARGV 为空,则访问 STDIN)。ARGF 本身并不是 IO 的子类。
类 StringIO 提供了一个类似 IO 的流,它处理一个 String。StringIO 本身并不是 IO 的子类。
基于 IO 的重要对象包括
-
$stdin。
-
$stdout。
-
$stderr。
-
类
File的实例。
可以使用以下方法创建 IO 类的实例
-
IO.new:为给定的整数文件描述符返回一个新的 IO 对象。 -
IO.open:将一个新的 IO 对象传递给给定的块。 -
IO.popen:返回一个与新启动的子进程的 $stdin 和 $stdout 相连的新 IO 对象。 -
Kernel#open:返回一个与给定源(流、文件或子进程)相连的新 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 方法接受可选的关键字参数,这些参数决定了新流的打开方式
-
:mode:流模式。 -
:flags:Integer文件打开标志;如果也给出了mode,则两者按位或。 -
:external_encoding:流的外部编码。 -
:internal_encoding:流的内部编码。'-'是默认内部编码的同义词。如果值为nil,则不进行转换。 -
:encoding:将外部和内部编码指定为'extern:intern'。 -
:textmode:如果值为真,则指定模式为纯文本,否则为二进制。 -
:binmode:如果值为真,则指定模式为二进制,否则为纯文本。 -
:autoclose:如果值为真,则指定当流关闭时fd会关闭;否则它将保持打开状态。
还可以使用 String#encode 中提供的选项,这些选项可以控制外部和内部编码之间的转换。
基本 IO
您可以使用这些方法执行基本流 IO,这些方法通常在多字节字符串上操作
位置
IO 流有一个非负整数*位置*,这是下一次读写将发生的字节偏移量。新流的位置为零(行号也为零);rewind 方法将位置(以及行号)重置为零。
这些方法将丢弃*缓冲区*和用于该 IO 的 Encoding::Converter 实例。
相关方法
-
IO#pos=:将流的位置设置为给定的整数new_position(以字节为单位)。 -
IO#seek:将流的位置设置为给定的整数offset(以字节为单位),相对于给定的位置whence(表示开始、结束或当前位置)。 -
IO#rewind:将流定位到开头(同时重置行号)。
打开和关闭的流
新的 IO 流可以为读取打开、为写入打开或两者都打开。
当流被垃圾回收器回收时,它会被自动关闭。
尝试在已关闭的流上读取或写入会引发异常。
相关方法
-
IO#close:关闭流的读写。 -
IO#close_read:关闭流的读取。 -
IO#close_write:关闭流的写入。 -
IO#closed?:返回流是否已关闭。
流末尾
您可以查询流是否位于其末尾
您可以使用方法 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 类支持面向行的*输入*和*输出*
行输入
IO 类支持*文件*的面向行的输入和*IO 流*的面向行的输入
文件行输入
您可以使用以下方法从文件中读取行
-
IO.foreach:读取每一行并将其传递给给定的块。 -
IO.readlines:读取所有行并以数组形式返回。
对于这些方法中的每一种
流行输入
您可以使用以下方法从 IO 流中读取行
-
IO#each_line:读取每一行剩余内容,并将其传递给给定的块。 -
IO#gets:返回下一行。 -
IO#readline:与gets类似,但在流末尾会引发异常。 -
IO#readlines:将所有剩余行以数组形式返回。
对于这些方法中的每一种
行分隔符
每个*行输入方法*都使用一个*行分隔符*:决定什么被视为一行的字符串;它有时被称为*输入记录分隔符*。
默认的行分隔符取自全局变量 $/,其初始值为 "\n"。
通常,下一行读取的内容是从当前*位置*到下一个行分隔符的所有数据(但参见 特殊行分隔符值)。
f = File.new('t.txt') # Method gets with no sep argument returns the next line, according to $/. 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
您可以通过传递参数 sep 来使用不同的行分隔符
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
或通过设置全局变量 $/
f = File.new('t.txt') $/ = 'l' f.gets # => "First l" f.gets # => "ine\nSecond l" f.gets # => "ine\n\nFourth l" f.close
特殊行分隔符值
*行输入方法*中的每一种都接受 sep 参数的两个特殊值
-
nil:整个流将被读取(“吞噬”)到一个字符串中。f = File.new('t.txt') f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.close
-
''(空字符串):将读取下一个“段落”(段落由两个连续的行分隔符分隔)。f = File.new('t.txt') f.gets('') # => "First line\nSecond line\n\n" f.gets('') # => "Fourth line\nFifth line\n" f.close
行限制
*行输入方法*中的每一种都使用一个整数*行限制*,它限制了可以返回的字节数。(多字节字符不会被分割,因此返回的行可能比限制稍长)。
默认限制值为 -1;任何负的限制值意味着没有限制。
如果没有限制,行仅由 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
行分隔符和行限制
给定 sep 和 limit 参数时,组合了这两种行为
-
根据行分隔符
sep返回下一行。 -
但返回的字节数不超过
limit允许的字节数。
示例
File.open('t.txt') {|f| f.gets('li', 20) } # => "First li" File.open('t.txt') {|f| f.gets('li', 2) } # => "Fi"
行号
可读的 IO 流有一个非负整数*行号*
-
IO#lineno:返回行号。 -
IO#lineno=:重置并返回行号。
除非通过调用方法 IO#lineno= 修改,否则行号是根据有效的*行分隔符*由某些面向行的方**法读取的行数。
-
IO.foreach:在每次调用块时增加行号。 -
IO#each_line:在每次调用块时增加行号。 -
IO#gets:增加行号。 -
IO#readline:增加行号。 -
IO#readlines:对于读取的每一行,增加行号。
新流最初的行号为零(位置也为零);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
与行号相关的是全局变量 $.。
-
当一个流被打开时,
$.不会被设置;它的值保留自进程中之前的活动。$. = 41 f = File.new('t.txt') $. = 41 # => 41 f.close
-
当一个流被读取时,
$.被设置为该流的行号。f0 = File.new('t.txt') f1 = File.new('t.dat') f0.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f1.readlines # => ["\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"] $. # => 1 f0.close f1.close
-
方法
IO#rewind和IO#seek不会影响$.。f = File.new('t.txt') f.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f.rewind f.seek(0, :SET) $. # => 5 f.close
行输出
您可以使用以下方法逐行写入 IO 流
-
IO#puts:将对象写入流。
字符 IO
您可以使用这些方法逐个字符地处理 IO 流
-
IO#getc:从流中读取并返回下一个字符。 -
IO#readchar:与getc类似,但在流末尾会引发异常。 -
IO#ungetc:将字符或整数推回到流中(“取消压入”)。 -
IO#putc:将一个字符写入流。 -
IO#each_char:读取流中剩余的每个字符,并将字符传递给给定的块。
字节 IO
您可以使用这些方法逐个字节地处理 IO 流
-
IO#getbyte:返回下一个 8 位字节,作为一个 0..255 范围内的整数。 -
IO#readbyte:与getbyte类似,但在流末尾会引发异常。 -
IO#ungetbyte:将字节推回到流中(“取消压入”)。 -
IO#each_byte:读取流中剩余的每个字节,并将字节作为整数传递给给定的块。
代码点 IO
您可以逐个代码点地处理 IO 流
-
IO#each_codepoint:读取每个代码点,并将其作为整数传递给给定的块。
这里有什么
首先,其他部分。类 IO
-
继承自 Object 类。
-
包含 Enumerable 模块,该模块提供了数十种额外的方法。
在此,IO 类提供了对以下方面有用的方法
创建
-
::open:创建新的 IO 对象。 -
::pipe:创建一对连接的读写 IO 对象。 -
::popen:创建 IO 对象以与子进程进行交互。 -
::select:选择哪些给定的 IO 实例已准备好读取、写入或有待处理的异常。
读取
-
::binread:返回一个二进制字符串,其中包含给定文件的全部或部分字节。 -
::read:返回一个字符串,其中包含给定文件的全部或部分字节。 -
::readlines:返回一个字符串数组,这些字符串是给定文件的行。 -
getbyte:返回从self读取的下一个 8 位字节,作为一个整数。 -
getc:返回从self读取的下一个字符,作为一个字符串。 -
gets:返回从self读取的行。 -
pread:从self返回全部或下一个 n 字节,不更新接收者的偏移量。 -
read:返回self中剩余的全部或给定 n 字节。 -
read_nonblock:在非阻塞模式下,从self读取给定 n 字节。 -
readline:返回从self读取的下一行;与 getline 相同,但在流末尾会引发异常。 -
readlines:返回一个从self读取的所有行的数组。 -
readpartial:从self返回最多给定的字节数。
写入
-
::binwrite:以二进制模式将给定的字符串写入指定文件路径的文件。 -
::write:将给定的字符串写入self。 -
<<:将给定的字符串追加到self。 -
print:将最后读取的行或给定的对象打印到self。 -
printf:根据给定的格式字符串和对象写入self。 -
putc:将一个字符写入self。 -
puts:将行写入self,确保行以换行符结尾。 -
pwrite:在指定的偏移量写入给定的字符串,不更新接收者的偏移量。 -
write:将一个或多个给定的字符串写入self。 -
write_nonblock:以非阻塞模式将一个或多个给定的字符串写入self。
定位
-
lineno:返回self中的当前行号。 -
lineno=:设置self中的行号。 -
pos=:设置self中的字节偏移量。 -
reopen:将self重新关联到新的或现有的 IO 流。 -
rewind:将self定位到输入开头。 -
seek:相对于给定位置设置self的偏移量。
迭代
-
::foreach:将给定文件的每一行交给块处理。 -
each_byte:将self中的每一个字节依次作为整数传递给给定的块。 -
each_char:将self中的每一个字符依次作为字符串传递给给定的块。 -
each_codepoint:将self中的每一个代码点依次作为整数传递给给定的块。
设置
-
autoclose=:设置self是否自动关闭。 -
binmode:将self设置为二进制模式。 -
close:关闭self。 -
close_on_exec=:设置 close-on-exec 标志。 -
close_read:关闭self的读取。 -
close_write:关闭self的写入。 -
set_encoding:设置self的编码。 -
set_encoding_by_bom:根据self的 Unicode 字节顺序标记设置其编码。 -
sync=:将同步模式设置为给定值。
查询
-
autoclose?:返回self是否自动关闭。 -
binmode?:返回self是否处于二进制模式。 -
close_on_exec?:返回self的 close-on-exec 标志。 -
closed?:返回self是否已关闭。 -
external_encoding:返回self的外部编码对象。 -
internal_encoding:返回self的内部编码对象。 -
stat:返回包含self状态信息的File::Stat对象。 -
sync:返回self是否处于同步模式。
Buffering
-
fdatasync:立即将self中的所有缓冲数据写入磁盘。 -
flush:将self中的任何缓冲数据刷新到底层操作系统。 -
fsync:立即将self中的所有缓冲数据和属性写入磁盘。 -
ungetbyte:在self的缓冲区前面加上给定的整数字节或字符串。 -
ungetc:在self的缓冲区前面加上给定的字符串。
低级访问
-
::sysopen:打开其路径为给定文件的文件,返回整数文件描述符。 -
advise:宣布意图以特定方式访问self中的数据。 -
fcntl:将一个低级命令传递给由给定文件描述符指定的文件。 -
ioctl:将一个低级命令传递给由给定文件描述符指定的目标设备。 -
sysread:使用低级读取从 self 读取最多 n 字节。 -
sysseek:设置self的偏移量。 -
syswrite:使用低级写入将给定的字符串写入self。
其他
-
::copy_stream:将数据从源复制到目标,源和目标都可以是文件路径或类 IO 的对象。 -
::try_convert:返回通过转换给定对象生成的新 IO 对象。 -
inspect:返回self的字符串表示。
Constants
Public Class Methods
Source
static VALUE
rb_io_s_binread(int argc, VALUE *argv, VALUE io)
{
VALUE offset;
struct foreach_arg arg;
enum rb_io_mode fmode = FMODE_READABLE|FMODE_BINMODE;
enum {
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);
}
Source
static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 1);
}
Source
static VALUE
console_dev(int argc, VALUE *argv, VALUE klass)
{
VALUE con = 0;
VALUE sym = 0;
if (argc) {
Check_Type(sym = argv[0], T_SYMBOL);
}
// Force the class to be File.
if (klass == rb_cIO) klass = rb_cFile;
if (console_dev_get(klass, &con)) {
if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) {
console_dev_remove(klass);
con = 0;
}
}
if (sym) {
if (sym == ID2SYM(id_close) && argc == 1) {
if (con) {
rb_io_close(con);
console_dev_remove(klass);
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;
#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
console_dev_set(klass, con);
}
if (sym) {
return rb_f_send(argc, argv, con);
}
return con;
}
Source
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);
}
将数据从给定的 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
如果只给出 src 和 dst 参数,则复制整个源流
# 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
With argument src_length a non-negative integer, no more than that many bytes are copied
IO.copy_stream('t.txt', 't.tmp', 10) # => 10 File.read('t.tmp') # => "First line"
With argument src_offset also given, the source stream is read beginning at that offset
IO.copy_stream('t.txt', 't.tmp', 11, 11) # => 11 IO.read('t.tmp') # => "Second line"
Source
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;
}
Synonym for IO.new.
Source
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);
}
Calls the block with each successive line read from the stream.
当从 IO 类(而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则存在潜在的安全漏洞;参见 命令注入。
The first argument must be a string that is the path to a file.
With only argument path given, parses lines from the file at the given path, as determined by the default line separator, and calls the block with each successive line
File.foreach('t.txt') {|line| p line }
Output: the same as above.
For both forms, command and path, the remaining arguments are the same.
With argument sep given, parses lines as determined by that line separator (see Line Separator)
File.foreach('t.txt', 'li') {|line| p line }
输出
"First li" "ne\nSecond li" "ne\n\nThird li" "ne\nFourth li" "ne\n"
Each paragraph
File.foreach('t.txt', '') {|paragraph| p paragraph }
输出
"First line\nSecond line\n\n" "Third line\nFourth line\n"
With argument limit given, parses lines as determined by the default line separator and the given line-length limit (see Line Separator and Line 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"
With arguments sep and limit given, combines the two behaviors (see Line Separator and Line Limit).
Optional keyword arguments opts specify
Returns an Enumerator if no block is given.
Source
static VALUE
rb_io_initialize(int argc, VALUE *argv, VALUE io)
{
VALUE fnum, vmode;
VALUE opt;
rb_scan_args(argc, argv, "11:", &fnum, &vmode, &opt);
return io_initialize(io, fnum, vmode, opt);
}
Creates and returns a new IO object (file stream) from a file descriptor.
IO.new may be useful for interaction with low-level libraries. For higher-level interactions, it may be simpler to create the file stream using File.open.
Argument fd must be a valid file descriptor (integer)
path = 't.tmp' fd = IO.sysopen(path) # => 3 IO.new(fd) # => #<IO:fd 3>
The new IO object does not inherit encoding (because the integer file descriptor does not have an encoding)
fd = IO.sysopen('t.rus', 'rb') io = IO.new(fd) io.external_encoding # => #<Encoding:UTF-8> # Not ASCII-8BIT.
Optional argument mode (defaults to ‘r’) must specify a valid mode; see Access Modes
IO.new(fd, 'w') # => #<IO:fd 3> IO.new(fd, File::WRONLY) # => #<IO:fd 3>
Optional keyword arguments opts specify
示例
IO.new(fd, internal_encoding: nil) # => #<IO:fd 3> IO.new(fd, autoclose: true) # => #<IO:fd 3>
Source
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;
}
Creates a new IO object, via IO.new with the given arguments.
With no block given, returns the IO object.
With a block given, calls the block with the IO object and returns the block’s value.
Source
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;
enum rb_io_mode 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;
}
Creates a pair of pipe endpoints, read_io and write_io, connected to each other.
If argument enc_string is given, it must be a string containing one of
-
The name of the encoding to be used as the external encoding.
-
The colon-separated names of two encodings to be used as the external and internal encodings.
If argument int_enc is given, it must be an Encoding object or encoding name string that specifies the internal encoding to be used; if argument ext_enc is also given, it must be an Encoding object or encoding name string that specifies the external encoding to be used.
The string read from read_io is tagged with the external encoding; if an internal encoding is also specified, the string is converted to, and tagged with, that encoding.
If any encoding is specified, optional hash arguments specify the conversion option.
Optional keyword arguments opts specify
With no block given, returns the two endpoints in an array
IO.pipe # => [#<IO:fd 4>, #<IO:fd 5>]
With a block given, calls the block with the two endpoints; closes both endpoints and returns the value of the block
IO.pipe {|read_io, write_io| p read_io; p write_io }
输出
#<IO:fd 6> #<IO:fd 7>
Not available on all platforms.
In the example below, the two processes close the ends of the pipe that they are not using. This is not just a cosmetic nicety. The read end of a pipe will not generate an end of file condition if there are any writers with the pipe still open. In the case of the parent process, the rd.read will never return if it does not first issue a wr.close
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>
Source
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);
}
Executes the given command cmd as a subprocess whose $stdin and $stdout are connected to a new stream io.
This method has potential security vulnerabilities if called with untrusted input; see Command Injection.
If no block is given, returns the new stream, which depending on given mode may be open for reading, writing, or both. The stream should be explicitly closed (eventually) to avoid resource leaks.
If a block is given, the stream is passed to the block (again, open for reading, writing, or both); when the block exits, the stream is closed, the block’s value is returned, and the global variable $? is set to the child’s exit status.
Optional argument mode may be any valid IO mode. See Access Modes.
Required argument cmd determines which of the following occurs
-
The process forks.
-
A specified program runs in a shell.
-
A specified program runs with specified arguments.
-
A specified program runs with specified arguments and a specified
argv0.
Each of these is detailed below.
The optional hash argument env specifies name/value pairs that are to be added to the environment variables for the subprocess
IO.popen({'FOO' => 'bar'}, 'ruby', 'r+') do |pipe| pipe.puts 'puts ENV["FOO"]' pipe.close_write pipe.gets end => "bar\n"
Optional keyword arguments opts specify
-
Options for
Kernel#spawn.
Forked Process
When argument cmd is the 1-character string '-', causes the process to fork
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
Note that this is not supported on all platforms.
Shell Subprocess
When argument cmd is a single string (but not '-'), the program named cmd is run as a shell command
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
Program Subprocess
When argument cmd is an array of strings, the program named cmd[0] is run with all elements of cmd as its arguments
IO.popen(['du', '..', '.']) do |pipe| $stderr.puts pipe.readlines.size end
输出
1111
Program Subprocess with argv0
When argument cmd is an array whose first element is a 2-element string array and whose remaining elements (if any) are strings
-
cmd[0][0](the first string in the nested array) is the name of a program that is run. -
cmd[0][1](the second string in the nested array) is set as the program’sargv[0]. -
cmd[1..-1](the strings in the outer array) are the program’s arguments.
Example (sets $0 to ‘foo’)
IO.popen([['/bin/sh', 'foo'], '-c', 'echo $0']).read # => "foo\n"
Some Special Examples
# 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 }
Output (from last section)
["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;
Raises exceptions that IO.pipe and Kernel.spawn raise.
Source
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);
}
Opens the stream, reads and returns some or all of its content, and closes the stream; returns nil if no bytes were read.
当从 IO 类(而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则存在潜在的安全漏洞;参见 命令注入。
The first argument must be a string that is the path to a file.
With only argument path given, reads in text mode and returns the entire content of the file at the given path
IO.read('t.txt') # => "First line\nSecond line\n\nThird line\nFourth line\n"
On Windows, text mode can terminate reading and leave bytes in the file unread when encountering certain special bytes. Consider using IO.binread if all bytes in the file should be read.
With argument length, returns length bytes if available
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"
With arguments length and offset, returns length bytes if available, beginning at the given offset
IO.read('t.txt', 10, 2) # => "rst line\nS" IO.read('t.txt', 10, 200) # => nil
Optional keyword arguments opts specify
Source
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);
}
Returns an array of all lines read from the stream.
当从 IO 类(而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则存在潜在的安全漏洞;参见 命令注入。
The first argument must be a string that is the path to a file.
With only argument path given, parses lines from the file at the given path, as determined by the default line separator, and returns those lines in an array
IO.readlines('t.txt') # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
With argument sep given, parses lines as determined by that line separator (see Line Separator)
# 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"]
With argument limit given, parses lines as determined by the default line separator and the given line-length limit (see Line Separator and Line Limit
IO.readlines('t.txt', 7) # => ["First l", "ine\n", "Second ", "line\n", "\n", "Third l", "ine\n", "Fourth ", "line\n"]
With arguments sep and limit given, combines the two behaviors (see Line Separator and Line Limit).
Optional keyword arguments opts specify
Source
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) || is_pos_inf(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);
}
Invokes system call select(2), which monitors multiple file descriptors, waiting until one or more of the file descriptors becomes ready for some class of I/O operation.
Not implemented on all platforms.
Each of the arguments read_ios, write_ios, and error_ios is an array of IO objects.
Argument timeout is a numeric value (such as integer or float) timeout interval in seconds. timeout can also be nil or Float::INFINITY. nil and Float::INFINITY means no timeout.
The method monitors the IO objects given in all three arrays, waiting for some to be ready; returns a 3-element array whose elements are
-
An array of the objects in
read_iosthat are ready for reading. -
An array of the objects in
write_iosthat are ready for writing. -
An array of the objects in
error_ioshave pending exceptions.
If no object becomes ready within the given timeout, nil is returned.
IO.select peeks the buffer of IO objects for testing readability. If the IO buffer is not empty, IO.select immediately notifies readability. This “peek” only happens for IO objects. It does not happen for IO-like objects such as OpenSSL::SSL::SSLSocket.
The best way to use IO.select is invoking it after non-blocking methods such as read_nonblock, write_nonblock, etc. The methods raise an exception which is extended by IO::WaitReadable or IO::WaitWritable. The modules notify how the caller should wait with IO.select. If IO::WaitReadable is raised, the caller should wait for reading. If IO::WaitWritable is raised, the caller should wait for writing.
So, blocking read (readpartial) can be emulated using read_nonblock and IO.select as follows
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
Especially, the combination of non-blocking methods and IO.select is preferred for IO like objects such as OpenSSL::SSL::SSLSocket. It has to_io method to return underlying IO object. IO.select calls to_io to obtain the file descriptor to wait.
This means that readability notified by IO.select doesn’t mean readability from OpenSSL::SSL::SSLSocket object.
The most likely situation is that OpenSSL::SSL::SSLSocket buffers some data. IO.select doesn’t see the buffer. So IO.select can block when OpenSSL::SSL::SSLSocket#readpartial doesn’t block.
However, several more complicated situations exist.
SSL is a protocol which is sequence of records. The record consists of multiple bytes. So, the remote side of SSL sends a partial record, IO.select notifies readability but OpenSSL::SSL::SSLSocket cannot decrypt a byte and OpenSSL::SSL::SSLSocket#readpartial will block.
Also, the remote side can request SSL renegotiation which forces the local SSL engine to write some data. This means OpenSSL::SSL::SSLSocket#readpartial may invoke write system call and it can block. In such a situation, OpenSSL::SSL::SSLSocket#read_nonblock raises IO::WaitWritable instead of blocking. So, the caller should wait for ready for writability as above example.
The combination of non-blocking methods and IO.select is also useful for streams such as tty, pipe socket socket when multiple processes read from a stream.
Finally, Linux kernel developers don’t guarantee that readability of select(2) means readability of following read(2) even for a single process; see select(2)
Invoking IO.select before IO#readpartial works well as usual. However it is not the best way to use IO.select.
The writability notified by select(2) doesn’t show how many bytes are writable. IO#write method blocks until given whole string is written. So, IO#write(two or more bytes) can block after writability is notified by IO.select. IO#write_nonblock is required to avoid the blocking.
Blocking write (write) can be emulated using write_nonblock and IO.select as follows: IO::WaitReadable should also be rescued for SSL renegotiation in OpenSSL::SSL::SSLSocket.
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
Source
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 {
StringValue(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);
}
Opens the file at the given path with the given mode and permissions; returns the integer file descriptor.
If the file is to be readable, it must exist; if the file is to be writable and does not exist, it is created with the given permissions
File.write('t.tmp', '') # => 0 IO.sysopen('t.tmp') # => 8 IO.sysopen('t.tmp', 'w') # => 9
Source
static VALUE
rb_io_s_try_convert(VALUE dummy, VALUE io)
{
return rb_io_check_io(io);
}
Attempts to convert object into an IO object via method to_io; returns the new IO object if successful, or nil otherwise
IO.try_convert(STDOUT) # => #<IO:<STDOUT>> IO.try_convert(ARGF) # => #<IO:<STDIN>> IO.try_convert('STDOUT') # => nil
Source
static VALUE
rb_io_s_write(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 0);
}
Opens the stream, writes the given data to it, and closes the stream; returns the number of bytes written.
当从 IO 类(而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则存在潜在的安全漏洞;参见 命令注入。
The first argument must be a string that is the path to a file.
With only argument path given, writes the given data to the file at that path
IO.write('t.tmp', 'abc') # => 3 File.read('t.tmp') # => "abc"
If offset is zero (the default), the file is overwritten
IO.write('t.tmp', 'A') # => 1 File.read('t.tmp') # => "A"
If offset in within the file content, the file is partly overwritten
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"
If offset is outside the file content, the file is padded with null characters "\u0000"
IO.write('t.tmp', 'xyz', 10) # => 3 File.read('t.tmp') # => "ab012f\u0000\u0000\u0000\u0000xyz"
Optional keyword arguments opts specify
Public Instance Methods
Source
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
rb_io_write(io, str);
return io;
}
Writes the given object to self, which must be opened for writing (see Access Modes); returns self; if object is not a string, it is converted via method to_s
$stdout << 'Hello' << ', ' << 'World!' << "\n" $stdout << 'foo' << :bar << 2 << "\n"
输出
Hello, World! foobar2
Source
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
}
Invokes Posix system call posix_fadvise(2), which announces an intention to access data from the current file in a particular manner.
The arguments and results are platform-dependent.
The relevant data is specified by
-
offset: The offset of the first byte of data. -
len: The number of bytes to be accessed; iflenis zero, or is larger than the number of bytes remaining, all remaining bytes will be accessed.
Argument advice is one of the following symbols
-
:normal: The application has no advice to give about its access pattern for the specified data. If no advice is given for an open file, this is the default assumption. -
:sequential: The application expects to access the specified data sequentially (with lower offsets read before higher ones). -
:random: The specified data will be accessed in random order. -
:noreuse: The specified data will be accessed only once. -
:willneed: The specified data will be accessed in the near future. -
:dontneed: The specified data will not be accessed in the near future.
Not implemented on all platforms.
Source
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;
}
Sets auto-close flag.
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
Source
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));
}
Returns true if the underlying file descriptor of ios will be closed at its finalization or at calling close, otherwise false.
Source
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;
}
Beeps on the output console.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
Sets the stream’s data mode as binary (see Data Mode).
A stream’s data mode may not be changed from binary to text.
Source
static VALUE
rb_io_binmode_p(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return RBOOL(fptr->mode & FMODE_BINMODE);
}
Returns true if the stream is on binary mode, false otherwise. See Data Mode.
Source
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;
}
Yields while console input events are queued.
This method is Windows only.
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_clear_screen(VALUE io)
{
console_erase_screen(io, INT2FIX(2));
console_goto(io, INT2FIX(0), INT2FIX(0));
return io;
}
Clears the entire screen and moves the cursor top-left corner.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
Closes the stream for both reading and writing if open for either or both; returns nil. See Open and Closed Streams.
If the stream is open for writing, flushes any buffered writes to the operating system before closing.
If the stream was opened by IO.popen, sets global variable $? (child exit status).
It is not an error to close an IO object that has already been closed. It just returns nil.
示例
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close puts $? puts pipe.closed? end
输出
false pid 13760 exit 0 true
Related: IO#close_read, IO#close_write, IO#closed?.
Source
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;
}
Sets a close-on-exec flag.
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 sets close-on-exec flags of all file descriptors by default since Ruby 2.0.0. So you don’t need to set by yourself. Also, unsetting a close-on-exec flag can cause file descriptor leak if another thread use fork() and exec() (via system() method for example). If you really needs file descriptor inheritance to child process, use spawn()‘s argument such as fd=>fd.
Source
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;
}
Returns true if the stream will be closed on exec, false otherwise
f = File.open('t.txt') f.close_on_exec? # => true f.close_on_exec = false f.close_on_exec? # => false f.close
Source
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);
}
Closes the stream for reading if open for reading; returns nil. See Open and Closed Streams.
If the stream was opened by IO.popen and is also closed for writing, sets global variable $? (child exit status).
示例
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
Related: IO#close, IO#close_write, IO#closed?.
Source
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;
}
Closes the stream for writing if open for writing; returns nil. See Open and Closed Streams.
Flushes any buffered writes to the operating system before closing.
If the stream was opened by IO.popen and is also closed for reading, sets global variable $? (child exit status).
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
Related: IO#close, IO#close_read, IO#closed?.
Source
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);
}
Returns true if the stream is closed for both reading and writing, false otherwise. See Open and Closed Streams.
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
Related: IO#close_read, IO#close_write, IO#close.
Source
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);
}
Returns a data represents the current console mode.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
Sets the console mode to mode.
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_cooked(VALUE io)
{
return ttymode(io, rb_yield, io, set_cookedmode, NULL);
}
Yields self within cooked mode.
STDIN.cooked(&:gets)
will read and return a line with echo back and line editing.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
Enables cooked mode.
If the terminal mode needs to be back, use io.cooked { … }.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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 - ws.srWindow.Top), 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
}
Returns the current cursor position as a two-element array of integers (row, column)
io.cursor # => [3, 5]
您必须先 require ‘io/console’ 才能使用此方法。
Source
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));
}
Source
static VALUE
console_cursor_down(VALUE io, VALUE val)
{
return console_move(io, +NUM2INT(val), 0);
}
Moves the cursor down n lines.
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_cursor_left(VALUE io, VALUE val)
{
return console_move(io, 0, -NUM2INT(val));
}
Moves the cursor left n columns.
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_cursor_right(VALUE io, VALUE val)
{
return console_move(io, 0, +NUM2INT(val));
}
Moves the cursor right n columns.
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_cursor_up(VALUE io, VALUE val)
{
return console_move(io, -NUM2INT(val), 0);
}
Moves the cursor up n lines.
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
rb_io_each_line(int argc, VALUE *argv, VALUE io)
{
VALUE str;
struct getline_arg args;
RETURN_ENUMERATOR(io, argc, argv);
prepare_getline_args(argc, argv, &args, io);
if (args.limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
while (!NIL_P(str = rb_io_getline_1(args.rs, args.limit, args.chomp, io))) {
rb_yield(str);
}
return io;
}
Source
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;
}
Calls the given block with each byte (0..255) in the stream; returns self. See Byte IO.
f = File.new('t.rus') a = [] f.each_byte {|b| a << b } a # => [209, 130, 208, 181, 209, 129, 209, 130] f.close
Returns an Enumerator if no block is given.
Related: IO#each_char, IO#each_codepoint.
Source
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;
}
Calls the given block with each character in the stream; returns self. See Character IO.
f = File.new('t.rus') a = [] f.each_char {|c| a << c.ord } a # => [1090, 1077, 1089, 1090] f.close
Returns an Enumerator if no block is given.
Related: IO#each_byte, IO#each_codepoint.
Source
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);
enc = io_read_encoding(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) {
r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
enc);
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)) {
goto invalid;
}
return io;
}
}
if (MBCLEN_INVALID_P(r)) {
goto invalid;
}
n = MBCLEN_CHARFOUND_LEN(r);
c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
enc);
fptr->cbuf.off += n;
fptr->cbuf.len -= n;
rb_yield(UINT2NUM(c));
rb_io_check_char_readable(fptr);
}
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(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);
}
Calls the given block with each codepoint in the stream; returns self
f = File.new('t.rus') a = [] f.each_codepoint {|c| a << c } a # => [1090, 1077, 1089, 1090] f.close
Returns an Enumerator if no block is given.
Related: IO#each_byte, IO#each_char.
Calls the block with each remaining line read from the stream; returns self. Does nothing if already at end-of-stream; See Line IO.
With no arguments given, reads lines as determined by line separator $/
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"
With only string argument sep given, reads lines as determined by line separator sep; see Line Separator
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"
The two special values for sep are honored
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"
With only integer argument limit given, limits the number of bytes in each line; see Line 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"
With arguments sep and limit given, combines the two behaviors (see Line Separator and Line Limit).
Optional keyword argument chomp specifies whether line separators are to be omitted
f = File.new('t.txt') f.each_line(chomp: true) {|line| p line } f.close
输出
"First line" "Second line" "" "Fourth line" "Fifth line"
Returns an Enumerator if no block is given.
Source
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;
}
Enables/disables echo back. On some platforms, all combinations of this flags and raw/cooked mode may not be valid.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
Returns true if echo back is enabled.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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);
}
Returns true if the stream is positioned at its end, false otherwise; see Position
f = File.open('t.txt') f.eof # => false f.seek(0, :END) # => 0 f.eof # => true f.close
Raises an exception unless the stream is opened for reading; see Mode.
If self is a stream such as pipe or socket, this method blocks until the other end sends some data or closes it
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
Note that this method reads data to the input byte buffer. So IO#sysread may not behave as you intend with IO#eof?, unless you call IO#rewind first (which is not available for some streams).
Source
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(CSI "%dK", mode));
#endif
return io;
}
Erases the line at the cursor corresponding to mode. mode may be either: 0: after cursor 1: before and cursor 2: entire line
您必须先 require ‘io/console’ 才能使用此方法。
Source
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(CSI "%dJ", mode));
#endif
return io;
}
Erases the screen at the cursor corresponding to mode. mode may be either: 0: after cursor 1: before and cursor 2: entire screen
您必须先 require ‘io/console’ 才能使用此方法。
Source
# 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
The expect library adds instance method IO#expect, which is similar to the TCL expect extension.
To use this method, you must require expect
require 'expect'
Reads from the IO until the given pattern matches or the timeout is over.
It returns an array with the read buffer, followed by the matches. If a block is given, the result is yielded to the block and returns nil.
When called without a block, it waits until the input that matches the given pattern is obtained from the IO or the time specified as the timeout passes. An array is returned when the pattern is obtained from the IO. The first element of the array is the entire string obtained from the IO until the pattern matches, followed by elements indicating which the pattern which matched to the anchor in the regular expression.
The optional timeout parameter defines, in seconds, the total time to wait for the pattern. If the timeout expires or eof is found, nil is returned or yielded. However, the buffer in a timeout session is kept for the next expect call. The default timeout is 9999999 seconds.
Source
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));
}
Source
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);
}
Invokes Posix system call fcntl(2), which provides a mechanism for issuing low-level commands to control or query a file-oriented I/O stream. Arguments and results are platform dependent.
If argument is a number, its value is passed directly; if it is a string, it is interpreted as a binary sequence of bytes. (Array#pack might be a useful way to build this string.)
Not implemented on all platforms.
Source
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_io_blocking_region(fptr, nogvl_fdatasync, fptr) == 0)
return INT2FIX(0);
/* fall back */
return rb_io_fsync(io);
}
Immediately writes to disk all data buffered in the stream, via the operating system’s: fdatasync(2), if supported, otherwise via fsync(2), if supported; otherwise raises an exception.
Source
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);
}
Returns the integer file descriptor for the stream
$stdin.fileno # => 0 $stdout.fileno # => 1 $stderr.fileno # => 2 File.open('t.txt').fileno # => 10 f.close
Source
VALUE
rb_io_flush(VALUE io)
{
return rb_io_flush_raw(io, 1);
}
Flushes data buffered in self to the operating system (but does not necessarily flush data buffered in the operating system)
$stdout.print 'no newline' # Not necessarily flushed. $stdout.flush # Flushed.
Source
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_io_blocking_region(fptr, nogvl_fsync, fptr))
rb_sys_fail_path(fptr->pathv);
return INT2FIX(0);
}
Immediately writes to disk all data buffered in the stream, via the operating system’s fsync(2).
Note this difference
-
IO#sync=: Ensures that data is flushed from the stream’s internal buffers, but does not guarantee that the operating system actually writes the data to disk. -
IO#fsync: Ensures both that data is flushed from internal buffers, and that data is written to disk.
Raises an exception if the operating system does not support fsync(2).
Source
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);
}
Reads and returns the next byte (in range 0..255) from the stream; returns nil if already at end-of-stream. See Byte IO.
f = File.open('t.txt') f.getbyte # => 70 f.close f = File.open('t.rus') f.getbyte # => 209 f.close
Related: IO#readbyte (may raise EOFError).
Source
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);
}
Reads and returns the next 1-character string from the stream; returns nil if already at end-of-stream. See Character IO.
f = File.open('t.txt') f.getc # => "F" f.close f = File.open('t.rus') f.getc.ord # => 1090 f.close
Related: IO#readchar (may raise EOFError).
Source
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
}
Reads and returns a character in raw mode.
See IO#raw for details on the parameters.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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);
}
Reads and returns a line without echo back. Prints prompt unless it is nil.
The newline character that terminates the read line is removed from the returned string, see String#chomp!.
您必须先 require ‘io/console’ 才能使用此方法。
require 'io/console' IO::console.getpass("Enter password:") Enter password: # => "mypassword"
Source
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;
}
读取并返回流中的一行;将返回值赋给 $_。请参阅 行 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
The two special values for sep are honored
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"
With arguments sep and limit given, combines the two behaviors (see Line Separator and Line Limit).
Optional keyword argument chomp specifies whether line separators are to be omitted
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
Source
static VALUE
console_goto(VALUE io, VALUE y, VALUE x)
{
#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 = NUM2UINT(x);
pos->Y = ws.srWindow.Top + NUM2UINT(y);
if (!SetConsoleCursorPosition(h, *pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
#else
rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
#endif
return io;
}
将光标位置设置为 line 和 column。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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(CSI "%dG", NUM2UINT(val)+1));
#endif
return io;
}
在当前行的 column 位置设置光标。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
刷新内核中的输入缓冲区。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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, ">");
}
返回 self 的字符串表示形式。
f = File.open('t.txt') f.inspect # => "#<File:t.txt>" f.close
Source
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));
}
Source
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);
}
调用 Posix 系统调用 ioctl(2),向 I/O 设备发出低级别命令。
向 I/O 设备发出低级别命令。参数和返回值是平台相关的。调用效果是平台相关的。
如果参数 argument 是整数,则直接传递;如果它是字符串,则将其解释为字节的二进制序列。
Not implemented on all platforms.
Source
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;
}
刷新内核中的输入和输出缓冲区。
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
rb_io_isatty(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return RBOOL(isatty(fptr->fd) != 0);
}
如果流与终端设备 (tty) 相关,则返回 true,否则返回 false。
f = File.new('t.txt').isatty #=> false f.close f = File.new('/dev/tty').isatty #=> true f.close
Source
static VALUE
rb_io_lineno(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
return INT2NUM(fptr->lineno);
}
返回流的当前行号;请参阅 行号。
Source
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;
}
设置并返回流的行号;请参阅 行号。
Source
static VALUE
console_noecho(VALUE io)
{
return ttymode(io, rb_yield, io, set_noecho, NULL);
}
yield self,禁用回显。
STDIN.noecho(&:gets)
将读取并返回一行,而不会回显。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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);
}
yield self,处于非阻塞模式。
当给出布尔值 false 作为参数时,self 在阻塞模式下被 yield。块执行完毕后,将恢复原始模式。
Source
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;
}
设置为 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
Source
static VALUE
rb_io_nonblock_p(VALUE io)
{
if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK)
return Qtrue;
return Qfalse;
}
如果 IO 对象处于非阻塞模式,则返回 true。
Source
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;
}
刷新内核中的输出缓冲区。
您必须先 require ‘io/console’ 才能使用此方法。
Source
VALUE
rb_io_path(VALUE io)
{
rb_io_t *fptr = RFILE(io)->fptr;
if (!fptr)
return Qnil;
return rb_obj_dup(fptr->pathv);
}
返回与 IO 关联的路径,如果流没有关联路径,则返回 nil。不保证路径存在于文件系统中。
$stdin.path # => "<STDIN>" File.open("testfile") {|f| f.path} # => "testfile"
Source
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);
}
使用 fpathconf() 返回路径名配置变量。
name 应该是 Etc 下以 PC_ 开头的常量。
返回值是一个整数或 nil。nil 表示无限限制。(fpathconf() 返回 -1,但 errno 未设置。)
require 'etc' IO.pipe {|r, w| p w.pathconf(Etc::PC_PIPE_BUF) #=> 4096 }
Source
static VALUE
rb_io_pid(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (!fptr->pid)
return Qnil;
return PIDT2NUM(fptr->pid);
}
返回与流关联的子进程的进程 ID,该 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
Source
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);
}
Source
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;
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.io = 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;
}
行为类似于 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
在某些平台上不可用。
Source
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;
}
Source
VALUE
rb_io_print(int argc, const VALUE *argv, VALUE out)
{
int i;
VALUE line;
/* if no argument given, print `$_' */
if (argc == 0) {
argc = 1;
line = rb_lastline_get();
argv = &line;
}
if (argc > 1 && !NIL_P(rb_output_fs)) {
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
}
for (i=0; i<argc; i++) {
if (!NIL_P(rb_output_fs) && i>0) {
rb_io_write(out, rb_output_fs);
}
rb_io_write(out, argv[i]);
}
if (argc > 0 && !NIL_P(rb_output_rs)) {
rb_io_write(out, rb_output_rs);
}
return Qnil;
}
将给定的对象写入流;返回 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
Source
VALUE
rb_io_printf(int argc, const VALUE *argv, VALUE out)
{
rb_io_write(out, rb_f_sprintf(argc, argv));
return Qnil;
}
格式化 objects 并将其写入流。
有关 format_string 的详细信息,请参阅 格式规范。
Source
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;
}
将一个字符写入流。请参阅 字符 IO。
如果 object 是数字,则在必要时转换为整数,然后写入其最低有效字节的字符代码;如果 object 是字符串,则写入第一个字符。
$stdout.putc "A" $stdout.putc 65
输出
AA
Source
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;
}
将给定的 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"
Source
static VALUE
rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
{
rb_io_t *fptr;
ssize_t n;
struct prdwr_internal_arg arg;
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.io = 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)pwrite_internal_call((VALUE)&arg);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return SSIZET2NUM(n);
}
Source
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);
}
yield self,处于原始模式,并返回块的结果。
STDIN.raw(&:gets)
将读取并返回一行,而不会回显或行编辑。
参数 min 指定执行读取操作时应接收的最小字节数。(默认值:1)
参数 time 指定超时时间(以秒为单位),精度为 1/10 秒。(默认值:0)
如果参数 intr 为 true,则启用中断、停止、退出和挂起特殊字符。
有关更多详细信息,请参阅 termios 手册页。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
启用原始模式,并返回 io。
如果需要恢复终端模式,请使用 io.raw { ... }。
See IO#raw for details on the parameters.
您必须先 require ‘io/console’ 才能使用此方法。
Source
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;
}
从流中读取字节;流必须以读取模式打开(请参阅 访问模式)。
-
如果
maxlen为nil,则使用流的数据模式读取所有字节。 -
否则,在二进制模式下读取最多
maxlen字节。
返回一个字符串(新字符串或给定的 out_string),包含读取的字节。字符串的编码取决于 maxLen 和 out_string。
-
maxlen为nil:使用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) 系统调用的行为,请考虑 readpartial、read_nonblock 和 sysread。
相关: IO#write。
Source
# File io.rb, line 62 def read_nonblock(len, buf = nil, exception: true) Primitive.io_read_nonblock(len, buf, exception) end
在为底层文件描述符设置 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 类型的异常时,为了避免忙循环,在 io 可读之前不应调用 read_nonblock。这可以按如下方式完成。
# 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。
Source
static VALUE
rb_io_readbyte(VALUE io)
{
VALUE c = rb_io_getbyte(io);
if (NIL_P(c)) {
rb_eof_error();
}
return c;
}
从流中读取并返回下一个字节(范围 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)。
Source
static VALUE
rb_io_readchar(VALUE io)
{
VALUE c = rb_io_getc(io);
if (NIL_P(c)) {
rb_eof_error();
}
return c;
}
Source
# File io.rb, line 133 def readline(sep = $/, limit = nil, chomp: false) Primitive.io_readline(sep, limit, chomp) end
Source
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);
}
读取并返回流中所有剩余的行;不修改 $_。请参阅 行 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
The two special values for sep are honored
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
With only integer argument limit given, limits the number of bytes in each line; see Line 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
With arguments sep and limit given, combines the two behaviors (see Line Separator and Line Limit).
Optional keyword argument chomp specifies whether line separators are to be omitted
f = File.new('t.txt') f.readlines(chomp: true) # => ["First line", "Second line", "", "Fourth line", "Fifth line"] f.close
Source
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;
}
从流中读取最多 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
此方法对于管道、套接字或终端等流很有用。只有在没有数据可立即读取时才会阻塞。这意味着只有在以下所有条件都成立时才会阻塞:
-
流中的字节缓冲区为空。
-
流的内容为空。
-
流未达到 EOF。
阻塞时,方法会等待更多数据或流的 EOF。
-
读取更多数据后,方法返回数据。
-
达到 EOF 时,方法引发
EOFError。
不阻塞时,方法立即响应。
-
如果有数据,则从缓冲区返回数据。
-
否则,如果有数据,则从流中返回数据。
-
否则,如果流已达到 EOF,则引发
EOFError。
注意,此方法与 sysread 相似。区别在于:
-
如果字节缓冲区不为空,则从字节缓冲区读取,而不是从“sysread for buffered
IO(IOError)”。 -
它不会引发 Errno::EWOULDBLOCK 和 Errno::EINTR。当 readpartial 通过 read 系统调用遇到 EWOULDBLOCK 和 EINTR 时,readpartial 会重试系统调用。
后者意味着 readpartial 与非阻塞标志无关。它在 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" "" ""
Source
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)) {
enum rb_io_mode 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;
}
将流重新关联到另一个流,后者可能属于不同的类。此方法可用于将现有流重定向到新目标。
提供了参数 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')
Optional keyword arguments opts specify
Source
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);
}
Source
static VALUE
console_scroll_backward(VALUE io, VALUE val)
{
return console_scroll(io, -NUM2INT(val));
}
将整个滚动条向后滚动 n 行。
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_scroll_forward(VALUE io, VALUE val)
{
return console_scroll(io, +NUM2INT(val));
}
将整个滚动条向前滚动 n 行。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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);
}
定位到由整数 offset(请参阅 位置)和常量 whence 指定的位置,其中 whence 是以下之一:
-
:CUR或IO::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
-
:END或IO::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
-
:SET或IO: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
Source
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;
}
Source
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);
}
如果流以 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 或其编码已设置,则引发异常。
Source
static VALUE
rb_io_stat(VALUE obj)
{
rb_io_t *fptr;
rb_io_stat_data st;
GetOpenFile(obj, fptr);
if (fstatx_without_gvl(fptr, &st, STATX_ALL) == -1) {
rb_sys_fail_path(fptr->pathv);
}
return rb_statx_new(&st);
}
以 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
Source
static VALUE
rb_io_sync(VALUE io)
{
rb_io_t *fptr;
io = GetWriteIO(io);
GetOpenFile(io, fptr);
return RBOOL(fptr->mode & FMODE_SYNC);
}
返回流的当前同步模式。当同步模式为 true 时,所有输出都会立即刷新到底层操作系统,并且不会被 Ruby 内部缓冲。另请参阅 fsync。
f = File.open('t.tmp', 'w') f.sync # => false f.sync = true f.sync # => true f.close
Source
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;
}
将流的同步模式设置为给定的值;返回给定的值。
同步模式的值
-
true:所有输出都立即刷新到底层操作系统,并且不会在内部缓冲。 -
false:输出可能会在内部缓冲。
示例;
f = File.open('t.tmp', 'w') f.sync # => false f.sync = true f.sync # => true f.close
相关: IO#fsync。
Source
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;
}
行为类似于 IO#readpartial,但它使用低级系统函数。
不应将此方法与其他流读取方法一起使用。
Source
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);
}
行为类似于 IO#seek,但它
-
使用低级系统函数。
-
返回新位置。
Source
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);
}
将给定的 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
不应将此方法与其他流写入方法一起使用。
Source
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);
}
返回 self 中的当前位置(以字节为单位)(请参阅 位置)。
f = File.open('t.txt') f.tell # => 0 f.gets # => "First line\n" f.tell # => 12 f.close
Source
VALUE
rb_io_timeout(VALUE self)
{
rb_io_t *fptr = rb_io_get_fptr(self);
return fptr->timeout;
}
获取内部超时时长,如果未设置则为 nil。
Source
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;
}
将内部超时设置为指定的时长或 nil。超时适用于所有可能的阻塞操作。
当操作执行时间超过设置的超时时间时,将引发 IO::TimeoutError。
这会影响以下方法(但不限于): gets、puts、read、write、wait_readable 和 wait_writable。这也会影响阻塞套接字操作,如 Socket#accept 和 Socket#connect。
像 File#open 和 IO#close 这样的某些操作不受超时影响。在写入操作期间超时可能会使 IO 处于不一致状态,例如数据已部分写入。总的来说,超时是为了防止应用程序在慢速 I/O 操作(如慢速攻击期间发生的操作)上挂起而采取的最后手段。
Source
static VALUE
console_ttyname(VALUE io)
{
int fd = rb_io_descriptor(io);
if (!isatty(fd)) return Qnil;
# if defined _WIN32
return rb_usascii_str_new_lit("con");
# elif defined HAVE_TTYNAME_R
{
char termname[1024], *tn = termname;
size_t size = sizeof(termname);
int e;
if (ttyname_r(fd, tn, size) == 0)
return rb_interned_str_cstr(tn);
if ((e = errno) == ERANGE) {
VALUE s = rb_str_new(0, size);
while (1) {
tn = RSTRING_PTR(s);
size = rb_str_capacity(s);
if (ttyname_r(fd, tn, size) == 0) {
return rb_str_to_interned_str(rb_str_resize(s, strlen(tn)));
}
if ((e = errno) != ERANGE) break;
if ((size *= 2) >= INT_MAX/2) break;
rb_str_resize(s, size);
}
}
rb_syserr_fail_str(e, rb_sprintf("ttyname_r(%d)", fd));
UNREACHABLE_RETURN(Qnil);
}
# elif defined HAVE_TTYNAME
{
const char *tn = ttyname(fd);
if (!tn) {
int e = errno;
rb_syserr_fail_str(e, rb_sprintf("ttyname(%d)", fd));
}
return rb_interned_str_cstr(tn);
}
# else
# error No ttyname function
# endif
}
如果 io 不是 tty,则返回关联终端(tty)的名称。否则返回 nil。
Source
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:
StringValue(b);
}
io_ungetbyte(b, fptr);
return Qnil;
}
将给定的数据推回(“unshifts”)到流的缓冲区,使其排在下一个要读取的位置;返回 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
Source
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 {
StringValue(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;
}
将给定的数据推回(“unshifts”)到流的缓冲区,使其排在下一个要读取的位置;返回 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
Source
static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
VALUE timeout = Qundef;
enum rb_io_event events = 0;
int return_io = 0;
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 (int i = 0; i < argc; i += 1) {
if (RB_SYMBOL_P(argv[i])) {
events |= wait_mode_sym(argv[i]);
}
else if (UNDEF_P(timeout)) {
rb_time_interval(timeout = argv[i]);
}
else {
rb_raise(rb_eArgError, "timeout given more than once");
}
}
if (UNDEF_P(timeout)) 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);
}
等待直到 IO 对于指定的事件准备就绪,并返回就绪事件的子集,或者在超时时返回一个假值。
事件可以是 IO::READABLE、IO::WRITABLE 或 IO::PRIORITY 的位掩码。
当有缓冲数据可用时,立即返回事件掩码(真值)。
第二种形式:如果传递了一个或多个事件符号(:read、:write 或 :read_write),则事件掩码是对应于这些符号的位掩码的按位或。在这种形式下,timeout 是可选的,参数的顺序是任意的,如果任何事件准备就绪,则返回 io。
Source
static VALUE
io_wait_priority(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
rb_io_check_char_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);
}
等待直到 IO 具有优先级,并在超时时返回真值或假值。优先级数据使用 Socket::MSG_OOB 标志发送和接收,通常仅限于流。
Source
static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
RB_IO_POINTER(io, fptr);
rb_io_check_char_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_READABLE, timeout, 1);
}
等待直到 IO 可读,并在超时时返回真值或假值。当有缓冲数据可用时,立即返回真值。
Source
static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
RB_IO_POINTER(io, fptr);
rb_io_check_writable(fptr);
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1);
}
等待直到 IO 可写,并在超时时返回真值或假值。
Source
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)));
}
返回控制台大小。
您必须先 require ‘io/console’ 才能使用此方法。
Source
static VALUE
console_set_winsize(VALUE io, VALUE size)
{
rb_console_size_t ws;
#if defined _WIN32
HANDLE wh;
int newrow, newcol;
COORD oldsize;
SMALL_RECT oldwindow;
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");
}
oldsize = ws.dwSize;
oldwindow = ws.srWindow;
if (ws.srWindow.Right + 1 < newcol) {
ws.dwSize.X = newcol;
}
if (ws.dwSize.Y < newrow) {
ws.dwSize.Y = newrow;
}
/* expand screen buffer first if needed */
if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
}
/* refresh ws for new dwMaximumWindowSize */
if (!GetConsoleScreenBufferInfo(wh, &ws)) {
rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
}
/* check new size before modifying buffer content */
if (newrow <= 0 || newcol <= 0 ||
newrow > ws.dwMaximumWindowSize.Y ||
newcol > ws.dwMaximumWindowSize.X) {
SetConsoleScreenBufferSize(wh, oldsize);
/* remove scrollbar if possible */
SetConsoleWindowInfo(wh, TRUE, &oldwindow);
rb_raise(rb_eArgError, "out of range winsize: [%d, %d]", newrow, newcol);
}
/* shrink screen buffer width */
ws.dwSize.X = newcol;
/* shrink screen buffer height if window height were the same */
if (oldsize.Y == ws.srWindow.Bottom - ws.srWindow.Top + 1) {
ws.dwSize.Y = newrow;
}
ws.srWindow.Left = 0;
ws.srWindow.Right = newcol - 1;
ws.srWindow.Bottom = ws.srWindow.Top + newrow -1;
if (ws.dwCursorPosition.Y > ws.srWindow.Bottom) {
console_scroll(io, ws.dwCursorPosition.Y - ws.srWindow.Bottom);
ws.dwCursorPosition.Y = ws.srWindow.Bottom;
console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X));
}
if (ws.srWindow.Bottom > ws.dwSize.Y - 1) {
console_scroll(io, ws.srWindow.Bottom - (ws.dwSize.Y - 1));
ws.dwCursorPosition.Y -= ws.srWindow.Bottom - (ws.dwSize.Y - 1);
console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X));
ws.srWindow.Bottom = ws.dwSize.Y - 1;
}
ws.srWindow.Top = ws.srWindow.Bottom - (newrow - 1);
/* perform changes to winsize */
if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
int last_error = LAST_ERROR;
SetConsoleScreenBufferSize(wh, oldsize);
rb_syserr_fail(last_error, "SetConsoleWindowInfo");
}
/* perform screen buffer shrinking if necessary */
if ((ws.dwSize.Y < oldsize.Y || ws.dwSize.X < oldsize.X) &&
!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;
}
尝试设置控制台大小。效果取决于平台和运行环境。
您必须先 require ‘io/console’ 才能使用此方法。
Source
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);
}
}
Source
# File io.rb, line 120 def write_nonblock(buf, exception: true) Primitive.io_write_nonblock(buf, exception) end
在为底层文件描述符设置 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 类型的异常时,为了避免忙循环,在 io 可写之前不应调用 write_nonblock。这可以按如下方式完成。
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。