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:返回一个新的 IO 对象,该对象连接到新启动的子进程的 $stdin 和 $stdout。 -
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.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 个字节(给定 n)。 -
read_nonblock:从self读取的下一个 n 个字节(给定 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: 基于其 Unicode 字节顺序标记设置self的编码。 -
sync=: 将同步模式设置为给定值。
查询¶ ↑
-
autoclose?: 返回self是否自动关闭。 -
binmode?: 返回self是否处于二进制模式。 -
close_on_exec?: 返回self的执行时关闭标志。 -
closed?: 返回self是否已关闭。 -
external_encoding: 返回self的外部编码对象。 -
internal_encoding: 返回self的内部编码对象。 -
stat: 返回包含self状态信息的File::Stat对象。 -
sync: 返回self是否处于同步模式。
缓冲¶ ↑
-
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的字符串表示形式。
常量
公共类方法
源代码
static VALUE
rb_io_s_binread(int argc, VALUE *argv, VALUE io)
{
VALUE offset;
struct foreach_arg arg;
enum {
fmode = FMODE_READABLE|FMODE_BINMODE,
oflags = O_RDONLY
#ifdef O_BINARY
|O_BINARY
#endif
};
struct rb_io_encoding convconfig = {NULL, NULL, 0, Qnil};
rb_scan_args(argc, argv, "12", NULL, NULL, &offset);
FilePathValue(argv[0]);
convconfig.enc = rb_ascii8bit_encoding();
arg.io = rb_io_open_generic(io, argv[0], oflags, fmode, &convconfig, 0);
if (NIL_P(arg.io)) return Qnil;
arg.argv = argv+1;
arg.argc = (argc > 1) ? 1 : 0;
if (!NIL_P(offset)) {
struct seek_arg sarg;
int state = 0;
sarg.io = arg.io;
sarg.offset = offset;
sarg.mode = SEEK_SET;
rb_protect(seek_before_access, (VALUE)&sarg, &state);
if (state) {
rb_io_close(arg.io);
rb_jump_tag(state);
}
}
return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}
行为类似于 IO.read,除了流以带有 ASCII-8BIT 编码的二进制模式打开。
当从类 IO (而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入。
源代码
源代码
static VALUE
console_dev(int argc, VALUE *argv, VALUE klass)
{
VALUE con = 0;
VALUE sym = 0;
rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);
if (argc) {
Check_Type(sym = argv[0], T_SYMBOL);
}
// Force the class to be File.
if (klass == rb_cIO) klass = rb_cFile;
if (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;
}
返回打开的控制台的 File 实例。
如果给定了 sym,它将使用 args 发送到打开的控制台,并且将返回结果而不是控制台 IO 本身。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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
使用参数 src_length(一个非负整数),最多复制那么多字节
IO.copy_stream('t.txt', 't.tmp', 10) # => 10 File.read('t.tmp') # => "First line"
如果还给定了参数 src_offset,则从该偏移量开始读取源流
IO.copy_stream('t.txt', 't.tmp', 11, 11) # => 11 IO.read('t.tmp') # => "Second line"
源代码
static VALUE
rb_io_s_for_fd(int argc, VALUE *argv, VALUE klass)
{
VALUE io = rb_obj_alloc(klass);
rb_io_initialize(argc, argv, io);
return io;
}
是 IO.new 的同义词。
源代码
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);
}
使用从流中读取的每个连续行调用块。
当从类 IO (而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入。
第一个参数必须是字符串,该字符串是文件的路径。
如果仅给定参数 path,则解析给定 path 处的文件中的行(由默认行分隔符确定),并使用每个连续行调用块
File.foreach('t.txt') {|line| p line }
输出:与上述相同。
对于命令和路径这两种形式,其余参数是相同的。
如果给定了参数 sep,则根据该行分隔符解析行(请参阅 行分隔符)
File.foreach('t.txt', 'li') {|line| p line }
输出
"First li" "ne\nSecond li" "ne\n\nThird li" "ne\nFourth li" "ne\n"
每个段落
File.foreach('t.txt', '') {|paragraph| p paragraph }
输出
"First line\nSecond line\n\n" "Third line\nFourth line\n"
如果给定了参数 limit,则根据默认行分隔符和给定的行长度限制解析行(请参阅 行分隔符 和 行限制)
File.foreach('t.txt', 7) {|line| p line }
输出
"First l" "ine\n" "Second " "line\n" "\n" "Third l" "ine\n" "Fourth l" "line\n"
如果给定了参数 sep 和 limit,则结合这两种行为(请参阅 行分隔符和行限制)。
可选关键字参数 opts 指定
如果没有给出块,则返回 Enumerator。
源代码
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);
}
从文件描述符创建并返回新的 IO 对象(文件流)。
IO.new 对于与低级别库的交互可能很有用。对于更高级别的交互,使用 File.open 创建文件流可能更简单。
参数 fd 必须是有效的文件描述符(整数)
path = 't.tmp' fd = IO.sysopen(path) # => 3 IO.new(fd) # => #<IO:fd 3>
新的 IO 对象不继承编码(因为整数文件描述符没有编码)
fd = IO.sysopen('t.rus', 'rb') io = IO.new(fd) io.external_encoding # => #<Encoding:UTF-8> # Not ASCII-8BIT.
可选参数 mode(默认为 ‘r’)必须指定有效的模式;请参阅 访问模式
IO.new(fd, 'w') # => #<IO:fd 3> IO.new(fd, File::WRONLY) # => #<IO:fd 3>
可选关键字参数 opts 指定
示例
IO.new(fd, internal_encoding: nil) # => #<IO:fd 3> IO.new(fd, autoclose: true) # => #<IO:fd 3>
源代码
static VALUE
rb_io_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;
}
通过使用给定参数的 IO.new 创建新的 IO 对象。
如果没有给出块,则返回 IO 对象。
如果给出了块,则使用 IO 对象调用该块并返回该块的值。
源代码
static VALUE
rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
{
int pipes[2], state;
VALUE r, w, args[3], v1, v2;
VALUE opt;
rb_io_t *fptr, *fptr2;
struct io_encoding_set_args ies_args;
int fmode = 0;
VALUE ret;
argc = rb_scan_args(argc, argv, "02:", &v1, &v2, &opt);
if (rb_pipe(pipes) < 0)
rb_sys_fail(0);
args[0] = klass;
args[1] = INT2NUM(pipes[0]);
args[2] = INT2FIX(O_RDONLY);
r = rb_protect(io_new_instance, (VALUE)args, &state);
if (state) {
close(pipes[0]);
close(pipes[1]);
rb_jump_tag(state);
}
GetOpenFile(r, fptr);
ies_args.fptr = fptr;
ies_args.v1 = v1;
ies_args.v2 = v2;
ies_args.opt = opt;
rb_protect(io_encoding_set_v, (VALUE)&ies_args, &state);
if (state) {
close(pipes[1]);
io_close(r);
rb_jump_tag(state);
}
args[1] = INT2NUM(pipes[1]);
args[2] = INT2FIX(O_WRONLY);
w = rb_protect(io_new_instance, (VALUE)args, &state);
if (state) {
close(pipes[1]);
if (!NIL_P(r)) rb_io_close(r);
rb_jump_tag(state);
}
GetOpenFile(w, fptr2);
rb_io_synchronized(fptr2);
extract_binmode(opt, &fmode);
if ((fmode & FMODE_BINMODE) && NIL_P(v1)) {
rb_io_ascii8bit_binmode(r);
rb_io_ascii8bit_binmode(w);
}
#if DEFAULT_TEXTMODE
if ((fptr->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
fptr->mode &= ~FMODE_TEXTMODE;
setmode(fptr->fd, O_BINARY);
}
#if RUBY_CRLF_ENVIRONMENT
if (fptr->encs.ecflags & ECONV_DEFAULT_NEWLINE_DECORATOR) {
fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
}
#endif
#endif
fptr->mode |= fmode;
#if DEFAULT_TEXTMODE
if ((fptr2->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
fptr2->mode &= ~FMODE_TEXTMODE;
setmode(fptr2->fd, O_BINARY);
}
#endif
fptr2->mode |= fmode;
ret = rb_assoc_new(r, w);
if (rb_block_given_p()) {
VALUE rw[2];
rw[0] = r;
rw[1] = w;
return rb_ensure(rb_yield, ret, pipe_pair_close, (VALUE)rw);
}
return ret;
}
创建一对管道端点 read_io 和 write_io,它们相互连接。
如果给定了参数 enc_string,则它必须是包含以下之一的字符串
-
要用作外部编码的编码名称。
-
要用作外部编码和内部编码的两个编码的冒号分隔名称。
如果给定了参数 int_enc,则它必须是一个 Encoding 对象或指定要使用的内部编码的编码名称字符串;如果还给定了参数 ext_enc,则它必须是一个 Encoding 对象或指定要使用的外部编码的编码名称字符串。
从 read_io 读取的字符串使用外部编码进行标记;如果还指定了内部编码,则该字符串将转换为该编码并使用该编码进行标记。
如果指定了任何编码,则可选的哈希参数指定转换选项。
可选关键字参数 opts 指定
如果没有给出块,则在数组中返回两个端点
IO.pipe # => [#<IO:fd 4>, #<IO:fd 5>]
如果给出了块,则使用两个端点调用该块;关闭两个端点并返回该块的值
IO.pipe {|read_io, write_io| p read_io; p write_io }
输出
#<IO:fd 6> #<IO:fd 7>
并非在所有平台上都可用。
在下面的示例中,两个进程关闭它们不使用的管道末端。这不仅仅是一个表面上的好处。如果还有任何写入器打开该管道,则管道的读取端将不会生成文件结束条件。在父进程的情况下,如果它不首先发出 wr.close,则 rd.read 将永远不会返回
rd, wr = IO.pipe if fork wr.close puts "Parent got: <#{rd.read}>" rd.close Process.wait else rd.close puts 'Sending message to parent' wr.write "Hi Dad" wr.close end
产生
Sending message to parent Parent got: <Hi Dad>
源代码
static VALUE
rb_io_s_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);
}
将给定命令 cmd 作为子进程执行,其 $stdin 和 $stdout 连接到新的流 io。
如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入。
如果没有给出块,则返回新的流,该流根据给定的 mode 可以打开以进行读取、写入或两者。应显式关闭该流(最终)以避免资源泄漏。
如果给出了块,则将流传递给该块(同样,打开以进行读取、写入或两者);当块退出时,流将关闭,并且该块的值将分配给全局变量 $? 并返回。
可选参数 mode 可以是任何有效的 IO 模式。 请参阅 访问模式。
必需参数 cmd 确定发生以下哪种情况
-
进程 fork。
-
指定的程序在 shell 中运行。
-
指定的程序使用指定的参数运行。
-
指定的程序使用指定的参数和指定的
argv0运行。
以下将详细介绍每种情况。
可选的哈希参数 env 指定要添加到子进程环境变量中的名称/值对
IO.popen({'FOO' => 'bar'}, 'ruby', 'r+') do |pipe| pipe.puts 'puts ENV["FOO"]' pipe.close_write pipe.gets end => "bar\n"
可选关键字参数 opts 指定
-
打开选项.
-
编码选项.
-
Kernel#spawn的选项。
Forked 进程
当参数 cmd 是单字符字符串 '-' 时,导致进程 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
请注意,并非所有平台都支持此操作。
Shell 子进程
当参数 cmd 是单个字符串(但不是 '-')时,名为 cmd 的程序将作为 shell 命令运行
IO.popen('uname') do |pipe| pipe.readlines end
输出
["Linux\n"]
另一个示例
IO.popen('/bin/sh', 'r+') do |pipe| pipe.puts('ls') pipe.close_write $stderr.puts pipe.readlines.size end
输出
213
程序子进程
当参数 cmd 是字符串数组时,名为 cmd[0] 的程序将运行,并将 cmd 的所有元素作为其参数
IO.popen(['du', '..', '.']) do |pipe| $stderr.puts pipe.readlines.size end
输出
1111
带有 argv0 的程序子进程
当参数 cmd 是一个数组,其第一个元素是 2 元素字符串数组,其余元素(如果有)是字符串时
-
cmd[0][0](嵌套数组中的第一个字符串)是要运行的程序的名称。 -
cmd[0][1](嵌套数组中的第二个字符串)设置为程序的argv[0]。 -
cmd[1..-1](外部数组中的字符串)是程序的参数。
示例(将 $0 设置为 'foo')
IO.popen([['/bin/sh', 'foo'], '-c', 'echo $0']).read # => "foo\n"
一些特殊示例
# Set IO encoding. IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io| euc_jp_string = nkf_io.read } # Merge standard output and standard error using Kernel#spawn option. See Kernel#spawn. IO.popen(["ls", "/", :err=>[:child, :out]]) do |io| ls_result_with_error = io.read end # Use mixture of spawn options and IO options. IO.popen(["ls", "/"], :err=>[:child, :out]) do |io| ls_result_with_error = io.read end f = IO.popen("uname") p f.readlines f.close puts "Parent is #{Process.pid}" IO.popen("date") {|f| puts f.gets } IO.popen("-") {|f| $stderr.puts "#{Process.pid} is here, f is #{f.inspect}"} p $? IO.popen(%w"sed -e s|^|<foo>| -e s&$&;zot;&", "r+") {|f| f.puts "bar"; f.close_write; puts f.gets }
输出(来自上一节)
["Linux\n"] Parent is 21346 Thu Jan 15 22:41:19 JST 2009 21346 is here, f is #<IO:fd 3> 21352 is here, f is nil #<Process::Status: pid 21352 exit 0> <foo>bar;zot;
引发 IO.pipe 和 Kernel.spawn 引发的异常。
源代码
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);
}
打开流,读取并返回其部分或全部内容,然后关闭流;如果没有读取到字节,则返回 nil。
当从类 IO (而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入。
第一个参数必须是字符串,该字符串是文件的路径。
仅给定参数 path 时,以文本模式读取并返回给定路径的文件全部内容
IO.read('t.txt') # => "First line\nSecond line\n\nThird line\nFourth line\n"
在 Windows 上,当遇到某些特殊字节时,文本模式可能会终止读取,并使文件中的字节未读取。 如果应该读取文件中的所有字节,请考虑使用 IO.binread。
使用参数 length,如果可用,则返回 length 个字节
IO.read('t.txt', 7) # => "First l" IO.read('t.txt', 700) # => "First line\r\nSecond line\r\n\r\nFourth line\r\nFifth line\r\n"
使用参数 length 和 offset,如果可用,则返回从给定 offset 开始的 length 个字节
IO.read('t.txt', 10, 2) # => "rst line\nS" IO.read('t.txt', 10, 200) # => nil
可选关键字参数 opts 指定
源代码
static VALUE
rb_io_s_readlines(int argc, VALUE *argv, VALUE io)
{
VALUE opt;
struct foreach_arg arg;
struct getline_arg garg;
argc = rb_scan_args(argc, argv, "12:", NULL, NULL, NULL, &opt);
extract_getline_args(argc-1, argv+1, &garg);
open_key_args(io, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
extract_getline_opts(opt, &garg);
check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
return rb_ensure(io_s_readlines, (VALUE)&garg, rb_io_close, arg.io);
}
返回从流中读取的所有行的数组。
当从类 IO (而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入。
第一个参数必须是字符串,该字符串是文件的路径。
仅给定参数 path 时,根据默认行分隔符解析给定 path 处文件的行,并在数组中返回这些行
IO.readlines('t.txt') # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
如果给定了参数 sep,则根据该行分隔符解析行(请参阅 行分隔符)
# Ordinary separator. IO.readlines('t.txt', 'li') # =>["First li", "ne\nSecond li", "ne\n\nThird li", "ne\nFourth li", "ne\n"] # Get-paragraphs separator. IO.readlines('t.txt', '') # => ["First line\nSecond line\n\n", "Third line\nFourth line\n"] # Get-all separator. IO.readlines('t.txt', nil) # => ["First line\nSecond line\n\nThird line\nFourth line\n"]
给定参数 limit 时,根据默认行分隔符和给定的行长度限制解析行(请参阅 行分隔符 和 行限制)
IO.readlines('t.txt', 7) # => ["First l", "ine\n", "Second ", "line\n", "\n", "Third l", "ine\n", "Fourth ", "line\n"]
如果给定了参数 sep 和 limit,则结合这两种行为(请参阅 行分隔符和行限制)。
可选关键字参数 opts 指定
源代码
static VALUE
rb_f_select(int argc, VALUE *argv, VALUE obj)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
// It's optionally supported.
VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv);
if (!UNDEF_P(result)) return result;
}
VALUE timeout;
struct select_args args;
struct timeval timerec;
int i;
rb_scan_args(argc, argv, "13", &args.read, &args.write, &args.except, &timeout);
if (NIL_P(timeout)) {
args.timeout = 0;
}
else {
timerec = rb_time_interval(timeout);
args.timeout = &timerec;
}
for (i = 0; i < numberof(args.fdsets); ++i)
rb_fd_init(&args.fdsets[i]);
return rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
}
调用系统调用 select(2),该系统调用监视多个文件描述符,直到一个或多个文件描述符准备好进行某种 I/O 操作。
并非在所有平台上都实现。
参数 read_ios、write_ios 和 error_ios 中的每一个都是 IO 对象的数组。
参数 timeout 是一个数值(例如整数或浮点数),表示超时时间间隔(以秒为单位)。
该方法监视所有三个数组中给定的 IO 对象,等待一些对象准备就绪;返回一个包含 3 个元素的数组,其元素是
-
read_ios中准备好进行读取的对象数组。 -
write_ios中准备好进行写入的对象数组。 -
error_ios中有待处理异常的对象数组。
如果在给定的 timeout 内没有对象准备就绪,则返回 nil。
IO.select 会窥视 IO 对象的缓冲区以测试可读性。 如果 IO 缓冲区不为空,IO.select 会立即通知可读性。 此“窥视”仅针对 IO 对象发生。 它不会发生在类似 IO 的对象上,例如 OpenSSL::SSL::SSLSocket。
使用 IO.select 的最佳方式是在非阻塞方法(例如 read_nonblock、write_nonblock 等)之后调用它。 这些方法会引发一个异常,该异常由 IO::WaitReadable 或 IO::WaitWritable 扩展。 这些模块通知调用方应如何使用 IO.select 等待。 如果引发 IO::WaitReadable,则调用方应等待读取。 如果引发 IO::WaitWritable,则调用方应等待写入。
因此,可以使用 read_nonblock 和 IO.select 模拟阻塞读取 (readpartial),如下所示
begin result = io_like.read_nonblock(maxlen) rescue IO::WaitReadable IO.select([io_like]) retry rescue IO::WaitWritable IO.select(nil, [io_like]) retry end
特别是,非阻塞方法和 IO.select 的组合对于类似 IO 的对象(例如 OpenSSL::SSL::SSLSocket)是首选。 它具有 to_io 方法来返回底层的 IO 对象。 IO.select 调用 to_io 来获取要等待的文件描述符。
这意味着 IO.select 通知的可读性并不意味着来自 OpenSSL::SSL::SSLSocket 对象的可读性。
最可能的情况是 OpenSSL::SSL::SSLSocket 缓冲了一些数据。 IO.select 看不到该缓冲区。 因此,当 OpenSSL::SSL::SSLSocket#readpartial 不阻塞时,IO.select 可能会阻塞。
但是,还存在几种更复杂的情况。
SSL 是一种记录序列协议。 该记录由多个字节组成。 因此,SSL 的远程端发送一个部分记录,IO.select 通知可读性,但是 OpenSSL::SSL::SSLSocket 无法解密字节,并且 OpenSSL::SSL::SSLSocket#readpartial 将会阻塞。
此外,远程端可以请求 SSL 重新协商,这将强制本地 SSL 引擎写入一些数据。 这意味着 OpenSSL::SSL::SSLSocket#readpartial 可能会调用 write 系统调用,并且它可能会阻塞。 在这种情况下,OpenSSL::SSL::SSLSocket#read_nonblock 会引发 IO::WaitWritable 而不是阻塞。 因此,调用方应等待准备好进行写入,如上面的示例所示。
当多个进程从流中读取时,非阻塞方法和 IO.select 的组合也适用于 tty、管道套接字套接字等流。
最后,Linux 内核开发人员不保证 select(2) 的可读性意味着后续 read(2) 的可读性,即使对于单个进程也是如此;请参阅 select(2)
在 IO#readpartial 之前调用 IO.select 通常效果很好。 但是,这不是使用 IO.select 的最佳方式。
select(2) 通知的可写性不显示有多少字节可写。IO#write 方法会阻塞,直到写入给定的整个字符串。 因此,在 IO.select 通知可写性之后,IO#write(两个或多个字节) 可能会阻塞。 需要 IO#write_nonblock 以避免阻塞。
可以使用 write_nonblock 和 IO.select 模拟阻塞写入 (write),如下所示: IO::WaitReadable 也应该在 OpenSSL::SSL::SSLSocket 中为 SSL 重新协商进行救援。
while 0 < string.bytesize begin written = io_like.write_nonblock(string) rescue IO::WaitReadable IO.select([io_like]) retry rescue IO::WaitWritable IO.select(nil, [io_like]) retry end string = string.byteslice(written..-1) end
示例
rp, wp = IO.pipe mesg = "ping " 100.times { # IO.select follows IO#read. Not the best way to use IO.select. rs, ws, = IO.select([rp], [wp]) if r = rs[0] ret = r.read(5) print ret case ret when /ping/ mesg = "pong\n" when /pong/ mesg = "ping " end end if w = ws[0] w.write(mesg) end }
输出
ping pong ping pong ping pong (snipped) ping
源代码
static VALUE
rb_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);
}
以给定的模式和权限打开给定路径的文件;返回整数文件描述符。
如果要使文件可读,则该文件必须存在;如果要使文件可写且不存在,则会使用给定的权限创建该文件
File.write('t.tmp', '') # => 0 IO.sysopen('t.tmp') # => 8 IO.sysopen('t.tmp', 'w') # => 9
源代码
static VALUE
rb_io_s_try_convert(VALUE dummy, VALUE io)
{
return rb_io_check_io(io);
}
尝试通过方法 to_io 将 object 转换为 IO 对象;如果成功,则返回新的 IO 对象,否则返回 nil
IO.try_convert(STDOUT) # => #<IO:<STDOUT>> IO.try_convert(ARGF) # => #<IO:<STDIN>> IO.try_convert('STDOUT') # => nil
源代码
static VALUE
rb_io_s_write(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 0);
}
打开流,将给定的 data 写入流,然后关闭流;返回写入的字节数。
当从类 IO (而不是 IO 的子类)调用时,如果使用不受信任的输入调用此方法,则此方法具有潜在的安全漏洞;请参阅 命令注入。
第一个参数必须是字符串,该字符串是文件的路径。
仅给定参数 path 时,将给定的 data 写入该路径的文件
IO.write('t.tmp', 'abc') # => 3 File.read('t.tmp') # => "abc"
如果 offset 为零(默认值),则会覆盖该文件
IO.write('t.tmp', 'A') # => 1 File.read('t.tmp') # => "A"
如果 offset 在文件内容中,则会部分覆盖该文件
IO.write('t.tmp', 'abcdef') # => 3 File.read('t.tmp') # => "abcdef" # Offset within content. IO.write('t.tmp', '012', 2) # => 3 File.read('t.tmp') # => "ab012f"
如果 offset 在文件内容之外,则文件将使用空字符 "\u0000" 填充
IO.write('t.tmp', 'xyz', 10) # => 3 File.read('t.tmp') # => "ab012f\u0000\u0000\u0000\u0000xyz"
可选关键字参数 opts 指定
公共实例方法
源代码
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
rb_io_write(io, str);
return io;
}
将给定的 object 写入 self,该对象必须打开才能写入(请参阅 访问模式);返回 self;如果 object 不是字符串,则通过方法 to_s 进行转换
$stdout << 'Hello' << ', ' << 'World!' << "\n" $stdout << 'foo' << :bar << 2 << "\n"
输出
Hello, World! foobar2
源代码
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
}
调用 Posix 系统调用 posix_fadvise(2),该系统调用声明以特定方式访问当前文件中数据的意图。
参数和结果取决于平台。
相关数据由以下指定
-
offset:数据第一个字节的偏移量。 -
len:要访问的字节数;如果len为零或大于剩余的字节数,则将访问所有剩余的字节。
参数 advice 是以下符号之一
-
:normal:该应用程序没有关于其对指定数据的访问模式的建议。 如果没有为打开的文件提供建议,则这是默认假设。 -
:sequential:应用程序希望按顺序访问指定的数据(读取较低偏移量的数据,然后再读取较高的偏移量的数据)。 -
:random:将以随机顺序访问指定的数据。 -
:noreuse:指定的数据将仅访问一次。 -
:willneed:指定的数据将在不久的将来访问。 -
:dontneed:指定的数据在不久的将来不会访问。
并非在所有平台上都实现。
源代码
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;
}
设置自动关闭标志。
f = File.open(File::NULL) IO.for_fd(f.fileno).close f.gets # raises Errno::EBADF f = File.open(File::NULL) g = IO.for_fd(f.fileno) g.autoclose = false g.close f.gets # won't cause Errno::EBADF
源代码
static VALUE
rb_io_autoclose_p(VALUE io)
{
rb_io_t *fptr = RFILE(io)->fptr;
rb_io_check_closed(fptr);
return RBOOL(!(fptr->mode & FMODE_EXTERNAL));
}
如果 ios 的底层文件描述符将在其最终化或调用 close 时关闭,则返回 true,否则返回 false。
源代码
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;
}
在输出控制台上发出嘟嘟声。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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;
}
将流的数据模式设置为二进制(请参阅 数据模式)。
流的数据模式可能不会从二进制更改为文本。
源代码
static VALUE
rb_io_binmode_p(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return RBOOL(fptr->mode & FMODE_BINMODE);
}
如果流处于二进制模式,则返回 true,否则返回 false。 请参阅 数据模式。
源代码
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;
}
在控制台输入事件排队时产生。
此方法仅适用于 Windows。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_clear_screen(VALUE io)
{
console_erase_screen(io, INT2FIX(2));
console_goto(io, INT2FIX(0), INT2FIX(0));
return io;
}
清除整个屏幕并将光标移动到左上角。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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;
}
如果流为读取或写入(或两者)而打开,则关闭该流;返回 nil。请参阅 打开和关闭的流。
如果流为写入而打开,则在关闭之前将所有缓冲的写入刷新到操作系统。
如果流是由 IO.popen 打开的,则设置全局变量 $?(子进程退出状态)。
关闭已经关闭的 IO 对象不是错误。它只会返回 nil。
示例
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close puts $? puts pipe.closed? end
输出
false pid 13760 exit 0 true
源代码
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;
}
设置执行时关闭标志。
f = File.open(File::NULL) f.close_on_exec = true system("cat", "/proc/self/fd/#{f.fileno}") # cat: /proc/self/fd/3: No such file or directory f.closed? #=> false
自 Ruby 2.0.0 起,Ruby 默认设置所有文件描述符的执行时关闭标志。因此,您无需自行设置。此外,如果另一个线程使用 fork() 和 exec()(例如通过 system() 方法),则取消设置执行时关闭标志可能会导致文件描述符泄漏。如果确实需要文件描述符继承到子进程,请使用 spawn() 的参数,例如 fd=>fd。
源代码
static VALUE
rb_io_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;
}
如果流将在执行时关闭,则返回 true,否则返回 false
f = File.open('t.txt') f.close_on_exec? # => true f.close_on_exec = false f.close_on_exec? # => false f.close
源代码
static VALUE
rb_io_close_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);
}
如果流为读取而打开,则关闭该流以进行读取;返回 nil。请参阅 打开和关闭的流。
如果流是由 IO.popen 打开的,并且也关闭了写入,则设置全局变量 $?(子进程退出状态)。
示例
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close_write puts pipe.closed? pipe.close_read puts $? puts pipe.closed? end
输出
false false pid 14748 exit 0 true
相关方法:IO#close、IO#close_write、IO#closed?。
源代码
static VALUE
rb_io_close_write(VALUE io)
{
rb_io_t *fptr;
VALUE write_io;
write_io = GetWriteIO(io);
fptr = rb_io_get_fptr(rb_io_taint_check(write_io));
if (fptr->fd < 0) return Qnil;
if (is_socket(fptr->fd, fptr->pathv)) {
#ifndef SHUT_WR
# define SHUT_WR 1
#endif
if (shutdown(fptr->fd, SHUT_WR) < 0)
rb_sys_fail_path(fptr->pathv);
fptr->mode &= ~FMODE_WRITABLE;
if (!(fptr->mode & FMODE_READABLE))
return rb_io_close(write_io);
return Qnil;
}
if ((fptr->mode & (FMODE_DUPLEX|FMODE_READABLE)) == FMODE_READABLE) {
rb_raise(rb_eIOError, "closing non-duplex IO for writing");
}
if (io != write_io) {
fptr = rb_io_get_fptr(rb_io_taint_check(io));
fptr->tied_io_for_writing = 0;
}
rb_io_close(write_io);
return Qnil;
}
如果流为写入而打开,则关闭该流以进行写入;返回 nil。请参阅 打开和关闭的流。
在关闭之前将所有缓冲的写入刷新到操作系统。
如果流是由 IO.popen 打开的,并且也关闭了读取,则设置全局变量 $?(子进程退出状态)。
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close_read puts pipe.closed? pipe.close_write puts $? puts pipe.closed? end
输出
false false pid 15044 exit 0 true
相关方法:IO#close、IO#close_read、IO#closed?。
源代码
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);
}
如果流同时关闭了读取和写入,则返回 true,否则返回 false。请参阅 打开和关闭的流。
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close_read puts pipe.closed? pipe.close_write puts pipe.closed? end
输出
false false true
源代码
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);
}
返回表示当前控制台模式的数据。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_conmode_set(VALUE io, VALUE mode)
{
conmode *t, r;
int fd = GetReadFD(io);
TypedData_Get_Struct(mode, conmode, &conmode_type, t);
r = *t;
if (!setattr(fd, &r)) sys_fail(io);
return mode;
}
将控制台模式设置为 mode。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_cooked(VALUE io)
{
return ttymode(io, rb_yield, io, set_cookedmode, NULL);
}
在 cooked 模式下产生 self。
STDIN.cooked(&:gets)
将读取并返回带有回显和行编辑的行。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_set_cooked(VALUE io)
{
conmode t;
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
set_cookedmode(&t, NULL);
if (!setattr(fd, &t)) sys_fail(io);
return io;
}
启用 cooked 模式。
如果需要恢复终端模式,请使用 io.cooked { … }。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_cursor_pos(VALUE io)
{
#ifdef _WIN32
rb_console_size_t ws;
int fd = GetWriteFD(io);
if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
#else
static const struct query_args query = {"\033[6n", 0};
VALUE resp = console_vt_response(0, 0, io, &query);
VALUE row, column, term;
unsigned int r, c;
if (!RB_TYPE_P(resp, T_ARRAY) || RARRAY_LEN(resp) != 3) return Qnil;
term = RARRAY_AREF(resp, 2);
if (!RB_TYPE_P(term, T_STRING) || RSTRING_LEN(term) != 1) return Qnil;
if (RSTRING_PTR(term)[0] != 'R') return Qnil;
row = RARRAY_AREF(resp, 0);
column = RARRAY_AREF(resp, 1);
rb_ary_resize(resp, 2);
r = NUM2UINT(row) - 1;
c = NUM2UINT(column) - 1;
RARRAY_ASET(resp, 0, INT2NUM(r));
RARRAY_ASET(resp, 1, INT2NUM(c));
return resp;
#endif
}
以两个整数的数组形式返回当前光标位置(行,列)
io.cursor # => [3, 5]
您必须 require ‘io/console’ 才能使用此方法。
源代码
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));
}
与 io.goto(行, 列) 相同
请参阅 IO#goto。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_cursor_down(VALUE io, VALUE val)
{
return console_move(io, +NUM2INT(val), 0);
}
将光标向下移动 n 行。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_cursor_left(VALUE io, VALUE val)
{
return console_move(io, 0, -NUM2INT(val));
}
将光标向左移动 n 列。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_cursor_right(VALUE io, VALUE val)
{
return console_move(io, 0, +NUM2INT(val));
}
将光标向右移动 n 列。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_cursor_up(VALUE io, VALUE val)
{
return console_move(io, -NUM2INT(val), 0);
}
将光标向上移动 n 行。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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;
}
使用流中的每个字节 (0..255) 调用给定的块;返回 self。请参阅 字节 IO。
f = File.new('t.rus') a = [] f.each_byte {|b| a << b } a # => [209, 130, 208, 181, 209, 129, 209, 130] f.close
如果没有给出块,则返回 Enumerator。
源代码
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;
}
使用流中的每个字符调用给定的块;返回 self。请参阅 字符 IO。
f = File.new('t.rus') a = [] f.each_char {|c| a << c.ord } a # => [1090, 1077, 1089, 1090] f.close
如果没有给出块,则返回 Enumerator。
源代码
static VALUE
rb_io_each_codepoint(VALUE io)
{
rb_io_t *fptr;
rb_encoding *enc;
unsigned int c;
int r, n;
RETURN_ENUMERATOR(io, 0, 0);
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
READ_CHECK(fptr);
if (NEED_READCONV(fptr)) {
SET_BINARY_MODE(fptr);
r = 1; /* no invalid char yet */
for (;;) {
make_readconv(fptr, 0);
for (;;) {
if (fptr->cbuf.len) {
if (fptr->encs.enc)
r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
fptr->encs.enc);
else
r = ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1);
if (!MBCLEN_NEEDMORE_P(r))
break;
if (fptr->cbuf.len == fptr->cbuf.capa) {
rb_raise(rb_eIOError, "too long character");
}
}
if (more_char(fptr) == MORE_CHAR_FINISHED) {
clear_readconv(fptr);
if (!MBCLEN_CHARFOUND_P(r)) {
enc = fptr->encs.enc;
goto invalid;
}
return io;
}
}
if (MBCLEN_INVALID_P(r)) {
enc = fptr->encs.enc;
goto invalid;
}
n = MBCLEN_CHARFOUND_LEN(r);
if (fptr->encs.enc) {
c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
fptr->encs.enc);
}
else {
c = (unsigned char)fptr->cbuf.ptr[fptr->cbuf.off];
}
fptr->cbuf.off += n;
fptr->cbuf.len -= n;
rb_yield(UINT2NUM(c));
rb_io_check_byte_readable(fptr);
}
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
enc = io_input_encoding(fptr);
while (io_fillbuf(fptr) >= 0) {
r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off,
fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
if (MBCLEN_CHARFOUND_P(r) &&
(n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
c = rb_enc_codepoint(fptr->rbuf.ptr+fptr->rbuf.off,
fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
fptr->rbuf.off += n;
fptr->rbuf.len -= n;
rb_yield(UINT2NUM(c));
}
else if (MBCLEN_INVALID_P(r)) {
goto invalid;
}
else if (MBCLEN_NEEDMORE_P(r)) {
char cbuf[8], *p = cbuf;
int more = MBCLEN_NEEDMORE_LEN(r);
if (more > numberof(cbuf)) goto invalid;
more += n = fptr->rbuf.len;
if (more > numberof(cbuf)) goto invalid;
while ((n = (int)read_buffered_data(p, more, fptr)) > 0 &&
(p += n, (more -= n) > 0)) {
if (io_fillbuf(fptr) < 0) goto invalid;
if ((n = fptr->rbuf.len) > more) n = more;
}
r = rb_enc_precise_mbclen(cbuf, p, enc);
if (!MBCLEN_CHARFOUND_P(r)) goto invalid;
c = rb_enc_codepoint(cbuf, p, enc);
rb_yield(UINT2NUM(c));
}
else {
continue;
}
rb_io_check_byte_readable(fptr);
}
return io;
invalid:
rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
UNREACHABLE_RETURN(Qundef);
}
使用流中的每个代码点调用给定的块;返回 self
f = File.new('t.rus') a = [] f.each_codepoint {|c| a << c } a # => [1090, 1077, 1089, 1090] f.close
如果没有给出块,则返回 Enumerator。
相关方法:IO#each_byte、IO#each_char。
使用从流中读取的每个剩余行调用该块;返回 self。如果已到达流的末尾,则不执行任何操作;请参阅 行 IO。
如果没有给出任何参数,则按照行分隔符 $/ 确定读取行
f = File.new('t.txt') f.each_line {|line| p line } f.each_line {|line| fail 'Cannot happen' } f.close
输出
"First line\n" "Second line\n" "\n" "Fourth line\n" "Fifth line\n"
如果仅给出字符串参数 sep,则按照行分隔符 sep 确定读取行;请参阅 行分隔符
f = File.new('t.txt') f.each_line('li') {|line| p line } f.close
输出
"First li" "ne\nSecond li" "ne\n\nFourth li" "ne\nFifth li" "ne\n"
将遵循 sep 的两个特殊值
f = File.new('t.txt') # Get all into one string. f.each_line(nil) {|line| p line } f.close
输出
"First line\nSecond line\n\nFourth line\nFifth line\n" f.rewind # Get paragraphs (up to two line separators). f.each_line('') {|line| p line }
输出
"First line\nSecond line\n\n" "Fourth line\nFifth line\n"
如果仅给出整数参数 limit,则限制每行的字节数;请参阅 行限制
f = File.new('t.txt') f.each_line(8) {|line| p line } f.close
输出
"First li" "ne\n" "Second l" "ine\n" "\n" "Fourth l" "ine\n" "Fifth li" "ne\n"
如果给定了参数 sep 和 limit,则结合这两种行为(请参阅 行分隔符和行限制)。
可选的关键字参数 chomp 指定是否省略行分隔符
f = File.new('t.txt') f.each_line(chomp: true) {|line| p line } f.close
输出
"First line" "Second line" "" "Fourth line" "Fifth line"
如果没有给出块,则返回 Enumerator。
源代码
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;
}
启用/禁用回显。在某些平台上,此标志和 raw/cooked 模式的所有组合可能无效。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_echo_p(VALUE io)
{
conmode t;
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
return echo_p(&t) ? Qtrue : Qfalse;
}
如果启用了回显,则返回 true。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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);
}
如果流位于其末尾,则返回 true,否则返回 false;请参阅 位置
f = File.open('t.txt') f.eof # => false f.seek(0, :END) # => 0 f.eof # => true f.close
除非流为读取而打开,否则会引发异常;请参阅 模式。
如果 self 是管道或套接字之类的流,则此方法会阻塞,直到另一端发送一些数据或将其关闭
r, w = IO.pipe Thread.new { sleep 1; w.close } r.eof? # => true # After 1-second wait. r, w = IO.pipe Thread.new { sleep 1; w.puts "a" } r.eof? # => false # After 1-second wait. r, w = IO.pipe r.eof? # blocks forever
请注意,此方法会将数据读取到输入字节缓冲区。因此,IO#sysread 的行为可能与 IO#eof? 不符合您的预期,除非您首先调用 IO#rewind(这对于某些流不可用)。
源代码
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;
}
根据 mode 擦除光标处的行。mode 可以是以下任一项:0:在光标之后 1:在光标之前 2:整行
您必须 require ‘io/console’ 才能使用此方法。
源代码
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;
}
根据 mode 擦除光标处的屏幕。mode 可以是以下任一项:0:在光标之后 1:在光标之前 2:整个屏幕
您必须 require ‘io/console’ 才能使用此方法。
源代码
# 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
expect 库添加了实例方法 IO#expect,该方法类似于 TCL expect 扩展。
要使用此方法,您必须 require expect
require 'expect'
从 IO 读取,直到给定的 pattern 匹配或 timeout 超时。
它返回一个数组,其中包含读取的缓冲区,后跟匹配项。如果给定一个块,则结果将产生到该块并返回 nil。
当在没有块的情况下调用时,它会等待,直到从 IO 获取与给定 pattern 匹配的输入,或者经过指定为超时的时间。当从 IO 获取到该模式时,将返回一个数组。数组的第一个元素是从 IO 获取的整个字符串,直到该模式匹配,后跟指示与正则表达式中的锚匹配的模式的元素。
可选的 timeout 参数以秒为单位定义等待模式的总时间。如果超时或找到 eof,则返回或产生 nil。但是,超时会话中的缓冲区会保留给下一个 expect 调用。默认超时为 9999999 秒。
源代码
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));
}
返回表示流编码的 Encoding 对象,如果流处于写入模式并且未指定编码,则返回 nil。
请参阅 编码。
源代码
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);
}
调用 Posix 系统调用 fcntl(2),该调用提供了一种发出低级命令来控制或查询面向文件的 I/O 流的机制。参数和结果与平台相关。
如果 argument 是一个数字,则直接传递其值;如果它是一个字符串,则将其解释为字节的二进制序列。(Array#pack 可能是构建此字符串的有用方法。)
并非在所有平台上都实现。
源代码
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);
}
立即通过操作系统的:fdatasync(2)(如果支持)将流中缓冲的所有数据写入磁盘,如果不支持,则通过 fsync(2)(如果支持)写入磁盘;否则引发异常。
源代码
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);
}
返回流的整数文件描述符
$stdin.fileno # => 0 $stdout.fileno # => 1 $stderr.fileno # => 2 File.open('t.txt').fileno # => 10 f.close
源代码
VALUE
rb_io_flush(VALUE io)
{
return rb_io_flush_raw(io, 1);
}
将 self 中缓冲的数据刷新到操作系统(但不一定会刷新操作系统中缓冲的数据)
$stdout.print 'no newline' # Not necessarily flushed. $stdout.flush # Flushed.
源代码
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);
}
通过操作系统的 fsync(2) 立即将流中缓冲的所有数据写入磁盘。
请注意此差异
如果操作系统不支持 fsync(2),则引发异常。
源代码
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);
}
从流中读取并返回下一个字节(范围 0..255);如果已到达流的末尾,则返回 nil。请参阅 字节 IO。
f = File.open('t.txt') f.getbyte # => 70 f.close f = File.open('t.rus') f.getbyte # => 209 f.close
相关方法:IO#readbyte(可能会引发 EOFError)。
源代码
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);
}
从流中读取并返回下一个 1 字符的字符串;如果已到达流的末尾,则返回 nil。请参阅 字符 IO。
f = File.open('t.txt') f.getc # => "F" f.close f = File.open('t.rus') f.getc.ord # => 1090 f.close
相关方法:IO#readchar(可能会引发 EOFError)。
源代码
static VALUE
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
}
在原始模式下读取并返回一个字符。
有关参数的详细信息,请参阅 IO#raw。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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);
}
读取并返回一行,但不回显。除非它是 nil,否则打印 prompt。
从返回的字符串中删除终止读取行的换行符,请参阅 String#chomp!。
您必须 require ‘io/console’ 才能使用此方法。
require 'io/console' IO::console.getpass("Enter password:") Enter password: # => "mypassword"
源代码
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
将遵循 sep 的两个特殊值
f = File.new('t.txt') # Get all. f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.rewind # Get paragraph (up to two line separators). f.gets('') # => "First line\nSecond line\n\n" f.close
如果仅给出整数参数 limit,则限制该行的字节数;请参阅 行限制
# No more than one line. File.open('t.txt') {|f| f.gets(10) } # => "First line" File.open('t.txt') {|f| f.gets(11) } # => "First line\n" File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
如果给定了参数 sep 和 limit,则结合这两种行为(请参阅 行分隔符和行限制)。
可选的关键字参数 chomp 指定是否省略行分隔符
f = File.open('t.txt') # Chomp the lines. f.gets(chomp: true) # => "First line" f.gets(chomp: true) # => "Second line" f.gets(chomp: true) # => "" f.gets(chomp: true) # => "Fourth line" f.gets(chomp: true) # => "Fifth line" f.gets(chomp: true) # => nil f.close
源代码
static VALUE
console_goto(VALUE io, VALUE y, VALUE x)
{
#ifdef _WIN32
COORD pos;
int fd = GetWriteFD(io);
pos.X = NUM2UINT(x);
pos.Y = NUM2UINT(y);
if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
#else
rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
#endif
return io;
}
Set 将光标位置设置在 line 和 column 指定的位置。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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;
}
Set 将光标位置设置在当前行的 column 指定的位置。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_iflush(VALUE io)
{
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
int fd = GetReadFD(io);
if (tcflush(fd, TCIFLUSH)) sys_fail(io);
#endif
return io;
}
刷新内核中的输入缓冲区。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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
源代码
源代码
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 是整数,则直接传递;如果它是字符串,则将其解释为二进制字节序列。
并非在所有平台上都实现。
源代码
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’ 才能使用此方法。
源代码
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
源代码
static VALUE
rb_io_lineno(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
return INT2NUM(fptr->lineno);
}
返回流的当前行号;请参阅 行号。
源代码
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;
}
设置并返回流的行号;请参阅 行号。
源代码
static VALUE
console_noecho(VALUE io)
{
return ttymode(io, rb_yield, io, set_noecho, NULL);
}
在禁用回显的情况下产生 self。
STDIN.noecho(&:gets)
将读取并返回一行,而不进行回显。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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);
}
在非阻塞模式下产生 self。
当提供 false 作为参数时,将在阻塞模式下产生 self。在执行块后,将恢复原始模式。
源代码
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/STDERR 处于非阻塞模式。(STDIN、STDOUT 和 STDERR 连接到终端。因此,使其中一个处于非阻塞模式会影响其他两个。)因此,cat 命令尝试从标准输入读取,这会导致“Resource temporarily unavailable”错误 (EAGAIN)。
% ruby -e '
STDOUT.write_nonblock("foo\n")'; cat
foo
cat: -: Resource temporarily unavailable
清除该标志使 cat 命令的行为正常。(cat 命令等待来自标准输入的输入。)
% ruby -rio/nonblock -e '
END { STDOUT.nonblock = false }
STDOUT.write_nonblock("foo")
'; cat
foo
源代码
static VALUE
rb_io_nonblock_p(VALUE io)
{
if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK)
return Qtrue;
return Qfalse;
}
如果 IO 对象处于非阻塞模式,则返回 true。
源代码
static VALUE
io_nread(VALUE io)
{
rb_io_t *fptr;
int len;
ioctl_arg n;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
len = rb_io_read_pending(fptr);
if (len > 0) return INT2FIX(len);
#ifdef HAVE_RB_IO_DESCRIPTOR
int fd = rb_io_descriptor(io);
#else
int fd = fptr->fd;
#endif
if (!FIONREAD_POSSIBLE_P(fd)) return INT2FIX(0);
if (ioctl(fd, FIONREAD, &n)) return INT2FIX(0);
if (n > 0) return ioctl_arg2num(n);
return INT2FIX(0);
}
返回可以在不阻塞的情况下读取的字节数。如果没有可用信息,则返回零。
您必须 require ‘io/wait’ 才能使用此方法。
源代码
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’ 才能使用此方法。
源代码
VALUE
rb_io_path(VALUE io)
{
rb_io_t *fptr = RFILE(io)->fptr;
if (!fptr)
return Qnil;
return rb_obj_dup(fptr->pathv);
}
返回与 IO 关联的路径,如果 IO 没有关联的路径,则返回 nil。不保证该路径存在于文件系统中。
$stdin.path # => "<STDIN>" File.open("testfile") {|f| f.path} # => "testfile"
源代码
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 }
源代码
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
源代码
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);
}
查找给定的 new_position(以字节为单位);请参阅 位置
f = File.open('t.txt') f.tell # => 0 f.pos = 20 # => 20 f.tell # => 20 f.close
源代码
static VALUE
rb_io_pread(int argc, VALUE *argv, VALUE io)
{
VALUE len, offset, str;
rb_io_t *fptr;
ssize_t n;
struct prdwr_internal_arg arg;
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
某些平台上不可用。
源代码
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;
}
如果按下 key,则返回 true。key 可以是虚拟键代码或其名称(String 或 Symbol),没有“VK_”前缀。
此方法仅适用于 Windows。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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
源代码
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 的详细信息,请参阅 格式规范。
源代码
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
源代码
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>$\</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"
源代码
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)rb_io_blocking_region_wait(fptr, internal_pwrite_func, &arg, RUBY_IO_WRITABLE);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return SSIZET2NUM(n);
}
行为类似于 IO#write,不同之处在于它
-
在给定的
offset(以字节为单位)处写入。 -
忽略并不会修改流的位置(请参阅 位置)。
-
绕过流中的任何用户空间缓冲。
由于此方法不干扰流的状态(特别是其位置),因此 pwrite 允许多个线程和进程使用同一 IO 对象在不同的偏移量处进行写入。
f = File.open('t.tmp', 'w+') # Write 6 bytes at offset 3. f.pwrite('ABCDEF', 3) # => 6 f.rewind f.read # => "\u0000\u0000\u0000ABCDEF" f.close
某些平台上不可用。
源代码
static VALUE
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);
}
在原始模式下产生 self,并返回块的结果。
STDIN.raw(&:gets)
将读取并返回一行,而不进行回显和行编辑。
参数 min 指定在执行读取操作时应接收的最小字节数。(默认值:1)
参数 time 指定以秒为单位的超时时间,精度为 1/10 秒。(默认值:0)
如果参数 intr 为 true,则启用 break、interrupt、quit 和 suspend 特殊字符。
有关详细信息,请参阅 termios 的手册页。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_set_raw(int argc, VALUE *argv, VALUE io)
{
conmode t;
rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
set_rawmode(&t, optp);
if (!setattr(fd, &t)) sys_fail(io);
return io;
}
启用原始模式,并返回 io。
如果需要返回终端模式,请使用 io.raw { ... }。
有关参数的详细信息,请参阅 IO#raw。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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。
源代码
# File io.rb, line 62 def read_nonblock(len, buf = nil, exception: true) Primitive.io_read_nonblock(len, buf, exception) end
使用 read(2) 系统调用从 ios 读取至多 maxlen 个字节,在读取之前会为底层文件描述符设置 O_NONBLOCK 标志。
如果存在可选参数 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 异常。
源代码
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 异常。请参阅 Byte IO。
f = File.open('t.txt') f.readbyte # => 70 f.close f = File.open('t.rus') f.readbyte # => 209 f.close
相关:IO#getbyte(不会引发 EOFError 异常)。
源代码
static VALUE
rb_io_readchar(VALUE io)
{
VALUE c = rb_io_getc(io);
if (NIL_P(c)) {
rb_eof_error();
}
return c;
}
从流中读取并返回下一个 1 字符的字符串;如果已到达流的末尾,则引发 EOFError 异常。请参阅 Character IO。
f = File.open('t.txt') f.readchar # => "F" f.close f = File.open('t.rus') f.readchar.ord # => 1090 f.close
源代码
源代码
static VALUE
rb_io_readlines(int argc, VALUE *argv, VALUE io)
{
struct getline_arg args;
prepare_getline_args(argc, argv, &args, io);
return io_readlines(&args, io);
}
从流中读取并返回所有剩余行;不修改 $_。请参阅 Line 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;请参阅 Line Separator。
f = File.new('t.txt') f.readlines('li') # => ["First li", "ne\nSecond li", "ne\n\nFourth li", "ne\nFifth li", "ne\n"] f.close
将遵循 sep 的两个特殊值
f = File.new('t.txt') # Get all into one string. f.readlines(nil) # => ["First line\nSecond line\n\nFourth line\nFifth line\n"] # Get paragraphs (up to two line separators). f.rewind f.readlines('') # => ["First line\nSecond line\n\n", "Fourth line\nFifth line\n"] f.close
如果仅给出整数参数 limit,则限制每行的字节数;请参阅 行限制
f = File.new('t.txt') f.readlines(8) # => ["First li", "ne\n", "Second l", "ine\n", "\n", "Fourth l", "ine\n", "Fifth li", "ne\n"] f.close
如果给定了参数 sep 和 limit,则结合这两种行为(请参阅 行分隔符和行限制)。
可选的关键字参数 chomp 指定是否省略行分隔符
f = File.new('t.txt') f.readlines(chomp: true) # => ["First line", "Second line", "", "Fourth line", "Fifth line"] f.close
源代码
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
此方法对于管道、套接字或 tty 等流非常有用。仅当没有数据立即可用时才会阻塞。这意味着只有当以下所有条件都为真时才会阻塞:
-
流中的字节缓冲区为空。
-
流的内容为空。
-
流未到达 EOF。
当阻塞时,该方法会等待流上出现更多数据或 EOF。
-
如果读取了更多数据,则该方法返回数据。
-
如果到达 EOF,则该方法会引发
EOFError异常。
当未阻塞时,该方法会立即响应。
-
如果缓冲区中有任何数据,则返回缓冲区中的数据。
-
否则,如果流中有任何数据,则返回流中的数据。
-
否则,如果流已到达 EOF,则引发
EOFError异常。
请注意,此方法类似于 sysread。区别在于:
-
它不会导致 Errno::EWOULDBLOCK 和 Errno::EINTR。当 readpartial 通过 read 系统调用遇到 EWOULDBLOCK 和 EINTR 时,readpartial 会重试系统调用。
后者意味着 readpartial 对非阻塞标志不敏感。它会在 IO#sysread 导致 Errno::EWOULDBLOCK 的情况下阻塞,就像 fd 处于阻塞模式一样。
示例
# # Returned Buffer Content Pipe Content r, w = IO.pipe # w << 'abc' # "" "abc". r.readpartial(4096) # => "abc" "" "" r.readpartial(4096) # (Blocks because buffer and pipe are empty.) # # Returned Buffer Content Pipe Content r, w = IO.pipe # w << 'abc' # "" "abc" w.close # "" "abc" EOF r.readpartial(4096) # => "abc" "" EOF r.readpartial(4096) # raises EOFError # # Returned Buffer Content Pipe Content r, w = IO.pipe # w << "abc\ndef\n" # "" "abc\ndef\n" r.gets # => "abc\n" "def\n" "" w << "ghi\n" # "def\n" "ghi\n" r.readpartial(4096) # => "def\n" "" "ghi\n" r.readpartial(4096) # => "ghi\n" "" ""
源代码
static VALUE
io_ready_p(VALUE io)
{
rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
struct timeval tv = {0, 0};
#endif
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
#ifndef HAVE_RB_IO_WAIT
return wait_for_single_fd(fptr, RB_WAITFD_IN, &tv) ? Qtrue : Qfalse;
#else
return io_wait_event(io, RUBY_IO_READABLE, RB_INT2NUM(0), 1);
#endif
}
如果输入在不阻塞的情况下可用,则返回 truthy 值,否则返回 falsy 值。
您必须 require ‘io/wait’ 才能使用此方法。
源代码
static VALUE
rb_io_reopen(int argc, VALUE *argv, VALUE file)
{
VALUE fname, nmode, opt;
int oflags;
rb_io_t *fptr;
if (rb_scan_args(argc, argv, "11:", &fname, &nmode, &opt) == 1) {
VALUE tmp = rb_io_check_io(fname);
if (!NIL_P(tmp)) {
return io_reopen(file, tmp);
}
}
FilePathValue(fname);
rb_io_taint_check(file);
fptr = RFILE(file)->fptr;
if (!fptr) {
fptr = RFILE(file)->fptr = ZALLOC(rb_io_t);
}
if (!NIL_P(nmode) || !NIL_P(opt)) {
int fmode;
struct rb_io_encoding convconfig;
rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
if (RUBY_IO_EXTERNAL_P(fptr) &&
((fptr->mode & FMODE_READWRITE) & (fmode & FMODE_READWRITE)) !=
(fptr->mode & FMODE_READWRITE)) {
rb_raise(rb_eArgError,
"%s can't change access mode from \"%s\" to \"%s\"",
PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
rb_io_fmode_modestr(fmode));
}
fptr->mode = fmode;
fptr->encs = convconfig;
}
else {
oflags = rb_io_fmode_oflags(fptr->mode);
}
fptr->pathv = fname;
if (fptr->fd < 0) {
fptr->fd = rb_sysopen(fptr->pathv, oflags, 0666);
fptr->stdio_file = 0;
return file;
}
if (fptr->mode & FMODE_WRITABLE) {
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
}
fptr->rbuf.off = fptr->rbuf.len = 0;
if (fptr->stdio_file) {
int e = rb_freopen(rb_str_encode_ospath(fptr->pathv),
rb_io_oflags_modestr(oflags),
fptr->stdio_file);
if (e) rb_syserr_fail_path(e, fptr->pathv);
fptr->fd = fileno(fptr->stdio_file);
rb_fd_fix_cloexec(fptr->fd);
#ifdef USE_SETVBUF
if (setvbuf(fptr->stdio_file, NULL, _IOFBF, 0) != 0)
rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
#endif
if (fptr->stdio_file == stderr) {
if (setvbuf(fptr->stdio_file, NULL, _IONBF, BUFSIZ) != 0)
rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
}
else if (fptr->stdio_file == stdout && isatty(fptr->fd)) {
if (setvbuf(fptr->stdio_file, NULL, _IOLBF, BUFSIZ) != 0)
rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
}
}
else {
int tmpfd = rb_sysopen(fptr->pathv, oflags, 0666);
int err = 0;
if (rb_cloexec_dup2(tmpfd, fptr->fd) < 0)
err = errno;
(void)close(tmpfd);
if (err) {
rb_syserr_fail_path(err, fptr->pathv);
}
}
return file;
}
将流与另一个流重新关联,该流可能属于不同的类。此方法可用于将现有流重定向到新的目标。
如果给定参数 other_io,则与该流重新关联。
# Redirect $stdin from a file. f = File.open('t.txt') $stdin.reopen(f) f.close # Redirect $stdout to a file. f = File.open('t.tmp', 'w') $stdout.reopen(f) f.close
如果给定参数 path,则与该文件路径的新流重新关联。
$stdin.reopen('t.txt') $stdout.reopen('t.tmp', 'w')
可选关键字参数 opts 指定
源代码
static VALUE
rb_io_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);
}
将流重新定位到其开头,并将位置和行号都设置为零;请参阅 Position 和 Line Number。
f = File.open('t.txt') f.tell # => 0 f.lineno # => 0 f.gets # => "First line\n" f.tell # => 12 f.lineno # => 1 f.rewind # => 0 f.tell # => 0 f.lineno # => 0 f.close
请注意,此方法不能用于管道、tty 和套接字等流。
源代码
static VALUE
console_scroll_backward(VALUE io, VALUE val)
{
return console_scroll(io, -NUM2INT(val));
}
将整个滚动向后滚动 n 行。
您必须 require ‘io/console’ 才能使用此方法。
源代码
static VALUE
console_scroll_forward(VALUE io, VALUE val)
{
return console_scroll(io, +NUM2INT(val));
}
将整个滚动向前滚动 n 行。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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 (请参阅 Position)和常量 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
源代码
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;
}
请参阅 编码。
如果给定参数 ext_enc,则它必须是 Encoding 对象或具有编码名称的 String;它被分配为流的编码。
如果给定参数 int_enc,则它必须是 Encoding 对象或具有编码名称的 String;它被分配为内部字符串的编码。
如果给定参数 'ext_enc:int_enc',则它是一个包含两个冒号分隔的编码名称的字符串;相应的 Encoding 对象被分配为流的外部和内部编码。
如果字符串的外部编码是二进制/ASCII-8BIT,则字符串的内部编码将设置为 nil,因为不需要转码。
可选的关键字参数 enc_opts 指定 Encoding options。
源代码
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
如果流不是二进制模式或其编码已设置,则引发异常。
源代码
static VALUE
rb_io_stat(VALUE obj)
{
rb_io_t *fptr;
struct stat st;
GetOpenFile(obj, fptr);
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail_path(fptr->pathv);
}
return rb_stat_new(&st);
}
以 File::Stat 类型的对象形式返回 ios 的状态信息。
f = File.new("testfile") s = f.stat "%o" % s.mode #=> "100644" s.blksize #=> 4096 s.atime #=> Wed Apr 09 08:53:54 CDT 2003
源代码
static VALUE
rb_io_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
源代码
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;
}
将流的 sync 模式 设置为给定的值;返回给定的值。
同步模式的值:
-
true:所有输出都立即刷新到基础操作系统,并且不会在内部缓冲。 -
false:输出可能在内部缓冲。
示例:
f = File.open('t.tmp', 'w') f.sync # => false f.sync = true f.sync # => true f.close
相关:IO#fsync。
源代码
static VALUE
rb_io_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,只不过它使用底层系统函数。
此方法不应与其他流读取器方法一起使用。
源代码
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,只不过它
-
使用底层系统函数。
-
返回新位置。
源代码
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,self 必须打开以进行写入(请参阅 Modes);返回写入的字节数。如果 object 不是字符串,则通过 to_s 方法进行转换。
f = File.new('t.tmp', 'w') f.syswrite('foo') # => 3 f.syswrite(30) # => 2 f.syswrite(:foo) # => 3 f.close
此方法不应与其他流写入器方法一起使用。
源代码
static VALUE
rb_io_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 中当前的位置(以字节为单位)(请参阅 Position)
f = File.open('t.txt') f.tell # => 0 f.gets # => "First line\n" f.tell # => 12 f.close
源代码
VALUE
rb_io_timeout(VALUE self)
{
rb_io_t *fptr = rb_io_get_fptr(self);
return fptr->timeout;
}
获取内部超时时长,如果未设置,则返回 nil。
源代码
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 操作(例如在 slowloris 攻击期间发生的那些操作)上挂起的最后一道防线。
源代码
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。
源代码
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;
}
将给定的数据推回(“取消移位”)到流的缓冲区上,将数据放置在接下来要读取的位置;返回 nil。请参阅 字节 IO。
请注意
-
使用未缓冲的读取(例如
IO#sysread)调用该方法无效。 -
对流调用
rewind会丢弃推回的数据。
当给出参数 integer 时,仅使用其低位字节
File.write('t.tmp', '012') f = File.open('t.tmp') f.ungetbyte(0x41) # => nil f.read # => "A012" f.rewind f.ungetbyte(0x4243) # => nil f.read # => "C012" f.close
当给出参数 string 时,使用所有字节
File.write('t.tmp', '012') f = File.open('t.tmp') f.ungetbyte('A') # => nil f.read # => "A012" f.rewind f.ungetbyte('BCDE') # => nil f.read # => "BCDE012" f.close
源代码
VALUE
rb_io_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;
}
将给定的数据推回(“取消移位”)到流的缓冲区上,将数据放置在接下来要读取的位置;返回 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
源代码
static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
#ifndef HAVE_RB_IO_WAIT
rb_io_t *fptr;
struct timeval timerec;
struct timeval *tv = NULL;
int event = 0;
int i;
GetOpenFile(io, fptr);
for (i = 0; i < argc; ++i) {
if (SYMBOL_P(argv[i])) {
event |= wait_mode_sym(argv[i]);
}
else {
*(tv = &timerec) = rb_time_interval(argv[i]);
}
}
/* rb_time_interval() and might_mode() might convert the argument */
rb_io_check_closed(fptr);
if (!event) event = RB_WAITFD_IN;
if ((event & RB_WAITFD_IN) && rb_io_read_pending(fptr))
return Qtrue;
if (wait_for_single_fd(fptr, event, tv))
return io;
return Qnil;
#else
VALUE timeout = Qundef;
rb_io_event_t events = 0;
int i, return_io = 0;
/* The documented signature for this method is actually incorrect.
* A single timeout is allowed in any position, and multiple symbols can be given.
* Whether this is intentional or not, I don't know, and as such I consider this to
* be a legacy/slow path. */
if (argc != 2 || (RB_SYMBOL_P(argv[0]) || RB_SYMBOL_P(argv[1]))) {
/* We'd prefer to return the actual mask, but this form would return the io itself: */
return_io = 1;
/* Slow/messy path: */
for (i = 0; i < argc; i += 1) {
if (RB_SYMBOL_P(argv[i])) {
events |= wait_mode_sym(argv[i]);
}
else if (timeout == Qundef) {
rb_time_interval(timeout = argv[i]);
}
else {
rb_raise(rb_eArgError, "timeout given more than once");
}
}
if (timeout == Qundef) timeout = Qnil;
if (events == 0) {
events = RUBY_IO_READABLE;
}
}
else /* argc == 2 and neither are symbols */ {
/* This is the fast path: */
events = io_event_from_value(argv[0]);
timeout = argv[1];
}
if (events & RUBY_IO_READABLE) {
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
if (rb_io_read_pending(fptr)) {
/* This was the original behaviour: */
if (return_io) return Qtrue;
/* New behaviour always returns an event mask: */
else return RB_INT2NUM(RUBY_IO_READABLE);
}
}
return io_wait_event(io, events, timeout, return_io);
#endif
}
等待直到 IO 准备好进行指定事件,并返回准备好的事件的子集,或者在超时时返回一个假值。
事件可以是 IO::READABLE、IO::WRITABLE 或 IO::PRIORITY 的位掩码。
当有缓冲数据可用时,立即返回一个真值。
可选参数 mode 是 :read、:write 或 :read_write 之一。
您必须 require ‘io/wait’ 才能使用此方法。
源代码
static VALUE
io_wait_priority(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
rb_io_check_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
rb_check_arity(argc, 0, 1);
VALUE timeout = argc == 1 ? argv[0] : Qnil;
return io_wait_event(io, RUBY_IO_PRIORITY, timeout, 1);
}
等待直到 IO 处于优先级状态,并返回真值;超时时返回假值。优先级数据使用 Socket::MSG_OOB 标志发送和接收,通常仅限于流。
您必须 require ‘io/wait’ 才能使用此方法。
源代码
static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
struct timeval timerec;
struct timeval *tv;
#endif
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
#ifndef HAVE_RB_IO_WAIT
tv = get_timeout(argc, argv, &timerec);
#endif
if (rb_io_read_pending(fptr)) return Qtrue;
#ifndef HAVE_RB_IO_WAIT
if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) {
return io;
}
return Qnil;
#else
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_READABLE, timeout, 1);
#endif
}
等待直到 IO 可读,并返回真值;超时时返回假值。当有缓冲数据可用时,立即返回一个真值。
您必须 require ‘io/wait’ 才能使用此方法。
源代码
static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
struct timeval timerec;
struct timeval *tv;
#endif
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
#ifndef HAVE_RB_IO_WAIT
tv = get_timeout(argc, argv, &timerec);
if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) {
return io;
}
return Qnil;
#else
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1);
#endif
}
等待直到 IO 可写,并返回真值;超时时返回假值。
您必须 require ‘io/wait’ 才能使用此方法。
源代码
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’ 才能使用此方法。
源代码
static VALUE
console_set_winsize(VALUE io, VALUE size)
{
rb_console_size_t ws;
#if defined _WIN32
HANDLE wh;
int newrow, newcol;
BOOL ret;
#endif
VALUE row, col, xpixel, ypixel;
const VALUE *sz;
long sizelen;
int fd;
size = rb_Array(size);
if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen);
}
sz = RARRAY_CONST_PTR(size);
row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
fd = GetWriteFD(io);
#if defined TIOCSWINSZ
ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
#define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
SET(row);
SET(col);
SET(xpixel);
SET(ypixel);
#undef SET
if (!setwinsize(fd, &ws)) sys_fail(io);
#elif defined _WIN32
wh = (HANDLE)rb_w32_get_osfhandle(fd);
#define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
SET(row);
SET(col);
#undef SET
if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel);
if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel);
if (!GetConsoleScreenBufferInfo(wh, &ws)) {
rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
}
ws.dwSize.X = newcol;
ret = SetConsoleScreenBufferSize(wh, ws.dwSize);
ws.srWindow.Left = 0;
ws.srWindow.Top = 0;
ws.srWindow.Right = newcol-1;
ws.srWindow.Bottom = newrow-1;
if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
}
/* retry when shrinking buffer after shrunk window */
if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
}
/* remove scrollbar if possible */
if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
}
#endif
return io;
}
尝试设置控制台大小。效果取决于平台和运行环境。
您必须 require ‘io/console’ 才能使用此方法。
源代码
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);
}
}
将给定的每个 objects 写入到 self,后者必须打开以进行写入(请参阅 访问模式);返回写入的总字节数;每个不是字符串的 objects 都通过方法 to_s 转换
$stdout.write('Hello', ', ', 'World!', "\n") # => 14 $stdout.write('foo', :bar, 2, "\n") # => 8
输出
Hello, World! foobar2
相关:IO#read。
源代码
# 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。