<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Ruby#open 你了解多少?

          共 3046字,需瀏覽 7分鐘

           ·

          2016-08-23 16:12

          如果現(xiàn)在要你使用Ruby,你會(huì)想到怎么做?

          直覺是使用File.open,但想想File.new似乎也可行,然后又發(fā)現(xiàn)不使用File類別,直接用open也能做到一樣的事。去查了Ruby文件結(jié)果發(fā)現(xiàn)IO.open和IO.new也能做到同樣的操作。

          如你所見,使用Ruby光是開個(gè)檔案描述符(以下簡(jiǎn)稱FD)就有數(shù)幾種方法,令人眼花撩亂,常看到的是有人用同一招打天下,卻一直沒有去了解其他的方法與其是用情境,有些可惜,而這篇文章將通過由下而上的方式,一一介紹、示范它們的差別和使用。

          IO.new

          IO類別是Ruby對(duì)FD進(jìn)行讀寫操作的一切基礎(chǔ),我們可以用File來(lái)操作是因?yàn)镕ile繼承自IO,只是稍嫌麻煩些。

          IO.new的第一個(gè)參數(shù)必須是FD,或在Windows下則稱句柄,無(wú)論何者都只是一個(gè)數(shù)字。

          如果你已知標(biāo)準(zhǔn)輸入與標(biāo)準(zhǔn)輸出的檔案描述符分別為0和1,不妨實(shí)驗(yàn)一下:

          stdin = IO.new(0)

          stdout = IO.new(1)

          stdout.puts“what's your name?”

          name = stdin.gets.chomp!

          stdout.puts“hello,#{name}!”

          what's your name? tony hello,tony!

          另外可用IO.sysopen來(lái)取得檔案的FD,這其實(shí)就是File類別的做法,F(xiàn)ile只是隱藏此細(xì)節(jié)罷了:

          fd = IO.sysopen('file.txt','w')#=> 3

          io = IO.new(fd)

          io.puts 'hello!'

          io.close

          另一個(gè)例子是通過/dev/tty寫到終端:

          fd = IO.sysopen('/dev/tty','w')

          io = IO.new(fd,'w')

          puts 'Hello'

          io.puts 'World'

          io.close

          Hello World

          在這里提醒要小心選擇正確的tty檔案,萬(wàn)一不慎選到其他使用者的,執(zhí)行上述代碼就會(huì)在他人的終端畫面上印出一堆垃圾。

          IO.open

          IO.open沒什么新奇之處,它只是IO.new加上block的擴(kuò)充版本,若無(wú)使用block時(shí),與IO.new無(wú)異,最后會(huì)回傳IO物件;但若與block使用,有兩個(gè)特點(diǎn):

          IO物件會(huì)在block結(jié)束時(shí)被自動(dòng)關(guān)閉(意即不需要寫IO#close)。

          IO.open最后回傳的不再是IO物件,而是block的最后執(zhí)行結(jié)果。

          IO.popen

          有曾好奇過市面上的CI是怎么做到即時(shí)顯示終端上的文字嗎?以Travis CI為例,下圖那塊黑色內(nèi)存塊中的內(nèi)容是即時(shí)輸出的:

          file

          或者曾想過在自己的網(wǎng)站上執(zhí)行外部的指令,并且即時(shí)呈現(xiàn)給使用者呢?若你有在Ruby中呼叫其他系統(tǒng)指令的經(jīng)驗(yàn)(例如ls、cat、bundle install等等),那應(yīng)該對(duì)system、%x{}或是``不陌生:

          system 'date' # => true,false or nil

          %x{date} # => the standard output of the running cmd

          date # => as above

          然而system只根據(jù)指令執(zhí)行結(jié)果成功與否回傳布爾值,無(wú)法直接存取子程序輸出的結(jié)果;%x{}會(huì)以字串形式回傳結(jié)果,但必須等到子程序執(zhí)行結(jié)束后才會(huì)回傳整個(gè)字串,無(wú)法即時(shí)監(jiān)控子程序的標(biāo)準(zhǔn)輸出。

          相較于%x{}回傳完整的字串,IO.popen則是回傳IO物件。為了比較出差異,這里就拿ping指令為例,因?yàn)樵撝噶顣?huì)不斷在終端畫面上輸出信息,直到使用者手動(dòng)停止,如果使用%x{}的話,Ruby程序?qū)?huì)卡在該處,且因準(zhǔn)備要回傳的字串越來(lái)越長(zhǎng),最后導(dǎo)致內(nèi)存不夠用或程序會(huì)卡到海枯石爛。

          相較下操作IO物件就可以一次讀一行:

          puts %x{ping www.alphacamp.co} # don't do this

          io = IO.popen('ping www.alphacamp.co')

          while line = io.gets

          print line

          end

          PING www.alphacamp.co(198.41.206.122):56 data bytes 64 bytes from 198.41.206.122: icmp_seq=0 ttl=58 time=2.794 ms 64 bytes from 198.41.206.122: icmp_seq=1 ttl=58 time=4.876 ms 64 bytes from 198.41.206.122: icmp_seq=2 ttl=58 time=7.081 ms …

          當(dāng)然這還離真正做出一個(gè)在網(wǎng)頁(yè)上呈現(xiàn)終端執(zhí)行畫面的功能還很遠(yuǎn),例如上述的代碼卡在一個(gè)無(wú)窮循環(huán)里面,你可能會(huì)想針對(duì)IO阻塞問題做出一些改善,像是配合IO.select或是IO#read_nonblock等,但純屬延伸議題,不在本章范圍,有機(jī)會(huì)筆者會(huì)在另一篇章中分享怎么做到:)

          File.new與File.open

          這兩個(gè)方方法就是大家耳熟能詳?shù)拈_檔方案了,它們和IO.new與IO.open幾乎一樣,只差在復(fù)寫了initialize方法,使其接受的參數(shù)不再是FD而是檔案的路徑字串。File.new回傳值也和IO.new一樣是IO物件;在File.open與block同時(shí)使用的情況下也和IO.open一樣,會(huì)自動(dòng)關(guān)檔,且回傳block的最后執(zhí)行結(jié)果。

          Kernel.open

          Kernel.open大概是最萬(wàn)用的方法了,留在最后講是因?yàn)樗荌O.popen與File.open的合體,除此也接受擁有#to_open方法的物件。

          當(dāng)傳入一個(gè)物件給Kernel.open時(shí),處理的優(yōu)先續(xù)如下:

          檢查該物件是否有#to_open方法,有則直接呼叫以取得IO物件。

          如果物件是字串且開頭是|,則去掉|,剩下丟給IO.popen處理。

          最后交給File.open處理

          to_open

          關(guān)于#to_open Ruby文件上沒有一處提及,只記載在Ruby原始碼中。實(shí)作的時(shí)候必要回傳IO物件即可:

          class Foo

          def to_open

          puts 'Foo#to_open is here'

          File.open('test.txt')#=> IO instance

          end

          end

          open Foo.new do |io|

          … io will be closed automatically

          end

          該用哪個(gè)?

          這沒有什么強(qiáng)制的規(guī)范,畢竟Ruby是一個(gè)自由的程序語(yǔ)言,比較接近Perl,和一板一眼的Python不太一樣(Only one way to do it)。不過建議大原則是盡量使用易讀易寫的API來(lái)完成工作,如果有細(xì)節(jié)需要處理再用其他的方法。例如一般開檔就使用File.open或是Kernel.open即可,需要存取FD則改用IO.open,若要手動(dòng)關(guān)檔再考慮File.new或IO.new。另外也不要特別使用Kernel.open調(diào)用IO.popen的奇怪語(yǔ)法(|),這會(huì)降低代碼的可讀性,不符合易讀易寫。像IO.popen('date')就比Kernel.open('|date')好懂多了。

          另一個(gè)原則是代碼的一致性,如果團(tuán)隊(duì)開檔案都使用File.open,那就盡量避免特立獨(dú)行使用Kernel.open,反之亦然。

          瀏覽 117
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品九九九九九九九九九屄 | 精品久久久久久久久久久2012 | 91在线操 | 青娱乐最新| 大香蕉国产伊人视频 |