類別 IO
類別 IO 的執行個體(通常稱為「串流」)代表底層作業系統中的輸入/輸出串流。類別 IO 是 Ruby 中輸入和輸出的基礎。
類別 File
是 Ruby 核心程式碼中唯一一個是 IO 子類別的類別。Ruby 標準函式庫中的一些類別也是 IO 的子類別;這些類別包括 TCPSocket
和 UDPSocket
。
全域常數 ARGF
(也可以存取為 $<
)提供一個類似 IO 的串流,允許存取 ARGV 中找到的所有檔案路徑(如果 ARGV 為空,則在 STDIN 中找到)。ARGF
本身不是 IO 的子類別。
類別 StringIO
提供一個類似 IO 的串流,用於處理 String
。StringIO
本身不是 IO 的子類別。
基於 IO 的重要物件包括
-
$stdin。
-
$stdout。
-
$stderr。
-
類別
File
的執行個體。
可以使用下列方式建立 IO 的執行個體
-
IO.new
:傳回給定整數檔案描述詞的新 IO 物件。 -
IO.open
:將新的 IO 物件傳遞給指定的區塊。 -
IO.popen
:傳回一個新的 IO 物件,該物件會連接到新啟動子程序的 $stdin 和 $stdout。 -
Kernel#open
:傳回一個新的 IO 物件,連接到指定的來源:串流、檔案或子程序。
與 File
串流一樣,IO 串流具有
與其他 IO 串流一樣,它具有
擴充模組 io/console
¶ ↑
擴充模組 io/console
提供許多與主控台互動的方法;需要它才能將許多方法新增到 IO 類別。
範例檔案¶ ↑
這裡的許多範例都使用這些變數
# English text with newlines. text = <<~EOT First line Second line Fourth line Fifth line EOT # Russian text. russian = "\u{442 435 441 442}" # => "тест" # Binary data. data = "\u9990\u9991\u9992\u9993\u9994" # Text file. File.write('t.txt', text) # File with Russian text. File.write('t.rus', russian) # File with binary data. f = File.new('t.dat', 'wb:UTF-16') f.write(data) f.close
開啟選項¶ ↑
許多 IO 方法接受決定如何開啟新串流的選用關鍵字引數
-
:mode
:串流模式。 -
:flags
:Integer
檔案開啟旗標;如果也給定mode
,則兩者會進行位元運算 OR。 -
:external_encoding
:串流的外部編碼。 -
:internal_encoding
:串流的內部編碼。'-'
是預設內部編碼的同義詞。如果值為nil
,則不會進行轉換。 -
:encoding
:將外部和內部編碼指定為'extern:intern'
。 -
:textmode
:如果為真值,則將模式指定為純文字,否則為二進制。 -
:binmode
:如果為真值,則將模式指定為二進制,否則為純文字。 -
:autoclose
:如果為真值,則指定fd
會在串流關閉時關閉;否則它會保持開啟。
在 String#encode
中提供的選項也可用,它可以控制外部和內部編碼之間的轉換。
基本 IO¶ ↑
您可以使用這些方法執行基本串流 IO,它們通常作用於多位元組字串
位置¶ ↑
IO 串流具有非負整數位置,這是下一次讀取或寫入將發生的位元組偏移量。新串流的位置為零(且行號為零);方法 rewind
會將位置(和行號)重設為零。
相關方法
-
IO#tell
(別名為#pos
):傳回串流中目前的位元組位置。 -
IO#pos=
:將串流的位置設定為給定的整數new_position
(以位元組為單位)。 -
IO#seek
:將串流的位置設定為給定的整數offset
(以位元組為單位),相對於給定的位置whence
(表示開頭、結尾或目前位置)。 -
IO#rewind
:將串流定位在開頭(同時重設行號)。
開啟和關閉串流¶ ↑
新的 IO 串流可以開啟為讀取、開啟為寫入,或同時開啟。
當垃圾收集器宣告串流時,串流會自動關閉。
嘗試在已關閉的串流上讀取或寫入會引發例外狀況。
相關方法
-
IO#close
:關閉串流以進行讀取和寫入。 -
IO#close_read
:關閉串流以進行讀取。 -
IO#close_write
:關閉串流以進行寫入。 -
IO#closed?
:傳回串流是否已關閉。
串流結束¶ ↑
你可以查詢串流是否定位在串流結束處
-
IO#eof?
(也別名為#eof
):傳回串流是否在串流結束處。
你可以使用 IO#seek
方法重新定位到串流結束處
f = File.new('t.txt') f.eof? # => false f.seek(0, :END) f.eof? # => true f.close
或透過讀取所有串流內容(比使用 IO#seek
慢)
f.rewind f.eof? # => false f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.eof? # => true
行 IO¶ ↑
你可以使用下列方法逐行讀取 IO 串流
-
IO#each_line
:讀取每個剩餘行,傳遞給指定的區塊。 -
IO#gets
:傳回下一行。 -
IO#readline
:與gets
類似,但在串流結束時引發例外狀況。 -
IO#readlines
:傳回陣列中所有剩餘行。
每個讀取方法都接受
對於每個讀取方法,讀取可能會從行中間開始,具體取決於串流的位置;請參閱 位置
f = File.new('t.txt') f.pos = 27 f.each_line {|line| p line } f.close
輸出
"rth line\n" "Fifth line\n"
你可以使用這個方法逐行寫入 IO 串流
-
IO#puts
:寫入物件到串流。
行分隔符¶ ↑
這些方法各使用一個行分隔符,它是區分行的字串
預設的行分隔符是由全域變數 $/
給定的,它的值預設為 "\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
有兩個特殊行分隔符
-
nil
:將整個串流讀入單一字串f = File.new('t.txt') f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.close
-
''
(空字串):讀取下一個「段落」(段落由兩個連續的行分隔符分隔)f = File.new('t.txt') f.gets('') # => "First line\nSecond line\n\n" f.gets('') # => "Fourth line\nFifth line\n" f.close
行限制¶ ↑
這些方法各使用一個行限制,它指定傳回的位元組數目可能不會(多)長於給定的 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
行分隔符和行限制¶ ↑
給定引數 sep
和 limit
,則結合這兩種行為
-
傳回由行分隔符
sep
決定的下一行。 -
但傳回不超過限制允許的位元組數目。
範例
File.open('t.txt') {|f| f.gets('li', 20) } # => "First li" File.open('t.txt') {|f| f.gets('li', 2) } # => "Fi"
行號¶ ↑
可讀的 IO 串流有一個非負整數行號。
相關方法
-
IO#lineno
:傳回行號。 -
IO#lineno=
:重設並傳回行號。
除非呼叫方法 IO#lineno=
進行修改,否則行號會根據指定的行分隔符號 sep
,計算某些面向行的方法所讀取的行數
-
IO.foreach
:每次呼叫區塊時,都會遞增行號。 -
IO#each_line
:每次呼叫區塊時,都會遞增行號。 -
IO#gets
:遞增行號。 -
IO#readline
:遞增行號。 -
IO#readlines
:每次讀取一行時,都會遞增行號。
新串流的初始行號為零(且位置為零);方法 rewind
會將行號(和位置)重設為零
f = File.new('t.txt') f.lineno # => 0 f.gets # => "First line\n" f.lineno # => 1 f.rewind f.lineno # => 0 f.close
從串流讀取行通常會變更其行號
f = File.new('t.txt', 'r') f.lineno # => 0 f.readline # => "This is line one.\n" f.lineno # => 1 f.readline # => "This is the second line.\n" f.lineno # => 2 f.readline # => "Here's the third line.\n" f.lineno # => 3 f.eof? # => true f.close
在串流中反覆運算行通常會變更其行號
File.open('t.txt') do |f| f.each_line do |line| p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" end end
輸出
"position=11 eof?=false lineno=1" "position=23 eof?=false lineno=2" "position=24 eof?=false lineno=3" "position=36 eof?=false lineno=4" "position=47 eof?=true lineno=5"
與串流的 位置 不同,行號不會影響下一次讀取或寫入的位置
f = File.new('t.txt') f.lineno = 1000 f.lineno # => 1000 f.gets # => "First line\n" f.lineno # => 1001 f.close
與行號關聯的全球變數為 $.
-
開啟串流時,不會設定
$.
;其值會保留自處理序中前一個活動$. = 41 f = File.new('t.txt') $. = 41 # => 41 f.close
-
讀取串流時,會將
$.
設定為該串流的行號f0 = File.new('t.txt') f1 = File.new('t.dat') f0.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f1.readlines # => ["\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"] $. # => 1 f0.close f1.close
-
方法
IO#rewind
和IO#seek
不會影響$.
f = File.new('t.txt') f.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f.rewind f.seek(0, :SET) $. # => 5 f.close
字元 IO¶ ↑
您可以使用這些方法逐字元處理 IO 串流
-
IO#getc
:從串流讀取並傳回下一個字元。 -
IO#readchar
:類似於getc
,但會在串流結束時引發例外狀況。 -
IO#ungetc
:將字元或整數推回(「取消移位」)到串流中。 -
IO#putc
:將字元寫入串流。 -
IO#each_char
:讀取串流中剩下的每個字元,並將字元傳遞給指定的區塊。
位元組 IO¶ ↑
你可以使用下列方法逐位元組處理 IO 串流
-
IO#getbyte
:傳回下一個 8 位元組位元組,範圍為 0..255 的整數。 -
IO#readbyte
:類似於getbyte
,但若在串流尾端會引發例外狀況。 -
IO#ungetbyte
:將一個位元組推回串流中(「取消位移」)。 -
IO#each_byte
:讀取串流中剩下的每個位元組,將位元組傳遞給指定的區塊。
碼點 IO¶ ↑
你可以逐碼點處理 IO 串流
-
IO#each_codepoint
:讀取剩下的每個碼點,傳遞給指定的區塊。
本處內容¶ ↑
首先,說明其他地方的內容。類別 IO
-
繼承自 類別 Object。
-
包含 模組 Enumerable,提供數十種其他方法。
在此,類別 IO 提供對下列事項有用的方法
建立¶ ↑
-
::open
:建立一個新的 IO 物件。 -
::pipe
:建立一對連線的讀取器和寫入器 IO 物件。 -
::popen
:建立一個 IO 物件,用於與子程序互動。 -
::select
:選擇哪些指定的 IO 實例已準備好讀取、寫入或有待處理的例外狀況。
讀取¶ ↑
-
::binread
:傳回一個二進制字串,其中包含來自指定檔案的所有或部分位元組。 -
::read
:傳回一個字串,其中包含來自指定檔案的所有或部分位元組。 -
::readlines
:傳回一個字串陣列,其中包含來自指定檔案的各行。 -
getbyte
:傳回從self
讀取的下一 8 位元組位元組,為整數。 -
getc
:傳回從self
讀取的下一字元,為字串。 -
gets
:傳回從self
讀取的行。 -
pread
:傳回從self
讀取的所有或下一 n 位元組,不會更新接收者的偏移量。 -
read
:傳回從self
讀取的所有或下一 n 位元組,n 為給定值。 -
read_nonblock
:傳回從self
讀取的下一 n 位元組,n 為給定值,處於非封鎖模式。 -
readline
:傳回從self
讀取的下一行;與 getline 相同,但會在串流結束時引發例外狀況。 -
readlines
:傳回從self
讀取的所有行的陣列。 -
readpartial
:傳回從self
讀取的給定位元組數目。
寫入¶ ↑
-
::binwrite
:以二進位模式將給定字串寫入給定檔案路徑的檔案。 -
::write
:將給定字串寫入self
。 -
<<
:將給定字串附加到self
。 -
print
:將最後讀取的行或給定物件印出到self
。 -
printf
:根據給定的格式字串和物件寫入self
。 -
putc
:將字元寫入self
。 -
puts
:將行寫入self
,確保行以換行字元結束。 -
pwrite
:將給定字串寫入給定偏移量,不會更新接收者的偏移量。 -
write
:將一個或多個給定的字串寫入self
。 -
write_nonblock
:在非封鎖模式下將一個或多個給定的字串寫入self
。
定位¶ ↑
-
lineno
:傳回self
中的目前行號。 -
lineno=
:設定self
中的行號。 -
pos=
:設定self
中的位元組偏移量。 -
reopen
:將self
重新關聯至新的或現有的 IO 串流。 -
rewind
:將self
定位至輸入的開頭。 -
seek
:設定self
相對於給定位置的偏移量。
反覆處理¶ ↑
-
::foreach
:將給定檔案的每一行傳送至區塊。 -
each_byte
:呼叫給定的區塊,並傳入self
中的每個連續位元組(以整數表示)。 -
each_char
:呼叫給定的區塊,並傳入self
中的每個連續字元(以字串表示)。 -
each_codepoint
:呼叫給定的區塊,並傳入self
中的每個連續碼點(以整數表示)。
設定¶ ↑
-
autoclose=
:設定self
是否自動關閉。 -
binmode
:將self
設定為二進位模式。 -
close
:關閉self
。 -
close_on_exec=
:設定 close-on-exec 旗標。 -
close_read
:關閉self
的讀取功能。 -
close_write
:關閉self
的寫入功能。 -
set_encoding
:設定self
的編碼。 -
set_encoding_by_bom
:根據self
的 Unicode 位元組順序標記設定其編碼。 -
sync=
:將同步模式設定為指定的值。
查詢¶ ↑
-
autoclose?
:傳回self
是否會自動關閉。 -
binmode?
:傳回self
是否為二進位模式。 -
close_on_exec?
:傳回self
的 close-on-exec 旗標。 -
closed?
:傳回self
是否已關閉。 -
external_encoding
:傳回self
的外部編碼物件。 -
internal_encoding
:傳回self
的內部編碼物件。 -
stat
:傳回包含self
狀態資訊的File::Stat
物件。 -
sync
:傳回self
是否為同步模式。
緩衝¶ ↑
-
fdatasync
:立即將self
中所有緩衝資料寫入磁碟。 -
flush
:將self
中的任何緩衝資料沖刷到底層作業系統。 -
fsync
:立即將self
中所有緩衝資料和屬性寫入磁碟。 -
ungetbyte
:在self
的緩衝區之前加上指定的整數位元組或字串。 -
ungetc
:在self
的緩衝區之前加上指定的字串。
低階存取¶ ↑
-
::sysopen
:開啟由其路徑指定的檔案,傳回整數檔案描述子。 -
advise
:宣告以特定方式存取self
中資料的意圖。 -
fcntl
:將低階指令傳遞給由指定檔案描述子指定的檔案。 -
ioctl
:將低階指令傳遞給由指定檔案描述子指定的裝置。 -
sysread
:使用低階讀取傳回從self
讀取的最多下一個 n 個位元組。 -
sysseek
:設定self
的偏移量。 -
syswrite
:使用低階寫入將給定的字串寫入self
。
其他¶ ↑
-
::copy_stream
:將資料從來源複製到目的地,每個都是檔案路徑或類似 IO 的物件。 -
::try_convert
:傳回將給定物件轉換後所產生的新 IO 物件。 -
inspect
:傳回self
的字串表示。
常數
公開類別方法
行為類似於 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); }
傳回開啟的 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; }
從給定的 src
複製到給定的 dst
,傳回複製的位元組數。
-
指定的
src
必須符合下列其中一項-
可讀取檔案的路徑,用於讀取來源資料。
-
已開啟供讀取的 IO 類似物件,且能回應方法
:readpartial
或方法:read
。
-
-
指定的
dst
必須符合下列其中一項-
可寫入檔案的路徑,用於寫入資料。
-
已開啟供寫入的 IO 類似物件,且能回應方法
:write
。
-
此處的範例使用檔案 t.txt
作為來源
File.read('t.txt') # => "First line\nSecond line\n\nThird line\nFourth line\n" File.read('t.txt').size # => 47
如果只提供引數 src
和 dst
,會複製整個來源串流
# Paths. IO.copy_stream('t.txt', 't.tmp') # => 47 # IOs (recall that a File is also an IO). src_io = File.open('t.txt', 'r') # => #<File:t.txt> dst_io = File.open('t.tmp', 'w') # => #<File:t.tmp> IO.copy_stream(src_io, dst_io) # => 47 src_io.close dst_io.close
如果提供引數 src_length
(非負整數),會複製不超過該數量的位元組
IO.copy_stream('t.txt', 't.tmp', 10) # => 10 File.read('t.tmp') # => "First line"
如果也提供引數 src_offset
,會從該偏移量開始讀取來源串流
IO.copy_stream('t.txt', 't.tmp', 11, 11) # => 11 IO.read('t.tmp') # => "Second line"
static VALUE rb_io_s_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); }
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; }
呼叫區塊,並提供從串流讀取的每一行。
當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入。
第一個引數必須是字串,也就是檔案的路徑。
如果只提供引數 path
,會根據預設行分隔符號,剖析指定 path
檔案中的行,並呼叫區塊,並提供每一行
File.foreach('t.txt') {|line| p line }
輸出:與上述相同。
對於命令和路徑這兩種形式,其餘引數都相同。
如果提供引數 sep
,會根據該行分隔符號剖析行(請參閱 行分隔符號)
File.foreach('t.txt', 'li') {|line| p line }
輸出
"First li" "ne\nSecond li" "ne\n\nThird li" "ne\nFourth li" "ne\n"
每一段落
File.foreach('t.txt', '') {|paragraph| p paragraph }
輸出
"First line\nSecond line\n\n" "Third line\nFourth line\n"
如果提供引數 limit
,會根據預設行分隔符號和指定的行長度限制剖析行(請參閱 行限制)
File.foreach('t.txt', 7) {|line| p line }
輸出
"First l" "ine\n" "Second " "line\n" "\n" "Third l" "ine\n" "Fourth l" "line\n"
給予參數 sep
和 limit
時,會根據給定的行分隔符號和給定的行長限制來解析行(請參閱 行分隔符號和行限制)
可選的關鍵字參數 opts
指定
如果沒有給定區塊,則傳回 Enumerator
。
static VALUE rb_io_s_foreach(int argc, VALUE *argv, VALUE self) { VALUE opt; int orig_argc = argc; struct foreach_arg arg; struct getline_arg garg; argc = rb_scan_args(argc, argv, "12:", NULL, NULL, NULL, &opt); RETURN_ENUMERATOR(self, orig_argc, argv); extract_getline_args(argc-1, argv+1, &garg); open_key_args(self, argc, argv, opt, &arg); if (NIL_P(arg.io)) return Qnil; extract_getline_opts(opt, &garg); check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io); return rb_ensure(io_s_foreach, (VALUE)&garg, rb_io_close, arg.io); }
從檔案描述符建立並傳回新的 IO 物件(檔案串流)。
IO.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; }
透過 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; }
建立一對管線端點,read_io
和 write_io
,彼此連接。
如果給定參數 enc_string
,它必須是包含下列其中一項的字串
-
要作為外部編碼使用的編碼名稱。
-
作為外部編碼和內部編碼使用的兩個編碼的冒號分隔名稱。
如果給定參數 int_enc
,它必須是 Encoding
物件或編碼名稱字串,用來指定要使用的內部編碼;如果也給定參數 ext_enc
,它必須是 Encoding
物件或編碼名稱字串,用來指定要使用的外部編碼。
從 read_io
讀取的字串會標記為外部編碼;如果也指定了內部編碼,則會將字串轉換為該編碼,並標記為該編碼。
如果指定任何編碼,則選用的雜湊引數會指定轉換選項。
可選的關鍵字參數 opts
指定
未提供區塊時,會在陣列中傳回兩個端點
IO.pipe # => [#<IO:fd 4>, #<IO:fd 5>]
提供區塊時,會呼叫具有兩個端點的區塊;關閉兩個端點並傳回區塊的值
IO.pipe {|read_io, write_io| p read_io; p write_io }
輸出
#<IO:fd 6> #<IO:fd 7>
並非所有平台都可用。
在以下範例中,兩個處理程序會關閉它們未使用的管線端點。這不只是美觀的細微差別。如果任何寫入器仍開啟管線,則管線的讀取端不會產生檔案結束條件。在父處理程序的情況下,如果未先發出 wr.close
,rd.read
永遠不會傳回
rd, wr = IO.pipe if fork wr.close puts "Parent got: <#{rd.read}>" rd.close Process.wait else rd.close puts 'Sending message to parent' wr.write "Hi Dad" wr.close end
產生
Sending message to parent Parent got: <Hi Dad>
static VALUE rb_io_s_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; }
將指定的命令 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
指定
-
開啟選項.
-
編碼選項.
-
Kernel#spawn
的選項。
分岔的處理程序
當引數 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.pipe
和 Kernel.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); }
開啟串流,讀取並傳回部分或全部內容,並關閉串流;如果未讀取任何位元組,傳回 nil
。
當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入。
第一個引數必須是字串,也就是檔案的路徑。
僅提供參數 path
時,以文字模式讀取並傳回指定路徑中檔案的全部內容
IO.read('t.txt') # => "First line\nSecond line\n\nThird line\nFourth line\n"
在 Windows 上,當遇到某些特殊位元組時,文字模式會終止讀取並在檔案中留下未讀取的位元組。如果應讀取檔案中的所有位元組,請考慮使用 IO.binread
。
使用參數 length
時,如果可用,傳回 length
位元組
IO.read('t.txt', 7) # => "First l" IO.read('t.txt', 700) # => "First line\r\nSecond line\r\n\r\nFourth line\r\nFifth line\r\n"
使用參數 length
和 offset
時,如果可用,傳回 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); }
傳回從串流中讀取的所有行的陣列。
當從類別 IO(但不是 IO 的子類別)呼叫時,如果使用不受信任的輸入呼叫此方法,可能會產生安全性漏洞;請參閱 命令注入。
第一個引數必須是字串,也就是檔案的路徑。
僅提供參數 path
時,根據預設行分隔符號,從指定 path
中的檔案分析行,並在陣列中傳回這些行
IO.readlines('t.txt') # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
如果提供引數 sep
,會根據該行分隔符號剖析行(請參閱 行分隔符號)
# Ordinary separator. IO.readlines('t.txt', 'li') # =>["First li", "ne\nSecond li", "ne\n\nThird li", "ne\nFourth li", "ne\n"] # Get-paragraphs separator. IO.readlines('t.txt', '') # => ["First line\nSecond line\n\n", "Third line\nFourth line\n"] # Get-all separator. IO.readlines('t.txt', nil) # => ["First line\nSecond line\n\nThird line\nFourth line\n"]
如果提供引數 limit
,會根據預設行分隔符號和指定的行長度限制剖析行(請參閱 行限制)
IO.readlines('t.txt', 7) # => ["First l", "ine\n", "Second ", "line\n", "\n", "Third l", "ine\n", "Fourth ", "line\n"]
給予參數 sep
和 limit
時,會根據給定的行分隔符號和給定的行長限制來解析行(請參閱 行分隔符號和行限制)
可選的關鍵字參數 opts
指定
static VALUE rb_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(2),它會監控多個檔案描述符,等到一個或多個檔案描述符準備好進行某類 I/O 作業時才停止等待。
並非所有平台都實作此功能。
每個參數 read_ios
、write_ios
和 error_ios
都是 IO
物件的陣列。
參數 timeout
是以秒為單位的整數逾時間隔。
此方法會監控所有三個陣列中指定的 IO 物件,等待某些物件準備好;傳回 3 個元素的陣列,其元素為
-
準備好讀取的
read_ios
中的物件陣列。 -
準備好寫入的
write_ios
中的物件陣列。 -
error_ios
中有待處理例外狀況的物件陣列。
如果在給定的 timeout
內沒有任何物件準備好,則傳回 nil
。
IO.select 會窺探 IO 物件的緩衝區以測試可讀性。如果 IO 緩衝區不為空,IO.select 會立即通知可讀性。此「窺探」僅發生在 IO 物件上。它不會發生在類 IO 物件上,例如 OpenSSL::SSL::SSLSocket
。
使用 IO.select 的最佳方式是在 read_nonblock
、write_nonblock
等非封鎖方法後呼叫它。這些方法會引發 IO::WaitReadable
或 IO::WaitWritable
延伸的例外狀況。這些模組會通知呼叫者應如何使用 IO.select 等待。如果引發 IO::WaitReadable
,則呼叫者應等待讀取。如果引發 IO::WaitWritable
,則呼叫者應等待寫入。
因此,封鎖讀取 (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_nonblock
和 IO.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); }
使用給定的模式和權限開啟給定路徑中的檔案;傳回整數檔案描述符。
如果檔案可讀取,則它必須存在;如果檔案可寫入且不存在,則會使用給定的權限建立檔案
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); }
嘗試透過方法 to_io
將 object
轉換為 IO 物件;如果成功,則傳回新的 IO 物件,否則傳回 nil
IO.try_convert(STDOUT) # => #<IO:<STDOUT>> IO.try_convert(ARGF) # => #<IO:<STDIN>> IO.try_convert('STDOUT') # => nil
static VALUE rb_io_s_try_convert(VALUE dummy, VALUE io) { return rb_io_check_io(io); }
開啟串流,將給定的 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); }
公開實例方法
將給定的 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; }
呼叫 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 }
設定自動關閉旗標。
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; }
如果 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)); }
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; }
將串流的資料模式設定為二進位(請參閱 資料模式)。
串流的資料模式無法從二進位變更為文字。
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; }
如果串流為二進位模式,則傳回 true
,否則傳回 false
。請參閱 資料模式。
static VALUE rb_io_binmode_p(VALUE io) { rb_io_t *fptr; GetOpenFile(io, fptr); return RBOOL(fptr->mode & FMODE_BINMODE); }
_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; }
static VALUE console_clear_screen(VALUE io) { console_erase_screen(io, INT2FIX(2)); console_goto(io, INT2FIX(0), INT2FIX(0)); return io; }
關閉串流以進行讀取和寫入(如果已開啟讀取或寫入);傳回 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_read
、IO#close_write
、IO#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 旗標。
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; }
如果串流會在執行時關閉,則傳回 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; }
如果已開啟讀取,則關閉串流以進行讀取;傳回 nil
。請參閱 開啟和關閉串流。
如果串流是由 IO.popen
開啟的,且也關閉寫入,則設定全域變數 $?
(子項退出狀態)。
範例
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close_write puts pipe.closed? pipe.close_read puts $? puts pipe.closed? end
輸出
false false pid 14748 exit 0 true
相關:IO#close
、IO#close_write
、IO#closed?
。
static VALUE rb_io_close_read(VALUE io) { rb_io_t *fptr; VALUE write_io; fptr = rb_io_get_fptr(rb_io_taint_check(io)); if (fptr->fd < 0) return Qnil; if (is_socket(fptr->fd, fptr->pathv)) { #ifndef SHUT_RD # define SHUT_RD 0 #endif if (shutdown(fptr->fd, SHUT_RD) < 0) rb_sys_fail_path(fptr->pathv); fptr->mode &= ~FMODE_READABLE; if (!(fptr->mode & FMODE_WRITABLE)) return rb_io_close(io); return Qnil; } write_io = GetWriteIO(io); if (io != write_io) { rb_io_t *wfptr; wfptr = rb_io_get_fptr(rb_io_taint_check(write_io)); wfptr->pid = fptr->pid; fptr->pid = 0; RFILE(io)->fptr = wfptr; /* bind to write_io temporarily to get rid of memory/fd leak */ fptr->tied_io_for_writing = 0; RFILE(write_io)->fptr = fptr; rb_io_fptr_cleanup(fptr, FALSE); /* should not finalize fptr because another thread may be reading it */ return Qnil; } if ((fptr->mode & (FMODE_DUPLEX|FMODE_WRITABLE)) == FMODE_WRITABLE) { rb_raise(rb_eIOError, "closing non-duplex IO for reading"); } return rb_io_close(io); }
如果已開啟寫入,則關閉串流以進行寫入;傳回 nil
。請參閱 開啟和關閉串流。
在關閉前,將任何緩衝寫入沖刷至作業系統。
如果串流是由 IO.popen
開啟的,且也關閉讀取,則設定全域變數 $?
(子項退出狀態)。
IO.popen('ruby', 'r+') do |pipe| puts pipe.closed? pipe.close_read puts pipe.closed? pipe.close_write puts $? puts pipe.closed? end
輸出
false false pid 15044 exit 0 true
相關:IO#close
、IO#close_read
、IO#closed?
。
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; }
如果串流已關閉讀取和寫入,則傳回 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_read
、IO#close_write
、IO#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); }
傳回表示目前主控台模式的資料。
您必須需要「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); }
將主控台模式設定為 模式
。
您必須需要「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 模式中產生 self
。
STDIN.cooked(&:gets)
將讀取並傳回一行,並帶有回音和行編輯。
您必須需要「io/console」才能使用此方法。
static VALUE console_cooked(VALUE io) { return ttymode(io, rb_yield, io, set_cookedmode, NULL); }
啟用 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; }
傳回目前的游標位置,為整數的二元陣列(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 }
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)); }
static VALUE console_cursor_down(VALUE io, VALUE val) { return console_move(io, +NUM2INT(val), 0); }
static VALUE console_cursor_left(VALUE io, VALUE val) { return console_move(io, 0, -NUM2INT(val)); }
static VALUE console_cursor_right(VALUE io, VALUE val) { return console_move(io, 0, +NUM2INT(val)); }
static VALUE console_cursor_up(VALUE io, VALUE val) { return console_move(io, -NUM2INT(val), 0); }
呼叫給定的區塊,其中包含串流中的每個位元組 (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_char
、IO#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; }
呼叫給定的區塊,其中包含串流中的每個字元;傳回 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_byte
、IO#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; }
呼叫給定的區塊,其中包含串流中的每個碼點;傳回 self
f = File.new('t.rus') a = [] f.each_codepoint {|c| a << c } a # => [1090, 1077, 1089, 1090] f.close
如果沒有給定區塊,則傳回 Enumerator
。
static VALUE rb_io_each_codepoint(VALUE io) { rb_io_t *fptr; rb_encoding *enc; unsigned int c; int r, n; RETURN_ENUMERATOR(io, 0, 0); GetOpenFile(io, fptr); rb_io_check_char_readable(fptr); READ_CHECK(fptr); if (NEED_READCONV(fptr)) { SET_BINARY_MODE(fptr); r = 1; /* no invalid char yet */ for (;;) { make_readconv(fptr, 0); for (;;) { if (fptr->cbuf.len) { if (fptr->encs.enc) r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off, fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len, fptr->encs.enc); else r = ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1); if (!MBCLEN_NEEDMORE_P(r)) break; if (fptr->cbuf.len == fptr->cbuf.capa) { rb_raise(rb_eIOError, "too long character"); } } if (more_char(fptr) == MORE_CHAR_FINISHED) { clear_readconv(fptr); if (!MBCLEN_CHARFOUND_P(r)) { enc = fptr->encs.enc; goto invalid; } return io; } } if (MBCLEN_INVALID_P(r)) { enc = fptr->encs.enc; goto invalid; } n = MBCLEN_CHARFOUND_LEN(r); if (fptr->encs.enc) { c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off, fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len, fptr->encs.enc); } else { c = (unsigned char)fptr->cbuf.ptr[fptr->cbuf.off]; } fptr->cbuf.off += n; fptr->cbuf.len -= n; rb_yield(UINT2NUM(c)); rb_io_check_byte_readable(fptr); } } NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr); enc = io_input_encoding(fptr); while (io_fillbuf(fptr) >= 0) { r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off, fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc); if (MBCLEN_CHARFOUND_P(r) && (n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) { c = rb_enc_codepoint(fptr->rbuf.ptr+fptr->rbuf.off, fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc); fptr->rbuf.off += n; fptr->rbuf.len -= n; rb_yield(UINT2NUM(c)); } else if (MBCLEN_INVALID_P(r)) { goto invalid; } else if (MBCLEN_NEEDMORE_P(r)) { char cbuf[8], *p = cbuf; int more = MBCLEN_NEEDMORE_LEN(r); if (more > numberof(cbuf)) goto invalid; more += n = fptr->rbuf.len; if (more > numberof(cbuf)) goto invalid; while ((n = (int)read_buffered_data(p, more, fptr)) > 0 && (p += n, (more -= n) > 0)) { if (io_fillbuf(fptr) < 0) goto invalid; if ((n = fptr->rbuf.len) > more) n = more; } r = rb_enc_precise_mbclen(cbuf, p, enc); if (!MBCLEN_CHARFOUND_P(r)) goto invalid; c = rb_enc_codepoint(cbuf, p, enc); rb_yield(UINT2NUM(c)); } else { continue; } rb_io_check_byte_readable(fptr); } return io; invalid: rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc)); UNREACHABLE_RETURN(Qundef); }
呼叫區塊,並從串流中讀取每一行剩餘的資料;傳回 self
。如果已在串流的結尾,則不會執行任何動作;請參閱 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"
給定引數 sep
和 limit
,則結合這兩種行為
-
呼叫時,會根據換行符號
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
。
啟用/停用回顯。在某些平台上,此旗標和原始/已處理模式的所有組合可能無效。
您必須需要「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; }
如果已啟用回顯,則傳回 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; }
如果串流已定位在其結尾,則傳回 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); }
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; }
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; }
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
傳回表示串流編碼的 編碼
物件,或如果串流處於寫入模式且未指定編碼,則傳回 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)); }
呼叫 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(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); }
傳回串流的整數檔案描述子
$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); }
將 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(2)
立即將串流中緩衝的所有資料寫入磁碟。
請注意此差異
如果作業系統不支援 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); }
從串流中讀取並傳回下一個位元組(範圍 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); }
從串流中讀取並傳回下一個 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); }
以原始模式讀取並傳回一個字元。
有關參數的詳細資訊,請參閱 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 }
讀取並傳回一行,不回顯。除非為 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); }
從串流中讀取並傳回一行;將傳回值指定給 $_
。請參閱 行 IO。
如果沒有提供任何參數,則傳回由行分隔符號 $/
確定的下一行,或在沒有任何行的情況下傳回 nil
f = File.open('t.txt') f.gets # => "First line\n" $_ # => "First line\n" f.gets # => "\n" f.gets # => "Fourth line\n" f.gets # => "Fifth line\n" f.gets # => nil f.close
如果只提供字串參數 sep
,則傳回由行分隔符號 sep
確定的下一行,或在沒有任何行的情況下傳回 nil
;請參閱 行分隔符號
f = File.new('t.txt') f.gets('l') # => "First l" f.gets('li') # => "ine\nSecond li" f.gets('lin') # => "ne\n\nFourth lin" f.gets # => "e\n" f.close
會尊重 sep
的兩個特殊值
f = File.new('t.txt') # Get all. f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.rewind # Get paragraph (up to two line separators). f.gets('') # => "First line\nSecond line\n\n" f.close
如果只提供整數參數 limit
,則限制行中的位元組數目;請參閱 行限制
# No more than one line. File.open('t.txt') {|f| f.gets(10) } # => "First line" File.open('t.txt') {|f| f.gets(11) } # => "First line\n" File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
給定引數 sep
和 limit
,則結合這兩種行為
-
傳回由行分隔符號
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; }
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; }
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; }
清除核心中的輸入緩衝區。
您必須需要「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; }
傳回 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, ">"); }
呼叫 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); }
清除核心中的輸入和輸出緩衝區。
您必須需要「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; }
如果串流與終端裝置 (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); }
傳回串流的目前行號;請參閱 行號。
static VALUE rb_io_lineno(VALUE io) { rb_io_t *fptr; GetOpenFile(io, fptr); rb_io_check_char_readable(fptr); return INT2NUM(fptr->lineno); }
設定並傳回串流的行號;請參閱 行號。
static VALUE rb_io_set_lineno(VALUE io, VALUE lineno) { rb_io_t *fptr; GetOpenFile(io, fptr); rb_io_check_char_readable(fptr); fptr->lineno = NUM2INT(lineno); return lineno; }
在停用回音的情況下產生 self
。
STDIN.noecho(&:gets)
會讀取並傳回一行,且不會回音。
您必須需要「io/console」才能使用此方法。
static VALUE console_noecho(VALUE io) { return ttymode(io, rb_yield, io, set_noecho, NULL); }
在非封鎖模式下產生 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); }
設定為 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; }
如果 IO
物件處於非封鎖模式,則傳回 true
。
static VALUE rb_io_nonblock_p(VALUE io) { if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK) return Qtrue; return Qfalse; }
傳回可以在不封鎖的情況下讀取的位元組數。如果沒有可用資訊,則傳回零。
您必須需要「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); }
清除核心中的輸出緩衝區。
您必須需要「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; }
傳回與 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); }
使用 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); }
傳回與串流關聯的子處理程序的處理程序 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); }
搜尋到指定的 new_position
(以位元組為單位);請參閱 Position
f = File.open('t.txt') f.tell # => 0 f.pos = 20 # => 20 f.tell # => 20 f.close
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); }
行為類似於 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; }
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; }
將指定的物件寫入串流;傳回 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
VALUE rb_io_print(int argc, const VALUE *argv, VALUE out) { int i; VALUE line; /* if no argument given, print `$_' */ if (argc == 0) { argc = 1; line = rb_lastline_get(); argv = &line; } if (argc > 1 && !NIL_P(rb_output_fs)) { rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value"); } for (i=0; i<argc; i++) { if (!NIL_P(rb_output_fs) && i>0) { rb_io_write(out, rb_output_fs); } rb_io_write(out, argv[i]); } if (argc > 0 && !NIL_P(rb_output_rs)) { rb_io_write(out, rb_output_rs); } return Qnil; }
格式化並將 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; }
寫入一個字元到串流。請參閱 字元 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; }
將指定的 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; }
行為類似於 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); }
在原始模式中產生 self
,並傳回區塊的結果。
STDIN.raw(&:gets)
將讀取並傳回一行,而不會回顯和編輯行。
參數 min
指定執行讀取作業時應接收的最小位元組數目。(預設值:1)
參數 time
指定以 1/10 秒為精度的逾時時間(以秒為單位)。(預設值:0)
如果參數 intr
為 true
,則啟用中斷、終止、退出和暫停特殊字元。
請參閱 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); }
啟用原始模式,並傳回 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; }
從串流中讀取位元組;串流必須開啟以進行讀取(請參閱 存取模式)
-
如果
maxlen
為nil
,則使用串流的資料模式讀取所有位元組。 -
否則,在二進位模式中讀取最多
maxlen
位元組。
傳回一個包含已讀取位元組的字串(新的字串或指定的 out_string
)。字串的編碼取決於 maxLen
和 out_string
-
maxlen
為nil
:使用self
的內部編碼(不論是否指定out_string
)。 -
maxlen
不為nil
-
指定
out_string
:不修改out_string
的編碼。 -
未指定
out_string
:使用 ASCII-8BIT。
-
沒有參數 out_string
如果省略參數 out_string
,傳回的值是新的字串
f = File.new('t.txt') f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.rewind f.read(30) # => "First line\r\nSecond line\r\n\r\nFou" f.read(30) # => "rth line\r\nFifth line\r\n" f.read(30) # => nil f.close
如果 maxlen
為零,則傳回空字串。
有參數 out_string
如果指定參數 out_string
,傳回的值是 out_string
,其內容會被取代
f = File.new('t.txt') s = 'foo' # => "foo" f.read(nil, s) # => "First line\nSecond line\n\nFourth line\nFifth line\n" s # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.rewind s = 'bar' f.read(30, s) # => "First line\r\nSecond line\r\n\r\nFou" s # => "First line\r\nSecond line\r\n\r\nFou" s = 'baz' f.read(30, s) # => "rth line\r\nFifth line\r\n" s # => "rth line\r\nFifth line\r\n" s = 'bat' f.read(30, s) # => nil s # => "" f.close
請注意,此方法的行為類似於 C 中的 fread() 函式。這表示它會重試呼叫 read(2) 系統呼叫以讀取資料(具有指定的 maxlen,或直到 EOF)。
即使串流處於非封鎖模式,此行為也會保留。(此方法與其他方法一樣,不受非封鎖旗標影響。)
如果您需要類似於單一 read(2) 系統呼叫的行為,請考慮 readpartial
、read_nonblock
和 sysread
。
相關:IO#write
。
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; }
在為基礎檔案描述符設定 O_NONBLOCK 之後,使用 read(2) 系統呼叫從 ios 讀取最多 maxlen 位元組。
如果存在選擇性的 outbuf 參數,它必須參照一個 String
,它會接收資料。即使在開始時並非為空,outbuf 在方法呼叫後只會包含已接收的資料。
read_nonblock
只會呼叫 read(2) 系統呼叫。它會導致 read(2) 系統呼叫導致的所有錯誤:Errno::EWOULDBLOCK、Errno::EINTR 等。呼叫者應注意此類錯誤。
如果例外情況為 Errno::EWOULDBLOCK 或 Errno::EAGAIN,則會由 IO::WaitReadable
延伸。因此,IO::WaitReadable
可用於救援例外情況,以重新嘗試 read_nonblock。
read_nonblock
會在 EOF 時導致 EOFError
。
在某些平台(例如 Windows)上,不支援套接字以外的 IO
物件的非封鎖模式。在這種情況下,將會引發 Errno::EBADF。
如果讀取位元組緩衝區不為空,read_nonblock
會像 readpartial 一樣從緩衝區讀取。在這種情況下,不會呼叫 read(2) 系統呼叫。
當 read_nonblock
引發 IO::WaitReadable
類型的例外情況時,read_nonblock
不應在 io 可讀取之前被呼叫,以避免忙碌迴圈。這可如下執行。
# emulates blocking read (readpartial). begin result = io.read_nonblock(maxlen) rescue IO::WaitReadable IO.select([io]) retry end
雖然 IO#read_nonblock
沒有引發 IO::WaitWritable
。OpenSSL::Buffering#read_nonblock
可以引發 IO::WaitWritable
。如果 IO
和 SSL 應多型使用,也應救援 IO::WaitWritable
。請參閱 OpenSSL::Buffering#read_nonblock
的文件以取得範例程式碼。
請注意,此方法與 readpartial 相同,除了設定非封鎖旗標。
透過指定關鍵字參數 exception 為 false
,你可以指出 read_nonblock
不應引發 IO::WaitReadable
例外情況,而應改為傳回符號 :wait_readable
。在 EOF 時,它將傳回 nil,而不是引發 EOFError
。
# File io.rb, line 62 def read_nonblock(len, buf = nil, exception: true) Primitive.io_read_nonblock(len, buf, exception) end
從串流讀取並傳回下一個位元組(範圍 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; }
讀取並傳回串流中所有剩餘的行;不會修改 $_
。請參閱 行 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
給定引數 sep
和 limit
,則結合這兩種行為
-
傳回由行分隔符號
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); }
從串流中讀取最多 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。不同之處在於
-
它不會導致 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; }
如果輸入可用且未封鎖,則傳回 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 }
將串流重新關聯至另一個串流,該串流可能是不同類型的。此方法可用於將現有串流重新導向至新的目的地。
如果給定參數 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; }
將串流重新定位至其開頭,將位置和行號都設定為零;請參閱 位置 和 行號
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); }
static VALUE console_scroll_backward(VALUE io, VALUE val) { return console_scroll(io, -NUM2INT(val)); }
static VALUE console_scroll_forward(VALUE io, VALUE val) { return console_scroll(io, +NUM2INT(val)); }
尋找由整數 offset
(請參閱 位置) 和常數 whence
給出的位置,其中常數 whence
為下列其中之一
-
:CUR
或IO::SEEK_CUR
:將串流重新定位至其目前位置加上給定的offset
f = File.open('t.txt') f.tell # => 0 f.seek(20, :CUR) # => 0 f.tell # => 20 f.seek(-10, :CUR) # => 0 f.tell # => 10 f.close
-
:END
或IO::SEEK_END
:將串流重新定位至其結尾加上給定的offset
f = File.open('t.txt') f.tell # => 0 f.seek(0, :END) # => 0 # Repositions to stream end. f.tell # => 52 f.seek(-20, :END) # => 0 f.tell # => 32 f.seek(-40, :END) # => 0 f.tell # => 12 f.close
-
:SET
或IO:SEEK_SET
:將串流重新定位至給定的offset
f = File.open('t.txt') f.tell # => 0 f.seek(20, :SET) # => 0 f.tell # => 20 f.seek(40, :SET) # => 0 f.tell # => 40 f.close
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); }
請參閱 編碼。
如果給定,參數 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; }
如果串流以 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); }
傳回 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); }
傳回串流目前的同步模式。當同步模式為 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); }
將串流的 同步 模式 設定為指定值;傳回指定值。
同步模式的值
-
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; }
行為類似 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; }
行為類似 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); }
將指定的 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); }
傳回 self
中的目前位置(以位元組為單位)(請參閱 位置)
f = File.open('t.txt') f.tell # => 0 f.gets # => "First line\n" f.tell # => 12 f.close
static VALUE rb_io_tell(VALUE io) { rb_io_t *fptr; rb_off_t pos; GetOpenFile(io, fptr); pos = io_tell(fptr); if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv); pos -= fptr->rbuf.len; return OFFT2NUM(pos); }
取得內部逾時持續時間,如果未設定則為 nil。
VALUE rb_io_timeout(VALUE self) { rb_io_t *fptr = rb_io_get_fptr(self); return fptr->timeout; }
將內部逾時設定為指定的持續時間或 nil。逾時適用於所有可能的封鎖作業。
當作業執行時間超過設定的逾時,會引發 IO::TimeoutError
。
這會影響下列方法(但不限於):gets
、puts
、read
、write
、wait_readable
和 wait_writable
。這也會影響封鎖 socket 作業,例如 Socket#accept
和 Socket#connect
。
有些作業,例如 File#open
和 IO#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; }
傳回 self
。
static VALUE rb_io_to_io(VALUE io) { return io; }
將給定的資料推回串流的緩衝區(「取消移出」),放置資料以便於接下來讀取;傳回 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; }
將給定的資料推回串流的緩衝區(「取消移出」),放置資料以便於接下來讀取;傳回 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; }
等待 IO
準備好指定的事件,並傳回已準備好的事件子集,或在逾時時傳回虛假值。
事件可以是 IO::READABLE
、IO::WRITABLE
或 IO::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 }
等到 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); }
等到 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 }
等到 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 }
傳回主控台大小。
您必須需要「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))); }
嘗試設定主控台大小。效果會依平台和執行環境而定。
您必須需要「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; }
將每個指定的 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); } }
在為基礎檔案描述符設定 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