類別 IO

類別 IO 的執行個體(通常稱為「串流」)代表底層作業系統中的輸入/輸出串流。類別 IO 是 Ruby 中輸入和輸出的基礎。

類別 File 是 Ruby 核心程式碼中唯一一個是 IO 子類別的類別。Ruby 標準函式庫中的一些類別也是 IO 的子類別;這些類別包括 TCPSocketUDPSocket

全域常數 ARGF(也可以存取為 $<)提供一個類似 IO 的串流,允許存取 ARGV 中找到的所有檔案路徑(如果 ARGV 為空,則在 STDIN 中找到)。ARGF 本身不是 IO 的子類別。

類別 StringIO 提供一個類似 IO 的串流,用於處理 StringStringIO 本身不是 IO 的子類別。

基於 IO 的重要物件包括

可以使用下列方式建立 IO 的執行個體

File 串流一樣,IO 串流具有

與其他 IO 串流一樣,它具有

擴充模組 io/console

擴充模組 io/console 提供許多與主控台互動的方法;需要它才能將許多方法新增到 IO 類別。

範例檔案

這裡的許多範例都使用這些變數

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

  Fourth line
  Fifth line
EOT

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

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

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

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

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

開啟選項

許多 IO 方法接受決定如何開啟新串流的選用關鍵字引數

String#encode 中提供的選項也可用,它可以控制外部和內部編碼之間的轉換。

基本 IO

您可以使用這些方法執行基本串流 IO,它們通常作用於多位元組字串

位置

IO 串流具有非負整數位置,這是下一次讀取或寫入將發生的位元組偏移量。新串流的位置為零(且行號為零);方法 rewind 會將位置(和行號)重設為零。

相關方法

開啟和關閉串流

新的 IO 串流可以開啟為讀取、開啟為寫入,或同時開啟。

當垃圾收集器宣告串流時,串流會自動關閉。

嘗試在已關閉的串流上讀取或寫入會引發例外狀況。

相關方法

串流結束

你可以查詢串流是否定位在串流結束處

你可以使用 IO#seek 方法重新定位到串流結束處

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

或透過讀取所有串流內容(比使用 IO#seek 慢)

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

行 IO

你可以使用下列方法逐行讀取 IO 串流

每個讀取方法都接受

對於每個讀取方法,讀取可能會從行中間開始,具體取決於串流的位置;請參閱 位置

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

輸出

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

你可以使用這個方法逐行寫入 IO 串流

行分隔符

這些方法各使用一個行分隔符,它是區分行的字串

預設的行分隔符是由全域變數 $/ 給定的,它的值預設為 "\n"。要讀取的下一行是從目前位置到下一個行分隔符的所有資料

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

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

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

有兩個特殊行分隔符

行限制

這些方法各使用一個行限制,它指定傳回的位元組數目可能不會(多)長於給定的 limit

多位元組字元不會被分割,所以一行可能稍微長於給定的限制。

如果未給定 limit,則行只由 sep 決定。

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

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

行分隔符和行限制

給定引數 seplimit,則結合這兩種行為

範例

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

行號

可讀的 IO 串流有一個非負整數行號

相關方法

除非呼叫方法 IO#lineno= 進行修改,否則行號會根據指定的行分隔符號 sep,計算某些面向行的方法所讀取的行數

新串流的初始行號為零(且位置為零);方法 rewind 會將行號(和位置)重設為零

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

從串流讀取行通常會變更其行號

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

在串流中反覆運算行通常會變更其行號

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

輸出

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

與串流的 位置 不同,行號不會影響下一次讀取或寫入的位置

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

與行號關聯的全球變數為 $.

字元 IO

您可以使用這些方法逐字元處理 IO 串流

位元組 IO

你可以使用下列方法逐位元組處理 IO 串流

碼點 IO

你可以逐碼點處理 IO 串流

本處內容

首先,說明其他地方的內容。類別 IO

在此,類別 IO 提供對下列事項有用的方法

建立

讀取

寫入

定位

反覆處理

設定

查詢

緩衝

低階存取

其他

常數

EWOULDBLOCKWaitReadable

IO::EAGAINWaitReadable 相同

EWOULDBLOCKWaitWritable

IO::EAGAINWaitWritable 相同

PRIORITY

IO#wait 的優先事件遮罩。

READABLE

IO#wait 的可讀取事件遮罩。

SEEK_CUR

Set 從目前位置開始的 I/O 位置

SEEK_DATA

Set I/O 位置到下一個包含資料的位置

SEEK_END

Set 從結尾開始的 I/O 位置

SEEK_HOLE

Set I/O 位置到下一個孔洞

SEEK_SET

Set 從開頭開始的 I/O 位置

WRITABLE

IO#wait 的可寫入事件遮罩。

公開類別方法

binread(path, length = nil, offset = 0) → string or nil 按一下以切換來源

行為類似於 IO.read,但串流會以 ASCII-8BIT 編碼開啟為二進位模式。

當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入

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

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

行為類似於 IO.write,但串流會以 ASCII-8BIT 編碼開啟為二進位模式。

當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入

static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
    return io_s_write(argc, argv, io, 1);
}
console → #<File:/dev/tty> 按一下以切換來源
console(sym, *args)

傳回開啟的 File 執行個體主控台。

如果給定 sym,它會傳送給開啟的主控台,並使用 args,傳回的結果會取代主控台 IO 本身。

您必須需要「io/console」才能使用此方法。

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

    rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);

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

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

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

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

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

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

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

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

    return con;
}
copy_stream(src, dst, src_length = nil, src_offset = 0) → integer 按一下以切換來源

從給定的 src 複製到給定的 dst,傳回複製的位元組數。

  • 指定的 src 必須符合下列其中一項

    • 可讀取檔案的路徑,用於讀取來源資料。

    • 已開啟供讀取的 IO 類似物件,且能回應方法 :readpartial 或方法 :read

  • 指定的 dst 必須符合下列其中一項

    • 可寫入檔案的路徑,用於寫入資料。

    • 已開啟供寫入的 IO 類似物件,且能回應方法 :write

此處的範例使用檔案 t.txt 作為來源

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

如果只提供引數 srcdst,會複製整個來源串流

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

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

如果提供引數 src_length(非負整數),會複製不超過該數量的位元組

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

如果也提供引數 src_offset,會從該偏移量開始讀取來源串流

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

    MEMZERO(&st, struct copy_stream_struct, 1);

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

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

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

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

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

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

    return OFFT2NUM(st.total);
}
for_fd(fd, mode = 'r', **opts) → io 按一下以切換來源

IO.new 的同義詞。

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

呼叫區塊,並提供從串流讀取的每一行。

當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入

第一個引數必須是字串,也就是檔案的路徑。

如果只提供引數 path,會根據預設行分隔符號,剖析指定 path 檔案中的行,並呼叫區塊,並提供每一行

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

輸出:與上述相同。

對於命令和路徑這兩種形式,其餘引數都相同。

如果提供引數 sep,會根據該行分隔符號剖析行(請參閱 行分隔符號

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

輸出

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

每一段落

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

輸出

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

如果提供引數 limit,會根據預設行分隔符號和指定的行長度限制剖析行(請參閱 行限制

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

輸出

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

給予參數 seplimit 時,會根據給定的行分隔符號和給定的行長限制來解析行(請參閱 行分隔符號和行限制)

可選的關鍵字參數 opts 指定

如果沒有給定區塊,則傳回 Enumerator

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

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

從檔案描述符建立並傳回新的 IO 物件(檔案串流)。

IO.new 可能有助於與低階程式庫進行互動。對於較高階的互動,使用 File.open 建立檔案串流會比較簡單。

參數 fd 必須是有效的檔案描述符(整數)

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

新的 IO 物件不會繼承編碼(因為整數檔案描述符沒有編碼)

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

可選參數 mode(預設為 'r')必須指定有效的模式;請參閱 存取模式

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

可選的關鍵字參數 opts 指定

範例

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


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

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

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

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

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

    if (fmode & FMODE_SETENC_BY_BOM) io_set_encoding_by_bom(io);
    return io;
}
open(fd, mode = 'r', **opts) → io 按一下以切換來源
open(fd, mode = 'r', **opts) {|io| ... } → 物件

透過 IO.new 建立新的 IO 物件,並提供給定的參數。

如果沒有給定區塊,則傳回 IO 物件。

如果給定區塊,則使用 IO 物件呼叫區塊,並傳回區塊的值。

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

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

    return io;
}
pipe(**opts) → [read_io, write_io] 按一下以切換來源
pipe(enc, **opts) → [read_io, write_io]
pipe(ext_enc, int_enc, **opts) → [read_io, write_io]
pipe(**opts) {|read_io, write_io| ...} → 物件
pipe(enc, **opts) {|read_io, write_io| ...} → 物件
pipe(ext_enc, int_enc, **opts) {|read_io, write_io| ...} → 物件

建立一對管線端點,read_iowrite_io,彼此連接。

如果給定參數 enc_string,它必須是包含下列其中一項的字串

  • 要作為外部編碼使用的編碼名稱。

  • 作為外部編碼和內部編碼使用的兩個編碼的冒號分隔名稱。

如果給定參數 int_enc,它必須是 Encoding 物件或編碼名稱字串,用來指定要使用的內部編碼;如果也給定參數 ext_enc,它必須是 Encoding 物件或編碼名稱字串,用來指定要使用的外部編碼。

read_io 讀取的字串會標記為外部編碼;如果也指定了內部編碼,則會將字串轉換為該編碼,並標記為該編碼。

如果指定任何編碼,則選用的雜湊引數會指定轉換選項。

可選的關鍵字參數 opts 指定

未提供區塊時,會在陣列中傳回兩個端點

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

提供區塊時,會呼叫具有兩個端點的區塊;關閉兩個端點並傳回區塊的值

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

輸出

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

並非所有平台都可用。

在以下範例中,兩個處理程序會關閉它們未使用的管線端點。這不只是美觀的細微差別。如果任何寫入器仍開啟管線,則管線的讀取端不會產生檔案結束條件。在父處理程序的情況下,如果未先發出 wr.closerd.read 永遠不會傳回

rd, wr = IO.pipe

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

產生

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

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

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

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

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

    extract_binmode(opt, &fmode);

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

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

    ret = rb_assoc_new(r, w);
    if (rb_block_given_p()) {
        VALUE rw[2];
        rw[0] = r;
        rw[1] = w;
        return rb_ensure(rb_yield, ret, pipe_pair_close, (VALUE)rw);
    }
    return ret;
}
popen(env = {}, cmd, mode = 'r', **opts) → io 按一下以切換來源
popen(env = {}, cmd, mode = 'r', **opts) {|io| ... } → 物件

將指定的命令 cmd 作為子處理程序執行,其 $stdin 和 $stdout 會連線到新的串流 io

如果使用不受信任的輸入呼叫此方法,可能會產生潛在的安全漏洞;請參閱 命令注入

如果未提供區塊,則會傳回新的串流,依據指定的 mode,可能會開啟以進行讀取、寫入或同時進行。應明確關閉串流(最終)以避免資源外洩。

如果提供區塊,則會將串流傳遞給區塊(再次說明,開啟以進行讀取、寫入或同時進行);當區塊結束時,串流會關閉,而區塊的值會指定給全域變數 $? 並傳回。

選用的引數 mode 可以是任何有效的 IO 模式。請參閱 存取模式

必要的引數 cmd 會決定發生下列哪一項

  • 處理程序會分岔。

  • 指定的程式會在殼層中執行。

  • 指定的程式會使用指定的引數執行。

  • 指定的程式會使用指定的引數和指定的 argv0 執行。

以下會詳細說明每一項。

選用的雜湊引數 env 會指定名稱/值對,這些對會新增到子處理程序的環境變數

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

可選的關鍵字參數 opts 指定

分岔的處理程序

當引數 cmd 是 1 個字元的字串 '-' 時,會導致處理程序分岔

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

輸出

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

請注意,並非所有平台都支援這項功能。

殼層子處理程序

當引數 cmd 是單一字串(但不是 '-')時,會將名為 cmd 的程式作為殼層命令執行

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

輸出

["Linux\n"]

另一個範例

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

輸出

213

程式子處理程序

當引數 cmd 是字串陣列時,會將名為 cmd[0] 的程式作為其引數執行 cmd 的所有元素

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

輸出

1111

使用 argv0 的程式子程序

當參數 cmd 是陣列,其第一個元素是 2 元素字串陣列,其餘元素(如果有)是字串時

  • cmd[0][0](巢狀陣列中的第一個字串)是執行的程式的名稱。

  • cmd[0][1](巢狀陣列中的第二個字串)設定為程式的 argv[0]

  • cmd[1..-1](外部陣列中的字串)是程式的參數。

範例(將 $0 設定為「foo」)

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

一些特殊範例

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

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

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

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

輸出(來自最後一個區段)

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

引發 IO.pipeKernel.spawn 引發的例外。

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

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

開啟串流,讀取並傳回部分或全部內容,並關閉串流;如果未讀取任何位元組,傳回 nil

當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入

第一個引數必須是字串,也就是檔案的路徑。

僅提供參數 path 時,以文字模式讀取並傳回指定路徑中檔案的全部內容

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

在 Windows 上,當遇到某些特殊位元組時,文字模式會終止讀取並在檔案中留下未讀取的位元組。如果應讀取檔案中的所有位元組,請考慮使用 IO.binread

使用參數 length 時,如果可用,傳回 length 位元組

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

使用參數 lengthoffset 時,如果可用,傳回 length 位元組,從指定的 offset 開始

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

可選的關鍵字參數 opts 指定

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

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

傳回從串流中讀取的所有行的陣列。

當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入

第一個引數必須是字串,也就是檔案的路徑。

僅提供參數 path 時,根據預設行分隔符號,從指定 path 中的檔案分析行,並在陣列中傳回這些行

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

如果提供引數 sep,會根據該行分隔符號剖析行(請參閱 行分隔符號

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

如果提供引數 limit,會根據預設行分隔符號和指定的行長度限制剖析行(請參閱 行限制

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

給予參數 seplimit 時,會根據給定的行分隔符號和給定的行長限制來解析行(請參閱 行分隔符號和行限制)

可選的關鍵字參數 opts 指定

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

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

呼叫系統呼叫 select(2),它會監控多個檔案描述符,等到一個或多個檔案描述符準備好進行某類 I/O 作業時才停止等待。

並非所有平台都實作此功能。

每個參數 read_ioswrite_ioserror_ios 都是 IO 物件的陣列。

參數 timeout 是以秒為單位的整數逾時間隔。

此方法會監控所有三個陣列中指定的 IO 物件,等待某些物件準備好;傳回 3 個元素的陣列,其元素為

  • 準備好讀取的 read_ios 中的物件陣列。

  • 準備好寫入的 write_ios 中的物件陣列。

  • error_ios 中有待處理例外狀況的物件陣列。

如果在給定的 timeout 內沒有任何物件準備好,則傳回 nil

IO.select 會窺探 IO 物件的緩衝區以測試可讀性。如果 IO 緩衝區不為空,IO.select 會立即通知可讀性。此「窺探」僅發生在 IO 物件上。它不會發生在類 IO 物件上,例如 OpenSSL::SSL::SSLSocket

使用 IO.select 的最佳方式是在 read_nonblockwrite_nonblock 等非封鎖方法後呼叫它。這些方法會引發 IO::WaitReadableIO::WaitWritable 延伸的例外狀況。這些模組會通知呼叫者應如何使用 IO.select 等待。如果引發 IO::WaitReadable,則呼叫者應等待讀取。如果引發 IO::WaitWritable,則呼叫者應等待寫入。

因此,封鎖讀取 (readpartial) 可以使用 read_nonblock 和 IO.select 模擬,如下所示

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、管線 socket socket,當多個程序從串流中讀取時。

最後,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) 可以使用 write_nonblockIO.select 模擬,如下所示: IO::WaitReadable 也應該在 OpenSSL::SSL::SSLSocket 中用於 SSL 重新協商。

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

範例

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

輸出

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

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

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

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

    return rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
}
sysopen(path, mode = 'r', perm = 0666) → integer 按一下以切換來源

使用給定的模式和權限開啟給定路徑中的檔案;傳回整數檔案描述符。

如果檔案可讀取,則它必須存在;如果檔案可寫入且不存在,則會使用給定的權限建立檔案

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

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

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

    RB_GC_GUARD(fname) = rb_str_new4(fname);
    fd = rb_sysopen(fname, oflags, perm);
    return INT2NUM(fd);
}
try_convert(object) → new_io or nil 按一下以切換來源

嘗試透過方法 to_ioobject 轉換為 IO 物件;如果成功,則傳回新的 IO 物件,否則傳回 nil

IO.try_convert(STDOUT)   # => #<IO:<STDOUT>>
IO.try_convert(ARGF)     # => #<IO:<STDIN>>
IO.try_convert('STDOUT') # => nil
static VALUE
rb_io_s_try_convert(VALUE dummy, VALUE io)
{
    return rb_io_check_io(io);
}
write(path, data, offset = 0, **opts) → integer 按一下以切換來源

開啟串流,將給定的 data 寫入其中,然後關閉串流;傳回寫入的位元組數目。

當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入

第一個引數必須是字串,也就是檔案的路徑。

僅給定引數 path 時,將給定的 data 寫入該路徑中的檔案

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

如果 offset 為零(預設值),則覆寫檔案

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

如果 offset 在檔案內容中,則部分覆寫檔案

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

如果 offset 在檔案內容之外,則以空字元 "\u0000" 補齊檔案

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

可選的關鍵字參數 opts 指定

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

公開實例方法

self << object → self 按一下以切換來源

將給定的 object 寫入 self,它必須開啟才能寫入(請參閱 存取模式);傳回 self;如果 object 不是字串,則會透過方法 to_s 轉換

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

輸出

Hello, World!
foobar2
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
    rb_io_write(io, str);
    return io;
}
advise(advice, offset = 0, len = 0) → nil 按一下以切換來源

呼叫 Posix 系統呼叫 posix_fadvise(2),它宣告以特定方式存取目前檔案中資料的意圖。

參數和結果取決於平台。

相關資料由下列指定

  • offset:資料第一個位元的偏移量。

  • len:要存取的位元組數目;如果 len 為零,或大於剩餘位元組數目,則會存取所有剩餘位元組。

參數 advice 為下列符號之一

  • :normal:應用程式沒有建議給定資料的存取模式。如果未針對開啟的檔案提供建議,則此為預設假設。

  • :sequential:應用程式預期會循序存取指定資料(先讀取較低偏移量,再讀取較高偏移量)。

  • :random:會以隨機順序存取指定資料。

  • :noreuse:指定資料只會存取一次。

  • :willneed:指定資料將在不久的將來存取。

  • :dontneed:指定資料將在不久的將來不會存取。

並非所有平台都實作此功能。

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

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

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

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

#ifdef HAVE_POSIX_FADVISE
    return do_io_advise(fptr, advice, off, l);
#else
    ((void)off, (void)l);       /* Ignore all hint */
    return Qnil;
#endif
}
autoclose = bool → true 或 false 按一下以切換來源

設定自動關閉旗標。

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

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

如果 ios 的底層檔案描述符會在完成或呼叫 close 時關閉,則傳回 true,否則傳回 false

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

將串流的資料模式設定為二進位(請參閱 資料模式)。

串流的資料模式無法從二進位變更為文字。

static VALUE
rb_io_binmode_m(VALUE io)
{
    VALUE write_io;

    rb_io_ascii8bit_binmode(io);

    write_io = GetWriteIO(io);
    if (write_io != io)
        rb_io_ascii8bit_binmode(write_io);
    return io;
}
binmode? → true 或 false 按一下以切換來源

如果串流為二進位模式,則傳回 true,否則傳回 false。請參閱 資料模式

static VALUE
rb_io_binmode_p(VALUE io)
{
    rb_io_t *fptr;
    GetOpenFile(io, fptr);
    return RBOOL(fptr->mode & FMODE_BINMODE);
}
check_winsize_changed() 按一下以切換來源
_WIN32
static VALUE
console_check_winsize_changed(VALUE io)
{
    HANDLE h;
    DWORD num;

    h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
    while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) {
        INPUT_RECORD rec;
        if (ReadConsoleInput(h, &rec, 1, &num)) {
            if (rec.EventType == WINDOW_BUFFER_SIZE_EVENT) {
                rb_yield(Qnil);
            }
        }
    }
    return io;
}
clear_screen() 按一下以切換來源
static VALUE
console_clear_screen(VALUE io)
{
    console_erase_screen(io, INT2FIX(2));
    console_goto(io, INT2FIX(0), INT2FIX(0));
    return io;
}
close → nil 按一下以切換來源

關閉串流以進行讀取和寫入(如果已開啟讀取或寫入);傳回 nil。請參閱 開啟和關閉的串流

如果串流已開啟以進行寫入,則在關閉之前會將任何緩衝的寫入沖洗到作業系統。

如果串流是由 IO.popen 開啟,則會設定全域變數 $?(子項目的結束狀態)。

範例

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

輸出

false
pid 13760 exit 0
true

相關:IO#close_readIO#close_writeIO#closed?

static VALUE
rb_io_close_m(VALUE io)
{
    rb_io_t *fptr = rb_io_get_fptr(io);
    if (fptr->fd < 0) {
        return Qnil;
    }
    rb_io_close(io);
    return Qnil;
}
close_on_exec = bool → true 或 false 按一下以切換來源

設定 close-on-exec 旗標。

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 自 Ruby 2.0.0 以來預設會為所有檔案描述符設定 close-on-exec 旗標。因此您不需要自行設定。此外,取消設定 close-on-exec 旗標可能會導致檔案描述符外洩,如果另一個執行緒使用 fork() 和 exec()(例如透過 system() 方法)。如果您真的需要檔案描述符繼承到子程序,請使用 spawn() 的參數,例如 fd=>fd。

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

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

    }

    GetOpenFile(io, fptr);
    if (fptr && 0 <= (fd = fptr->fd)) {
        if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
        if ((ret & FD_CLOEXEC) != flag) {
            ret = (ret & ~FD_CLOEXEC) | flag;
            ret = fcntl(fd, F_SETFD, ret);
            if (ret != 0) rb_sys_fail_path(fptr->pathv);
        }
    }
    return Qnil;
}
close_on_exec? → true 或 false 按一下以切換來源

如果串流會在執行時關閉,則傳回 true,否則傳回 false

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

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

    GetOpenFile(io, fptr);
    if (fptr && 0 <= (fd = fptr->fd)) {
        if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
        if (!(ret & FD_CLOEXEC)) return Qfalse;
    }
    return Qtrue;
}
close_read → nil 按一下以切換來源

如果已開啟讀取,則關閉串流以進行讀取;傳回 nil。請參閱 開啟和關閉串流

如果串流是由 IO.popen 開啟的,且也關閉寫入,則設定全域變數 $?(子項退出狀態)。

範例

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

輸出

false
false
pid 14748 exit 0
true

相關:IO#closeIO#close_writeIO#closed?

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

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

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

    if ((fptr->mode & (FMODE_DUPLEX|FMODE_WRITABLE)) == FMODE_WRITABLE) {
        rb_raise(rb_eIOError, "closing non-duplex IO for reading");
    }
    return rb_io_close(io);
}
close_write → nil 按一下以切換來源

如果已開啟寫入,則關閉串流以進行寫入;傳回 nil。請參閱 開啟和關閉串流

在關閉前,將任何緩衝寫入沖刷至作業系統。

如果串流是由 IO.popen 開啟的,且也關閉讀取,則設定全域變數 $?(子項退出狀態)。

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

輸出

false
false
pid 15044 exit 0
true

相關:IO#closeIO#close_readIO#closed?

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

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

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

    if (io != write_io) {
        fptr = rb_io_get_fptr(rb_io_taint_check(io));
        fptr->tied_io_for_writing = 0;
    }
    rb_io_close(write_io);
    return Qnil;
}
closed? → true 或 false 按一下以切換來源

如果串流已關閉讀取和寫入,則傳回 true,否則傳回 false。請參閱 開啟和關閉串流

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

輸出

false
false
true

相關:IO#close_readIO#close_writeIO#close

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

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

    fptr = rb_io_get_fptr(io);
    return RBOOL(0 > fptr->fd);
}
console_mode → 模式 按一下以切換來源

傳回表示目前主控台模式的資料。

您必須需要「io/console」才能使用此方法。

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

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

    return conmode_new(cConmode, &t);
}
console_mode = 模式 按一下以切換來源

將主控台模式設定為 模式

您必須需要「io/console」才能使用此方法。

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

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

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

    return mode;
}
cooked {|io| } 按一下以切換來源

在 cooked 模式中產生 self

STDIN.cooked(&:gets)

將讀取並傳回一行,並帶有回音和行編輯。

您必須需要「io/console」才能使用此方法。

static VALUE
console_cooked(VALUE io)
{
    return ttymode(io, rb_yield, io, set_cookedmode, NULL);
}
cooked! 按一下以切換來源

啟用 cooked 模式。

如果終端機模式需要返回,請使用 io.cooked { … }。

您必須需要「io/console」才能使用此方法。

static VALUE
console_set_cooked(VALUE io)
{
    conmode t;
    int fd = GetReadFD(io);
    if (!getattr(fd, &t)) sys_fail(io);
    set_cookedmode(&t, NULL);
    if (!setattr(fd, &t)) sys_fail(io);
    return io;
}
cursor → [row, column] 按一下以切換來源

傳回目前的游標位置,為整數的二元陣列(row、column)

io.cursor # => [3, 5]

您必須需要「io/console」才能使用此方法。

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

呼叫給定的區塊,其中包含串流中的每個位元組 (0..255);傳回 self。請參閱位元組 IO

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

如果沒有給定區塊,則傳回 Enumerator

相關:IO#each_charIO#each_codepoint

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

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

    do {
        while (fptr->rbuf.len > 0) {
            char *p = fptr->rbuf.ptr + fptr->rbuf.off++;
            fptr->rbuf.len--;
            rb_yield(INT2FIX(*p & 0xff));
            rb_io_check_byte_readable(fptr);
            errno = 0;
        }
        READ_CHECK(fptr);
    } while (io_fillbuf(fptr) >= 0);
    return io;
}
each_char {|c| ... } → self 按一下以切換來源
each_char → 列舉器

呼叫給定的區塊,其中包含串流中的每個字元;傳回 self。請參閱字元 IO

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

如果沒有給定區塊,則傳回 Enumerator

相關:IO#each_byteIO#each_codepoint

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

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

    enc = io_input_encoding(fptr);
    READ_CHECK(fptr);
    while (!NIL_P(c = io_getc(fptr, enc))) {
        rb_yield(c);
    }
    return io;
}
each_codepoint {|c| ... } → self 按一下以切換來源
each_codepoint → 列舉器

呼叫給定的區塊,其中包含串流中的每個碼點;傳回 self

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

如果沒有給定區塊,則傳回 Enumerator

相關:IO#each_byteIO#each_char

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

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

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

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

呼叫區塊,並從串流中讀取每一行剩餘的資料;傳回 self。如果已在串流的結尾,則不會執行任何動作;請參閱 Line 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 讀取各行資料;請參閱 Line Separator

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

輸出

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

會尊重 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,則會限制每一行的位元組數目;請參閱 Line Limit

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

輸出

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

給定引數 seplimit,則結合這兩種行為

  • 呼叫時,會根據換行符號 sep 讀取下一行資料。

  • 但傳回不超過限制允許的位元組數目。

選用的關鍵字參數 chomp 指定是否省略換行符號

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

輸出

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

如果沒有給定區塊,則傳回 Enumerator

別名為:each
echo = flag 按一下以切換來源

啟用/停用回顯。在某些平台上,此旗標和原始/已處理模式的所有組合可能無效。

您必須需要「io/console」才能使用此方法。

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

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

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

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

    return io;
}
echo? → true 或 false 按一下以切換來源

如果已啟用回顯,則傳回 true

您必須需要「io/console」才能使用此方法。

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

    if (!getattr(fd, &t)) sys_fail(io);
    return echo_p(&t) ? Qtrue : Qfalse;
}
eof → true 或 false 按一下以切換來源

如果串流已定位在其結尾,則傳回 true,否則傳回 false;請參閱 Position

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

除非串流已開啟以供讀取,否則會引發例外情況;請參閱 Mode

如果 self 是串流,例如管線或 socket,則此方法會封鎖,直到另一端傳送一些資料或關閉它

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

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

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

請注意,此方法會將資料讀取到輸入位元組緩衝區。因此,IO#sysread 可能無法與 IO#eof? 搭配使用,除非您先呼叫 IO#rewind(某些串流不支援此功能)。

VALUE
rb_io_eof(VALUE io)
{
    rb_io_t *fptr;

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

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

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

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

expect 函式庫會新增執行個體方法 IO#expect,它類似於 TCL expect 延伸模組

若要使用此方法,您必須需要 expect

require 'expect'

IO 讀取資料,直到指定的 pattern 相符或 timeout 結束。

傳回包含讀取緩衝區和比對結果的陣列。如果給定區塊,結果會傳遞到區塊並傳回 nil。

在沒有區塊的情況下呼叫時,它會等到從 IO 取得與給定 pattern 比對的輸入,或經過指定為逾時的時間。當從 IO 取得比對時,會傳回陣列。IO 取得的整個字串會成為陣列的第一個元素,直到比對到 pattern,後面的元素會指出與正規表示式中的錨點比對的 pattern。

選用逾時參數會定義以秒為單位的時間,用於等待 pattern。如果逾時或找到 eof,會傳回或傳遞 nil。不過,逾時區段的緩衝區會保留到下一個 expect 呼叫。預設逾時時間為 9999999 秒。

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

傳回表示串流編碼的 編碼 物件,或如果串流處於寫入模式且未指定編碼,則傳回 nil

請參閱 編碼

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

    if (fptr->encs.enc2) {
        return rb_enc_from_encoding(fptr->encs.enc2);
    }
    if (fptr->mode & FMODE_WRITABLE) {
        if (fptr->encs.enc)
            return rb_enc_from_encoding(fptr->encs.enc);
        return Qnil;
    }
    return rb_enc_from_encoding(io_read_encoding(fptr));
}
fcntl(整數_cmd, 參數) → 整數 按一下切換來源

呼叫 Posix 系統呼叫 fcntl(2),它提供一種機制,用於發出低階指令來控制或查詢面向檔案的 I/O 串流。參數和結果取決於平台。

如果 參數 是數字,其值會直接傳遞;如果它是字串,它會被解釋為位元組的二進位順序。(Array#pack 可能是一種建立此字串的有用方法。)

並非所有平台都實作此功能。

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

    rb_scan_args(argc, argv, "11", &req, &arg);
    return rb_fcntl(io, req, arg);
}
fdatasync → 0 按一下切換來源

透過作業系統的 fdatasync(2)(如果支援)立即將串流中緩衝的所有資料寫入磁碟,否則透過 fsync(2)(如果支援)寫入,否則會引發例外狀況。

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

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

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

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

    /* fall back */
    return rb_io_fsync(io);
}
fileno → 整數 按一下切換來源

傳回串流的整數檔案描述子

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

    rb_io_check_closed(fptr);
    fd = fptr->fd;
    return INT2FIX(fd);
}
別名為:to_i
flush → self 按一下切換來源

self 中緩衝的資料沖至作業系統(但並非一定沖至作業系統中緩衝的資料)

$stdout.print 'no newline' # Not necessarily flushed.
$stdout.flush              # Flushed.
VALUE
rb_io_flush(VALUE io)
{
    return rb_io_flush_raw(io, 1);
}
fsync → 0 按一下切換來源

透過作業系統的 fsync(2) 立即將串流中緩衝的所有資料寫入磁碟。

請注意此差異

  • IO#sync=:確保資料已從串流的內部緩衝區中清除,但不保證作業系統實際將資料寫入磁碟。

  • IO#fsync:確保資料已從內部緩衝區中清除,且資料已寫入磁碟。

如果作業系統不支援 fsync(2),則會引發例外狀況。

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

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

    if (io_fflush(fptr) < 0)
        rb_sys_fail_on_write(fptr);
    if ((int)rb_thread_io_blocking_region(nogvl_fsync, fptr, fptr->fd) < 0)
        rb_sys_fail_path(fptr->pathv);
    return INT2FIX(0);
}
getbyte → 整數或 nil 按一下以切換來源

從串流中讀取並傳回下一個位元組(範圍 0..255);如果已在串流結尾,則傳回 nil。請參閱 位元組 IO

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

相關:IO#readbyte(可能會引發 EOFError)。

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

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

從串流中讀取並傳回下一個 1 個字元的字串;如果已在串流結尾,則傳回 nil。請參閱 字元 IO

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

相關:IO#readchar(可能會引發 EOFError)。

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

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

    enc = io_input_encoding(fptr);
    READ_CHECK(fptr);
    return io_getc(fptr, enc);
}
getch(min: nil, time: nil, intr: nil) → 字元 按一下以切換來源

以原始模式讀取並傳回一個字元。

有關參數的詳細資訊,請參閱 IO#raw

您必須需要「io/console」才能使用此方法。

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

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

讀取並傳回一行,不回顯。除非為 nil,否則會列印 prompt

終止讀取行的換行字元會從傳回的字串中移除,請參閱 String#chomp!

您必須需要「io/console」才能使用此方法。

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

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

從串流中讀取並傳回一行;將傳回值指定給 $_。請參閱 行 IO

如果沒有提供任何參數,則傳回由行分隔符號 $/ 確定的下一行,或在沒有任何行的情況下傳回 nil

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

如果只提供字串參數 sep,則傳回由行分隔符號 sep 確定的下一行,或在沒有任何行的情況下傳回 nil;請參閱 行分隔符號

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

會尊重 sep 的兩個特殊值

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

如果只提供整數參數 limit,則限制行中的位元組數目;請參閱 行限制

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

給定引數 seplimit,則結合這兩種行為

  • 傳回由行分隔符號 sep 確定的下一行,或在沒有任何行的情況下傳回 nil

  • 但傳回不超過限制允許的位元組數目。

選用的關鍵字參數 chomp 指定是否省略換行符號

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

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

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

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

清除核心中的輸入緩衝區。

您必須需要「io/console」才能使用此方法。

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

    return io;
}
inspect → string 按一下以切換來源

傳回 self 的字串表示。

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

    fptr = RFILE(obj)->fptr;
    if (!fptr) return rb_any_to_s(obj);
    result = rb_str_new_cstr("#<");
    rb_str_append(result, rb_class_name(CLASS_OF(obj)));
    rb_str_cat2(result, ":");
    if (NIL_P(fptr->pathv)) {
        if (fptr->fd < 0) {
            rb_str_cat(result, closed+1, strlen(closed)-1);
        }
        else {
            rb_str_catf(result, "fd %d", fptr->fd);
        }
    }
    else {
        rb_str_append(result, fptr->pathv);
        if (fptr->fd < 0) {
            rb_str_cat(result, closed, strlen(closed));
        }
    }
    return rb_str_cat2(result, ">");
}
internal_encoding → 編碼或 nil 按一下以切換來源

傳回表示內部字串編碼的 編碼 物件(如果指定轉換)或 nil(否則)。

請參閱 編碼

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

    if (!fptr->encs.enc2) return Qnil;
    return rb_enc_from_encoding(io_read_encoding(fptr));
}
ioctl(integer_cmd, argument) → integer 按一下以切換來源

呼叫 Posix 系統呼叫 ioctl(2),它會對 I/O 裝置發出低階命令。

對 I/O 裝置發出低階命令。參數和傳回值取決於平台。呼叫的效果也取決於平台。

如果參數 argument 是整數,則會直接傳遞;如果它是字串,則會解譯為位元組的二進位序列。

並非所有平台都實作此功能。

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

    rb_scan_args(argc, argv, "11", &req, &arg);
    return rb_ioctl(io, req, arg);
}
ioflush 按一下以切換來源

清除核心中的輸入和輸出緩衝區。

您必須需要「io/console」才能使用此方法。

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

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

    return io;
}
isatty → true 或 false 按一下以切換來源

如果串流與終端裝置 (tty) 相關聯,則傳回 true,否則傳回 false

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

    GetOpenFile(io, fptr);
    return RBOOL(isatty(fptr->fd) != 0);
}
別名為:tty?
lineno → integer 按一下以切換來源

傳回串流的目前行號;請參閱 行號

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

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);
    return INT2NUM(fptr->lineno);
}
lineno = integer → integer 按一下以切換來源

設定並傳回串流的行號;請參閱 行號

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

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);
    fptr->lineno = NUM2INT(lineno);
    return lineno;
}
noecho {|io| } 按一下以切換來源

在停用回音的情況下產生 self

STDIN.noecho(&:gets)

會讀取並傳回一行,且不會回音。

您必須需要「io/console」才能使用此方法。

static VALUE
console_noecho(VALUE io)
{
    return ttymode(io, rb_yield, io, set_noecho, NULL);
}
nonblock {|io| } → 物件 按一下以切換來源
nonblock(boolean) {|io| } → 物件

在非封鎖模式下產生 self

當給定 false 作為參數時,self 會在封鎖模式下產生。在執行封鎖後,會還原為原始模式。

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

    int descriptor = rb_io_descriptor(self);

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

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

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

    return rb_ensure(rb_yield, self, io_nonblock_restore, (VALUE)restore);
}
nonblock = boolean → boolean 按一下以切換原始碼

設定為 true 時,會在串流上啟用非封鎖模式,設定為 false 時,會啟用封鎖模式。

此方法會設定或清除 ios 中檔案描述子的 O_NONBLOCK 旗標。

大多數 IO 方法的行為不受此旗標影響,因為它們會在 EAGAIN 和部分讀取/寫入後重試系統呼叫以完成其任務。(例外為 IO#syswrite,它不會重試。)

此方法可用於清除標準 I/O 的非封鎖模式。由於非封鎖方法(read_nonblock 等)會設定非封鎖模式,但不會清除它,因此此方法可用於如下所示。

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

由於旗標會在程序間共用,且許多非 Ruby 指令不預期標準 I/O 處於非封鎖模式,因此在 Ruby 程式結束前清除旗標會比較安全。

例如,下列 Ruby 程式會讓 STDIN/STDOUT/STDER 離開非封鎖模式。(STDIN、STDOUT 和 STDERR 會連接到終端機。因此,讓其中一個處於非封鎖模式會影響其他兩個。)因此,cat 指令會嘗試從標準輸入讀取,並會導致「資源暫時無法使用」錯誤(EAGAIN)。

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

清除旗標會讓 cat 指令的行為回復正常。(cat 指令會等待標準輸入的輸入。)

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

    return self;
}
nonblock? → boolean 按一下以切換原始碼

如果 IO 物件處於非封鎖模式,則傳回 true

static VALUE
rb_io_nonblock_p(VALUE io)
{
    if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK)
        return Qtrue;
    return Qfalse;
}
nread → int 按一下以切換原始碼

傳回可以在不封鎖的情況下讀取的位元組數。如果沒有可用資訊,則傳回零。

您必須需要「io/wait」才能使用此方法。

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

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

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

    if (!FIONREAD_POSSIBLE_P(fd)) return INT2FIX(0);
    if (ioctl(fd, FIONREAD, &n)) return INT2FIX(0);
    if (n > 0) return ioctl_arg2num(n);
    return INT2FIX(0);
}
oflush 按一下以切換原始碼

清除核心中的輸出緩衝區。

您必須需要「io/console」才能使用此方法。

static VALUE
console_oflush(VALUE io)
{
    int fd = GetWriteFD(io);
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
    if (tcflush(fd, TCOFLUSH)) sys_fail(io);
#endif
    (void)fd;
    return io;
}
path → 字串或 nil 按一下以切換原始碼

傳回與 IO 關聯的路徑,如果沒有與 IO 關聯的路徑,則傳回 nil。無法保證路徑存在於檔案系統中。

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

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

    if (!fptr)
        return Qnil;

    return rb_obj_dup(fptr->pathv);
}
別名:to_path
pathconf(p1) 按一下以切換來源

使用 fpathconf() 傳回路徑名稱組態變數。

name 應該是 Etc 下的常數,以 PC_ 開頭。

傳回值是整數或 nil。nil 表示不確定限制。(fpathconf() 傳回 -1 但 errno 未設定。)

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

    name = NUM2INT(arg);

    errno = 0;
    ret = fpathconf(rb_io_descriptor(io), name);
    if (ret == -1) {
        if (errno == 0) /* no limit */
            return Qnil;
        rb_sys_fail("fpathconf");
    }
    return LONG2NUM(ret);
}
pid → 整數或 nil 按一下以切換來源

傳回與串流關聯的子處理程序的處理程序 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_pid(VALUE io)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    if (!fptr->pid)
        return Qnil;
    return PIDT2NUM(fptr->pid);
}
pos
別名:tell
pos = new_position → new_position 按一下以切換來源

搜尋到指定的 new_position(以位元組為單位);請參閱 Position

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

相關:IO#seekIO#tell

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

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

    return OFFT2NUM(pos);
}
pread(maxlen, offset) → 字串 按一下以切換來源
pread(maxlen, offset, out_string) → 字串

行為類似於 IO#readpartial,但

  • 在指定的 offset(以位元組為單位)讀取。

  • 忽略串流的位置(請參閱 Position),且不修改該位置。

  • 略過串流中的任何使用者空間緩衝。

因為此方法不會影響串流的狀態(特別是其位置),所以 pread 允許多個執行緒和處理程序使用相同的 IO 物件在不同偏移量處讀取。

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

某些平台不提供此方法。

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

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

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

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

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

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

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

    return str;
}
pressed?(p1) 按一下以切換來源
static VALUE
console_key_pressed_p(VALUE io, VALUE k)
{
    int vk = -1;

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

將指定的物件寫入串流;傳回 nil。附加輸出記錄分隔符號 $OUTPUT_RECORD_SEPARATOR$\),如果它不是 nil。請參閱 Line IO

如果指定引數 objects,則對每個物件

  • 如果非字串,則透過其方法 to_s 轉換。

  • 寫入串流。

  • 如果不是最後一個物件,則寫入輸出欄位分隔符號 $OUTPUT_FIELD_SEPARATOR ($,),如果它不是 nil 的話。

使用預設分隔符號

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

輸出

nil
nil
"00.00/10+0izerozero"

使用指定分隔符號

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

輸出

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

如果沒有給定引數,則寫入 $_ 的內容(通常是最新的使用者輸入)

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

格式化並將 objects 寫入串流。

有關 format_string 的詳細資訊,請參閱 格式規範

VALUE
rb_io_printf(int argc, const VALUE *argv, VALUE out)
{
    rb_io_write(out, rb_f_sprintf(argc, argv));
    return Qnil;
}
putc(object) → object 按一下以切換來源

寫入一個字元到串流。請參閱 字元 IO

如果 object 是數字,則在必要時轉換為整數,然後寫入其代碼為最低有效位元的字元;如果 object 是字串,則寫入第一個字元

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

輸出

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

將指定的 objects 寫入串流,該串流必須開啟以供寫入;傳回 nil。在每個尚未以換行序列結尾的物件後面寫入換行符號。如果在沒有引數的情況下呼叫,則寫入換行符號。請參閱 行 IO

請注意,每個新增的換行符號都是字元 "\n"<//tt>,而不是輸出記錄分隔符號 (<tt>$\)。

每個物件的處理方式

  • 字串:寫入字串。

  • 既不是字串也不是陣列:寫入 object.to_s

  • 陣列:寫入陣列的每個元素;陣列可以巢狀。

為了讓這些範例簡短,我們定義此輔助方法

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

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

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

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

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

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

        rb_io_writev(out, n, args);
    }

    return Qnil;
}
pwrite(object, offset) → integer 按一下以切換來源

行為類似於 IO#write,但它

  • 寫入指定的 offset(以位元組為單位)。

  • 忽略串流的位置(請參閱 Position),且不修改該位置。

  • 略過串流中的任何使用者空間緩衝。

由於此方法不會影響串流的狀態(特別是其位置),因此 pwrite 允許多個執行緒和程序使用同一個 IO 物件在不同的偏移量處寫入。

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

某些平台不提供此方法。

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

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

    arg.offset = NUM2OFFT(offset);

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

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

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

    return SSIZET2NUM(n);
}
raw(min: nil, time: nil, intr: nil) {|io| } 按一下以切換原始碼

在原始模式中產生 self,並傳回區塊的結果。

STDIN.raw(&:gets)

將讀取並傳回一行,而不會回顯和編輯行。

參數 min 指定執行讀取作業時應接收的最小位元組數目。(預設值:1)

參數 time 指定以 1/10 秒為精度的逾時時間(以秒為單位)。(預設值:0)

如果參數 intrtrue,則啟用中斷、終止、退出和暫停特殊字元。

請參閱 termios 手冊頁面以取得進一步的詳細資料。

您必須需要「io/console」才能使用此方法。

static VALUE
console_raw(int argc, VALUE *argv, VALUE io)
{
    rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
    return ttymode(io, rb_yield, io, set_rawmode, optp);
}
raw!(min: nil, time: nil, intr: nil) → io 按一下以切換原始碼

啟用原始模式,並傳回 io

如果需要將終端機模式改回,請使用 io.raw { ... }

有關參數的詳細資訊,請參閱 IO#raw

您必須需要「io/console」才能使用此方法。

static VALUE
console_set_raw(int argc, VALUE *argv, VALUE io)
{
    conmode t;
    rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
    int fd = GetReadFD(io);
    if (!getattr(fd, &t)) sys_fail(io);
    set_rawmode(&t, optp);
    if (!setattr(fd, &t)) sys_fail(io);
    return io;
}
read(maxlen = nil, out_string = nil) → new_string, out_string, or nil 按一下以切換原始碼

從串流中讀取位元組;串流必須開啟以進行讀取(請參閱 存取模式

  • 如果 maxlennil,則使用串流的資料模式讀取所有位元組。

  • 否則,在二進位模式中讀取最多 maxlen 位元組。

傳回一個包含已讀取位元組的字串(新的字串或指定的 out_string)。字串的編碼取決於 maxLenout_string

  • maxlennil:使用 self 的內部編碼(不論是否指定 out_string)。

  • maxlen 不為 nil

    • 指定 out_string:不修改 out_string 的編碼。

    • 未指定 out_string:使用 ASCII-8BIT。

沒有參數 out_string

如果省略參數 out_string,傳回的值是新的字串

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

如果 maxlen 為零,則傳回空字串。

有參數 out_string

如果指定參數 out_string,傳回的值是 out_string,其內容會被取代

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

請注意,此方法的行為類似於 C 中的 fread() 函式。這表示它會重試呼叫 read(2) 系統呼叫以讀取資料(具有指定的 maxlen,或直到 EOF)。

即使串流處於非封鎖模式,此行為也會保留。(此方法與其他方法一樣,不受非封鎖旗標影響。)

如果您需要類似於單一 read(2) 系統呼叫的行為,請考慮 readpartialread_nonblocksysread

相關:IO#write

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

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

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

    shrinkable = io_setstrbuf(&str,len);

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

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

    return str;
}
read_nonblock(maxlen [, options]) → string 按一下以切換來源
read_nonblock(maxlen, outbuf [, options]) → outbuf

在為基礎檔案描述符設定 O_NONBLOCK 之後,使用 read(2) 系統呼叫從 ios 讀取最多 maxlen 位元組。

如果存在選擇性的 outbuf 參數,它必須參照一個 String,它會接收資料。即使在開始時並非為空,outbuf 在方法呼叫後只會包含已接收的資料。

read_nonblock 只會呼叫 read(2) 系統呼叫。它會導致 read(2) 系統呼叫導致的所有錯誤:Errno::EWOULDBLOCK、Errno::EINTR 等。呼叫者應注意此類錯誤。

如果例外情況為 Errno::EWOULDBLOCK 或 Errno::EAGAIN,則會由 IO::WaitReadable 延伸。因此,IO::WaitReadable 可用於救援例外情況,以重新嘗試 read_nonblock。

read_nonblock 會在 EOF 時導致 EOFError

在某些平台(例如 Windows)上,不支援套接字以外的 IO 物件的非封鎖模式。在這種情況下,將會引發 Errno::EBADF。

如果讀取位元組緩衝區不為空,read_nonblock 會像 readpartial 一樣從緩衝區讀取。在這種情況下,不會呼叫 read(2) 系統呼叫。

read_nonblock 引發 IO::WaitReadable 類型的例外情況時,read_nonblock 不應在 io 可讀取之前被呼叫,以避免忙碌迴圈。這可如下執行。

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

雖然 IO#read_nonblock 沒有引發 IO::WaitWritableOpenSSL::Buffering#read_nonblock 可以引發 IO::WaitWritable。如果 IO 和 SSL 應多型使用,也應救援 IO::WaitWritable。請參閱 OpenSSL::Buffering#read_nonblock 的文件以取得範例程式碼。

請注意,此方法與 readpartial 相同,除了設定非封鎖旗標。

透過指定關鍵字參數 exceptionfalse,你可以指出 read_nonblock 不應引發 IO::WaitReadable 例外情況,而應改為傳回符號 :wait_readable。在 EOF 時,它將傳回 nil,而不是引發 EOFError

# File io.rb, line 62
def read_nonblock(len, buf = nil, exception: true)
  Primitive.io_read_nonblock(len, buf, exception)
end
readbyte → integer 按一下以切換來源

從串流讀取並傳回下一個位元組(範圍 0..255);如果已在串流結尾,則引發 EOFError。請參閱 位元組 IO

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

相關:IO#getbyte(不會引發 EOFError)。

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

    if (NIL_P(c)) {
        rb_eof_error();
    }
    return c;
}
readchar → 字串 按一下以切換來源

從串流中讀取並傳回下一個 1 個字元的字串;如果已在串流結尾,則會引發 EOFError。請參閱 字元 IO

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

相關:IO#getc(不會引發 EOFError)。

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

    if (NIL_P(c)) {
        rb_eof_error();
    }
    return c;
}
readline(sep = $/, chomp: false) → 字串 按一下以切換來源
readline(limit, chomp: false) → 字串
readline(sep, limit, chomp: false) → 字串

讀取一行,如同 IO#gets,但如果已在串流結尾,則會引發 EOFError

選用關鍵字引數 chomp 指定是否要略過行分隔符號。

# File io.rb, line 133
def readline(sep = $/, limit = nil, chomp: false)
  Primitive.io_readline(sep, limit, chomp)
end
readlines(sep = $/, chomp: false) → 陣列 按一下以切換來源
readlines(limit, chomp: false) → 陣列
readlines(sep, limit, chomp: false) → 陣列

讀取並傳回串流中所有剩餘的行;不會修改 $_。請參閱 行 IO

如果未提供任何引數,則會傳回由行分隔符號 $/ 確定的行,或如果沒有行分隔符號,則會傳回 nil

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

如果只提供字串引數 sep,則會傳回由行分隔符號 sep 確定的行,或如果沒有行分隔符號,則會傳回 nil;請參閱 行分隔符號

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

會尊重 sep 的兩個特殊值

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

如果只提供整數參數 limit,則會限制每一行的位元組數目;請參閱 Line Limit

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

給定引數 seplimit,則結合這兩種行為

  • 傳回由行分隔符號 sep 確定的行。

  • 但不會傳回超過限制允許的行中的位元組。

選用的關鍵字參數 chomp 指定是否省略換行符號

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

    prepare_getline_args(argc, argv, &args, io);
    return io_readlines(&args, io);
}
readpartial(maxlen) → 字串 按一下以切換來源
readpartial(maxlen, out_string) → out_string

從串流中讀取最多 maxlen 個位元組;傳回字串(新的字串或指定的 out_string)。其編碼為

  • 如果提供 out_string,則為 out_string 的不變編碼。

  • 否則為 ASCII-8BIT。

  • 如果可用,則包含串流中的 maxlen 個位元組。

  • 否則包含所有可用的位元組(如果有的話)。

  • 否則為空字串。

如果只提供單一非負整數引數 maxlen,則會傳回新的字串

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

如果同時提供引數 maxlen 和字串引數 out_string,則會傳回修改後的 out_string

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

此方法對於串流(例如管線、socket 或 tty)很有用。它只會在沒有立即可用的資料時才會封鎖。這表示它只會在以下所有條件都成立時才會封鎖

  • 串流中的位元組緩衝區為空。

  • 串流的內容為空。

  • 串流未在 EOF。

當被封鎖時,此方法會等待串流上的更多資料或 EOF

  • 如果讀取到更多資料,此方法會傳回資料。

  • 如果已到達 EOF,此方法會引發 EOFError

當未被封鎖時,此方法會立即回應

  • 如果有資料,則從緩衝區傳回資料。

  • 否則,如果有資料,則從串流傳回資料。

  • 否則,如果串流已到達 EOF,則引發 EOFError

請注意,此方法類似於 sysread。不同之處在於

  • 如果位元組緩衝區不為空,則從位元組緩衝區讀取,而不是「針對緩衝的 IO (IOError) 執行 sysread」。

  • 它不會導致 Errno::EWOULDBLOCK 和 Errno::EINTR。當 readpartial 在 read 系統呼叫中遇到 EWOULDBLOCK 和 EINTR 時,readpartial 會重試系統呼叫。

後者表示 readpartial 不受非封鎖旗標影響。它會在 IO#sysread 導致 Errno::EWOULDBLOCK 的情況下封鎖,就像 fd 處於封鎖模式一樣。

範例

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

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

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

    ret = io_getpartial(argc, argv, io, Qnil, 0);
    if (NIL_P(ret))
        rb_eof_error();
    return ret;
}
ready? → true 或 false 按一下以切換來源

如果輸入可用且未封鎖,則傳回 true 值,否則傳回 false 值。

您必須需要「io/wait」才能使用此方法。

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

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

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

將串流重新關聯至另一個串流,該串流可能是不同類型的。此方法可用於將現有串流重新導向至新的目的地。

如果給定參數 other_io,則重新關聯至該串流

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

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

如果給定參數 path,則重新關聯至該檔案路徑的新串流

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

可選的關鍵字參數 opts 指定

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

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

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

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

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

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

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

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

    return file;
}
rewind → 0 按一下以切換來源

將串流重新定位至其開頭,將位置和行號都設定為零;請參閱 位置行號

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

請注意,此方法無法用於串流,例如管線、tty 和 socket。

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

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

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

尋找由整數 offset (請參閱 位置) 和常數 whence 給出的位置,其中常數 whence 為下列其中之一

  • :CURIO::SEEK_CUR:將串流重新定位至其目前位置加上給定的 offset

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

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

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

相關:IO#pos=IO#tell

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

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

    return rb_io_seek(io, offset, whence);
}
set_encoding(ext_enc) → self 按一下以切換來源
set_encoding(ext_enc, int_enc, **enc_opts) → self
set_encoding('ext_enc:int_enc', **enc_opts) → self

請參閱 編碼

如果給定,參數 ext_enc 必須是 Encoding 物件或具有編碼名稱的 String;它會指定為串流的編碼。

如果給定,參數 int_enc 必須是 Encoding 物件或具有編碼名稱的 String;它會指定為內部字串的編碼。

如果給定,參數 'ext_enc:int_enc' 是包含兩個以冒號分隔的編碼名稱的字串;對應的 Encoding 物件會指定為串流的外部和內部編碼。

如果字串的外部編碼是 binary/ASCII-8BIT,則會將字串的內部編碼設定為 nil,因為不需要轉碼。

選用的關鍵字參數 enc_opts 指定 編碼選項

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

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

    argc = rb_scan_args(argc, argv, "11:", &v1, &v2, &opt);
    GetOpenFile(io, fptr);
    io_encoding_set(fptr, v1, v2, opt);
    return io;
}
set_encoding_by_bom → 編碼或 nil 按一下以切換來源

如果串流以 BOM (位元組順序標記) 開頭,則會使用 BOM 並據此設定外部編碼;如果找到,則傳回結果編碼,否則傳回 nil

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

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

如果串流不是二進位模式或其編碼已設定,則會引發例外狀況。

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

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

傳回 ios 的狀態資訊,為 File::Stat 類型的物件。

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

    GetOpenFile(obj, fptr);
    if (fstat(fptr->fd, &st) == -1) {
        rb_sys_fail_path(fptr->pathv);
    }
    return rb_stat_new(&st);
}
sync → true 或 false 按一下以切換來源

傳回串流目前的同步模式。當同步模式為 true 時,所有輸出會立即快取到基礎作業系統,且不會由 Ruby 內部快取。另請參閱 fsync

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

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    return RBOOL(fptr->mode & FMODE_SYNC);
}
sync = 布林值 → 布林值 按一下以切換來源

將串流的 同步 模式 設定為指定值;傳回指定值。

同步模式的值

  • true:所有輸出會立即快取到基礎作業系統,且不會在內部快取。

  • false:輸出可能會在內部快取。

範例;

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

相關:IO#fsync

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

    io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    if (RTEST(sync)) {
        fptr->mode |= FMODE_SYNC;
    }
    else {
        fptr->mode &= ~FMODE_SYNC;
    }
    return sync;
}
sysread(maxlen) → 字串 按一下以切換來源
sysread(maxlen, out_string) → 字串

行為類似 IO#readpartial,但它使用低階系統函式。

這個方法不應該與其他串流讀取器方法一起使用。

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

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

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

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

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

    rb_io_check_closed(fptr);

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

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

    io_set_read_length(str, n, shrinkable);

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

    return str;
}
sysseek(offset, whence = IO::SEEK_SET) → 整數 按一下以切換來源

行為類似 IO#seek,但它

  • 使用低階系統函式。

  • 傳回新的位置。

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

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

    return OFFT2NUM(pos);
}
syswrite(object) → 整數 按一下以切換來源

將指定的 object 寫入 self,它必須開啟才能寫入(請參閱模式);傳回已寫入的位元組數。如果 object 不是字串,會透過 to_s 方法轉換

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

這個方法不應該與其他串流寫入器方法一起使用。

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

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

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

    if (fptr->wbuf.len) {
        rb_warn("syswrite for buffered IO");
    }

    tmp = rb_str_tmp_frozen_acquire(str);
    RSTRING_GETMEM(tmp, ptr, len);
    n = rb_io_write_memory(fptr, ptr, len);
    if (n < 0) rb_sys_fail_path(fptr->pathv);
    rb_str_tmp_frozen_release(str, tmp);

    return LONG2FIX(n);
}
tell → 整數 按一下以切換來源

傳回 self 中的目前位置(以位元組為單位)(請參閱 位置

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

相關:IO#pos=IO#seek

static VALUE
rb_io_tell(VALUE io)
{
    rb_io_t *fptr;
    rb_off_t pos;

    GetOpenFile(io, fptr);
    pos = io_tell(fptr);
    if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);
    pos -= fptr->rbuf.len;
    return OFFT2NUM(pos);
}
別名:pos
timeout → 持續時間或 nil 按一下以切換來源

取得內部逾時持續時間,如果未設定則為 nil。

VALUE
rb_io_timeout(VALUE self)
{
    rb_io_t *fptr = rb_io_get_fptr(self);

    return fptr->timeout;
}
timeout = duration → duration 按一下以切換來源
timeout = nil → nil

將內部逾時設定為指定的持續時間或 nil。逾時適用於所有可能的封鎖作業。

當作業執行時間超過設定的逾時,會引發 IO::TimeoutError

這會影響下列方法(但不限於):getsputsreadwritewait_readablewait_writable。這也會影響封鎖 socket 作業,例如 Socket#acceptSocket#connect

有些作業,例如 File#openIO#close,不受逾時影響。寫入作業期間逾時可能會讓 IO 處於不一致的狀態,例如資料已部分寫入。一般來說,逾時是避免應用程式因緩慢的 I/O 作業(例如在 slowloris 攻擊期間發生的作業)而當機的最後手段。

VALUE
rb_io_set_timeout(VALUE self, VALUE timeout)
{
    // Validate it:
    if (RTEST(timeout)) {
        rb_time_interval(timeout);
    }

    rb_io_t *fptr = rb_io_get_fptr(self);

    fptr->timeout = timeout;

    return self;
}
to_i
別名:fileno
to_io → self 按一下以切換來源

傳回 self

static VALUE
rb_io_to_io(VALUE io)
{
    return io;
}
to_path
別名:path
tty?
別名:isatty
ungetbyte(integer) → nil 按一下以切換來源
ungetbyte(string) → nil

將給定的資料推回串流的緩衝區(「取消移出」),放置資料以便於接下來讀取;傳回 nil。請參閱位元組 IO

請注意

  • 如果進行未緩衝的讀取(例如 IO#sysread),呼叫此方法不會產生任何效果。

  • 在串流上呼叫 rewind 會捨棄已推回的資料。

當給定引數 integer 時,只使用其低位元組

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetbyte(0x41)   # => nil
f.read              # => "A012"
f.rewind
f.ungetbyte(0x4243) # => nil
f.read              # => "C012"
f.close

當給定引數 string 時,使用所有位元組

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetbyte('A')    # => nil
f.read              # => "A012"
f.rewind
f.ungetbyte('BCDE') # => nil
f.read              # => "BCDE012"
f.close
VALUE
rb_io_ungetbyte(VALUE io, VALUE b)
{
    rb_io_t *fptr;

    GetOpenFile(io, fptr);
    rb_io_check_byte_readable(fptr);
    switch (TYPE(b)) {
      case T_NIL:
        return Qnil;
      case T_FIXNUM:
      case T_BIGNUM: ;
        VALUE v = rb_int_modulo(b, INT2FIX(256));
        unsigned char c = NUM2INT(v) & 0xFF;
        b = rb_str_new((const char *)&c, 1);
        break;
      default:
        SafeStringValue(b);
    }
    io_ungetbyte(b, fptr);
    return Qnil;
}
ungetc(integer) → nil 按一下以切換來源
ungetc(string) → nil

將給定的資料推回串流的緩衝區(「取消移出」),放置資料以便於接下來讀取;傳回 nil。請參閱字元 IO

請注意

  • 如果進行未緩衝的讀取(例如 IO#sysread),呼叫此方法不會產生任何效果。

  • 在串流上呼叫 rewind 會捨棄已推回的資料。

當給定引數 integer 時,將整數解釋為字元

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetc(0x41)     # => nil
f.read             # => "A012"
f.rewind
f.ungetc(0x0442)   # => nil
f.getc.ord         # => 1090
f.close

當給定引數 string 時,使用所有字元

File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetc('A')      # => nil
f.read      # => "A012"
f.rewind
f.ungetc("\u0442\u0435\u0441\u0442") # => nil
f.getc.ord      # => 1090
f.getc.ord      # => 1077
f.getc.ord      # => 1089
f.getc.ord      # => 1090
f.close
VALUE
rb_io_ungetc(VALUE io, VALUE c)
{
    rb_io_t *fptr;
    long len;

    GetOpenFile(io, fptr);
    rb_io_check_char_readable(fptr);
    if (FIXNUM_P(c)) {
        c = rb_enc_uint_chr(FIX2UINT(c), io_read_encoding(fptr));
    }
    else if (RB_BIGNUM_TYPE_P(c)) {
        c = rb_enc_uint_chr(NUM2UINT(c), io_read_encoding(fptr));
    }
    else {
        SafeStringValue(c);
    }
    if (NEED_READCONV(fptr)) {
        SET_BINARY_MODE(fptr);
        len = RSTRING_LEN(c);
#if SIZEOF_LONG > SIZEOF_INT
        if (len > INT_MAX)
            rb_raise(rb_eIOError, "ungetc failed");
#endif
        make_readconv(fptr, (int)len);
        if (fptr->cbuf.capa - fptr->cbuf.len < len)
            rb_raise(rb_eIOError, "ungetc failed");
        if (fptr->cbuf.off < len) {
            MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.capa-fptr->cbuf.len,
                    fptr->cbuf.ptr+fptr->cbuf.off,
                    char, fptr->cbuf.len);
            fptr->cbuf.off = fptr->cbuf.capa-fptr->cbuf.len;
        }
        fptr->cbuf.off -= (int)len;
        fptr->cbuf.len += (int)len;
        MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.off, RSTRING_PTR(c), char, len);
    }
    else {
        NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
        io_ungetbyte(c, fptr);
    }
    return Qnil;
}
wait(events, timeout) → event mask, false or nil 按一下以切換來源
wait(timeout = nil, mode = :read) → self, true, or false

等待 IO 準備好指定的事件,並傳回已準備好的事件子集,或在逾時時傳回虛假值。

事件可以是 IO::READABLEIO::WRITABLEIO::PRIORITY 的位元遮罩。

當有緩衝資料可用時,立即傳回真值。

選用參數 mode:read:write:read_write 之一。

您必須需要「io/wait」才能使用此方法。

static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
#ifndef HAVE_RB_IO_WAIT
    rb_io_t *fptr;
    struct timeval timerec;
    struct timeval *tv = NULL;
    int event = 0;
    int i;

    GetOpenFile(io, fptr);
    for (i = 0; i < argc; ++i) {
        if (SYMBOL_P(argv[i])) {
            event |= wait_mode_sym(argv[i]);
        }
        else {
            *(tv = &timerec) = rb_time_interval(argv[i]);
        }
    }
    /* rb_time_interval() and might_mode() might convert the argument */
    rb_io_check_closed(fptr);
    if (!event) event = RB_WAITFD_IN;
    if ((event & RB_WAITFD_IN) && rb_io_read_pending(fptr))
        return Qtrue;
    if (wait_for_single_fd(fptr, event, tv))
        return io;
    return Qnil;
#else
    VALUE timeout = Qundef;
    rb_io_event_t events = 0;
    int i, return_io = 0;

    /* The documented signature for this method is actually incorrect.
     * A single timeout is allowed in any position, and multiple symbols can be given.
     * Whether this is intentional or not, I don't know, and as such I consider this to
     * be a legacy/slow path. */
    if (argc != 2 || (RB_SYMBOL_P(argv[0]) || RB_SYMBOL_P(argv[1]))) {
        /* We'd prefer to return the actual mask, but this form would return the io itself: */
        return_io = 1;

        /* Slow/messy path: */
        for (i = 0; i < argc; i += 1) {
            if (RB_SYMBOL_P(argv[i])) {
                events |= wait_mode_sym(argv[i]);
            }
            else if (timeout == Qundef) {
                rb_time_interval(timeout = argv[i]);
            }
            else {
                rb_raise(rb_eArgError, "timeout given more than once");
            }
        }

        if (timeout == Qundef) timeout = Qnil;

        if (events == 0) {
            events = RUBY_IO_READABLE;
        }
    }
    else /* argc == 2 and neither are symbols */ {
        /* This is the fast path: */
        events = io_event_from_value(argv[0]);
        timeout = argv[1];
    }

    if (events & RUBY_IO_READABLE) {
        rb_io_t *fptr = NULL;
        RB_IO_POINTER(io, fptr);

        if (rb_io_read_pending(fptr)) {
            /* This was the original behaviour: */
            if (return_io) return Qtrue;
            /* New behaviour always returns an event mask: */
            else return RB_INT2NUM(RUBY_IO_READABLE);
        }
    }

    return io_wait_event(io, events, timeout, return_io);
#endif
}
別名也為:wait
wait_priority → true 或 false 按一下切換來源
wait_priority(timeout) → true 或 false

等到 IO 優先,並在逾時時傳回 true 或 false。優先資料使用 Socket::MSG_OOB 旗標傳送和接收,通常限制在串流。

您必須需要「io/wait」才能使用此方法。

static VALUE
io_wait_priority(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr = NULL;

    RB_IO_POINTER(io, fptr);
    rb_io_check_readable(fptr);

    if (rb_io_read_pending(fptr)) return Qtrue;

    rb_check_arity(argc, 0, 1);
    VALUE timeout = argc == 1 ? argv[0] : Qnil;

    return io_wait_event(io, RUBY_IO_PRIORITY, timeout, 1);
}
別名:wait_priority
wait_readable → true 或 false 按一下切換來源
wait_readable(timeout) → true 或 false

等到 IO 可讀,並在逾時時傳回 true 或 false。當有緩衝資料可用時,會立即傳回 true。

您必須需要「io/wait」才能使用此方法。

static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
    struct timeval timerec;
    struct timeval *tv;
#endif

    GetOpenFile(io, fptr);
    rb_io_check_readable(fptr);

#ifndef HAVE_RB_IO_WAIT
    tv = get_timeout(argc, argv, &timerec);
#endif
    if (rb_io_read_pending(fptr)) return Qtrue;

#ifndef HAVE_RB_IO_WAIT
    if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) {
        return io;
    }
    return Qnil;
#else
    rb_check_arity(argc, 0, 1);
    VALUE timeout = (argc == 1 ? argv[0] : Qnil);

    return io_wait_event(io, RUBY_IO_READABLE, timeout, 1);
#endif
}
別名:wait_readable
wait_writable → true 或 false 按一下切換來源
wait_writable(timeout) → true 或 false

等到 IO 可寫,並在逾時時傳回 true 或 false。

您必須需要「io/wait」才能使用此方法。

static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
    struct timeval timerec;
    struct timeval *tv;
#endif

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

#ifndef HAVE_RB_IO_WAIT
    tv = get_timeout(argc, argv, &timerec);
    if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) {
        return io;
    }
    return Qnil;
#else
    rb_check_arity(argc, 0, 1);
    VALUE timeout = (argc == 1 ? argv[0] : Qnil);

    return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1);
#endif
}
別名:wait_writable
winsize → [列數, 行數] 按一下切換來源

傳回主控台大小。

您必須需要「io/console」才能使用此方法。

static VALUE
console_winsize(VALUE io)
{
    rb_console_size_t ws;
    int fd = GetWriteFD(io);
    if (!getwinsize(fd, &ws)) sys_fail(io);
    return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
}
winsize = [列數, 行數] 按一下切換來源

嘗試設定主控台大小。效果會依平台和執行環境而定。

您必須需要「io/console」才能使用此方法。

static VALUE
console_set_winsize(VALUE io, VALUE size)
{
    rb_console_size_t ws;
#if defined _WIN32
    HANDLE wh;
    int newrow, newcol;
    BOOL ret;
#endif
    VALUE row, col, xpixel, ypixel;
    const VALUE *sz;
    long sizelen;
    int fd;

    size = rb_Array(size);
    if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
        rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen);
    }
    sz = RARRAY_CONST_PTR(size);
    row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
    if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
    fd = GetWriteFD(io);
#if defined TIOCSWINSZ
    ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
#define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
    SET(row);
    SET(col);
    SET(xpixel);
    SET(ypixel);
#undef SET
    if (!setwinsize(fd, &ws)) sys_fail(io);
#elif defined _WIN32
    wh = (HANDLE)rb_w32_get_osfhandle(fd);
#define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
    SET(row);
    SET(col);
#undef SET
    if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel);
    if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel);
    if (!GetConsoleScreenBufferInfo(wh, &ws)) {
        rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
    }
    ws.dwSize.X = newcol;
    ret = SetConsoleScreenBufferSize(wh, ws.dwSize);
    ws.srWindow.Left = 0;
    ws.srWindow.Top = 0;
    ws.srWindow.Right = newcol-1;
    ws.srWindow.Bottom = newrow-1;
    if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
        rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
    }
    /* retry when shrinking buffer after shrunk window */
    if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) {
        rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
    }
    /* remove scrollbar if possible */
    if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
        rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
    }
#endif
    return io;
}
write(*objects) → 整數 按一下切換來源

將每個指定的 objects 寫入 self,而 self 必須開啟為寫入(請參閱 存取模式);傳回寫入的總位元組數;每個不是字串的 objects 會透過方法 to_s 轉換

$stdout.write('Hello', ', ', 'World!', "\n") # => 14
$stdout.write('foo', :bar, 2, "\n")          # => 8

輸出

Hello, World!
foobar2

相關:IO#read

static VALUE
io_write_m(int argc, VALUE *argv, VALUE io)
{
    if (argc != 1) {
        return io_writev(argc, argv, io);
    }
    else {
        VALUE str = argv[0];
        return io_write(io, str, 0);
    }
}
write_nonblock(字串) → 整數 按一下切換來源
write_nonblock(字串 [, 選項]) → 整數

在為基礎檔案描述符設定 O_NONBLOCK 之後,使用 write(2) 系統呼叫將指定的字串寫入 ios

它會傳回寫入的位元組數。

write_nonblock 只會呼叫 write(2) 系統呼叫。它會導致 write(2) 系統呼叫會導致的所有錯誤:Errno::EWOULDBLOCK、Errno::EINTR 等。結果也可能小於 string.length(部分寫入)。呼叫者應該注意這些錯誤和部分寫入。

如果例外狀況是 Errno::EWOULDBLOCK 或 Errno::EAGAIN,它會由 IO::WaitWritable 延伸。因此,IO::WaitWritable 可用來救援例外狀況,以重試 write_nonblock。

# Creates a pipe.
r, w = IO.pipe

# write_nonblock writes only 65536 bytes and return 65536.
# (The pipe size is 65536 bytes on this environment.)
s = "a" * 100000
p w.write_nonblock(s)     #=> 65536

# write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
p w.write_nonblock("b")   # Resource temporarily unavailable (Errno::EAGAIN)

如果寫入緩衝區不為空,則會先將其清除。

write_nonblock 引發 IO::WaitWritable 類型的例外狀況時,write_nonblock 不應在 io 可寫入前呼叫,以避免忙碌迴圈。這可以透過下列方式完成。

begin
  result = io.write_nonblock(string)
rescue IO::WaitWritable, Errno::EINTR
  IO.select(nil, [io])
  retry
end

請注意,這無法保證寫入字串中的所有資料。寫入的長度會回報為結果,且應在稍後檢查。

在某些平台(例如 Windows)上,write_nonblock 不支援 IO 物件的類型。在這種情況下,write_nonblock 會引發 Errno::EBADF

透過將關鍵字引數 exception 指定為 false,您可以指出 write_nonblock 不應引發 IO::WaitWritable 例外狀況,而應傳回符號 :wait_writable

# File io.rb, line 120
def write_nonblock(buf, exception: true)
  Primitive.io_write_nonblock(buf, exception)
end