比較文件清單(Everyday Scripting with Ruby第三章試譯)

    想嘗試翻譯《Everyday Scripting with Ruby》,經過幾個晚上的鏖戰,給對方發出了我的試譯稿。很可惜,估計是我英語水平有限、文字功底尚待提高的緣故,無緣本書的翻譯工作。
    這兒,我貼出我的試稿件,希望大家能夠給出意見和建議,也希望有過翻譯經驗的xdjm提出批評,先謝過。:)
    翻譯樣章下載
    試譯文如下:

第一個腳本:比較文件清單

在這一章裏,我們將創建一個雖簡單卻很有用的腳本程序。在這個過程中,你會掌握Ruby的一些基本術語,領會它的一些技巧。到第五章,從33頁開始,我們將對這個程序進一步完善:作三點改進,修正一個bug

 

3.1 運行腳本

在命令行狀態下,先進入(用cd命令)源代碼所在目錄的子目錄inventory。(如果你忘了如何操作,參見第13頁的小貼士)。這時我們會看到一個腳本文件inventory.rb,運行它:

prompt> ruby inventory.rb

我們會看到如下內容:

exercise-differences.rb

inventory.rb

old-inventory.txt

recycler

recycler/inst-39.tmp

snapshots

snapshots/differences-version-1.rb

snapshots/differences-version-2.rb

snapshots/differences-version-3.rb

snapshots/differences-version-4.rb

snapshots/differences-version-5.rb

snapshots/differences-version-6.rb

snapshots/differences-version-7.rb

snapshots/differences-version-8.rb

temp

temp/inst-39

 

這樣,我們就運行了一個腳本。它列出了一份清單,清單中包含了當前工作目錄中的所有文件(子目錄中的所有目錄和文件也包括在內)。在本章,我們還要創建一個腳本用來比較兩份清單。比較什麼呢?我想知道第二份清單和第一份相比,加入了哪些內容,刪除了哪些內容。如果你是測試人員,這個腳本至少在兩方面對你很有用:

l          假設你每週五要編譯出一個測試版本,而這周編譯的源代碼文件跟上週相比可能有些改變。現實情況中,人不是十全十美的,他總會犯錯誤。有時候,可能源文件的這份清單就是錯誤的,那麼編譯出來的測試版本當然就談不上對了。如果有一份清單能夠告訴你增加和刪除了哪些文件,那麼它就可以防患於未然,幫你決定哪些文件是需要編譯的。

l          你想列出整個文件系統的文件清單(比如說,C:/)。安裝一個程序,之後又卸載它,然後比較安裝前和刪除後的文件清單。你可能會發現在文件系統中還殘留着尚未完全刪除的垃圾文件。

 

3.2 Ruby的世界

當我們運行腳本inventory.rb的時候,就創建了一個小“Ruby的世界”。這個Ruby的世界本質上包含3種事物:名詞、動詞和名稱。名詞,我們通常稱之爲對象,它們是Ruby世界的“事物”。對象是靜止不動的,直到你告訴它需要做什麼。因此,我們需要動詞。在Ruby世界裏,所有的動詞都是祈使動詞:像“起立”、“坐下”、“打滾”等等。這些動詞我們稱之爲消息。因此,如果告訴一個對象做什麼,就說發送消息。

我們沒有權限直接訪問Ruby世界中的對象。但是想要訪問它,怎麼辦呢?我們就必須使用名稱,讓程序知道當使用這個名稱的時候,實際上就是這個對象。在我們的世界裏,我就是一個對象,我兒子涉及到我的時候,稱呼我爲“爸爸”,妻子稱呼我爲“丈夫”或暱稱,而天河機場的職員可能會說,“31號”或“嘿,說你呢!”,實際上他指的也是我。

 

3.3 對象發送和接收消息

上一節的內容相當的抽象。在這一節裏,我們通過實例來理解什麼是名稱、對象和消息。現在開始,在命令行中輸入如下命令創建一個清單文件:

prompt> ruby inventory.rb > new-inventory.txt

 

這兒,> new-inventory.txt 告訴命令行解釋器把腳本的運行結果保存到一個命名爲new-inventory.txt的文件中。> new-inventory.txt並不是Ruby語言的一部分—它對任何命令都管用。(我之所以使用文件new-inventory.txt,是想表明這份清單是在安裝一個程序,然後把它卸載之後得到的。你可能已經注意到了,在目錄inventory中已經存在文件old-inventory.txt,現在我們假設它是在安裝之前的清單。)

打開irb, 然後運行如下命令。注意File的第一個字母F是大寫的。Ruby語言是字母大小寫敏感的,這就意味着Filefile是不同的。如果寫成file,就會得到一條出錯信息。

irb(main):001:0> File.open('new-inventory.txt')

=> #<File:new-inventory.txt>

 

現在,我每一步都講得比較詳細,因此我們的進展非常慢。彆着急,慢慢來,很快我們將進入下一個境界。(在Introduction->How the book works中,作者提到前兩個例子他會講得比較慢,後面的例子會講得比較快。)

 

FileRuby世界中一個非常特殊的對象。 Ruby世界裏有個對象知道如何打開文件,併爲使用文件做好各項準備,它就是FileOpen消息告訴File對象打開文件。既然File需要知道應該打開哪個文件,那麼open就有個參數,即字符串“new-inventory.txt”。

在收到消息後,File打開這個文件。至此,我們又引入另外一個對象,就是打開的這個文件(我指的對象僅限於Ruby中涉及到的)。File接着把新創建的對象返回給發創建消息的對象(通常我們稱之爲發送方)。在這個例子中,發送方是irb。(既然irb是一個Ruby腳本,那麼它就在Ruby世界裏作爲一個對象而存在。)當irb得到返回值後,以一種程序員看得懂的方式打印到屏幕上。這兒,#<File:new-inventory.txt>告訴我們,File通過創建一個對象來給它入口以訪問文件系統中的new-inventory.txt文件。其它對象的輸出方式可能有所不同。事實上,每個對象都能決定如何輸出結果。

不過,除了打印文件的信息(這兒的文件信息是指文件本身的信息,而非文件的內容)之外,還有更多的事情要做,你可以讓它打印出整個文件的內容,具體操作如下:

irb(main):002:0> File.open('new-inventory.txt').readlines

=> ["exercise-differences.rb/n", "inventory.rb/n", "new-inventory.txt/n", _

"old-inventory.txt/n", "recycler/n", "recycler/inst-39.tmp/n", "snapshots _

/n", "snapshots/differences-version-1.rb/n", "snapshots/differences-versi _

on-2.rb/n", "snapshots/differences-version-3.rb/n", "snapshots/difference _

s-version-4.rb/n", "snapshots/differences-version-5.rb/n", "snapshots/dif _

ferences-version-6.rb/n", "snapshots/differences-version-7.rb/n", "snapsh _

ots/differences-version-8.rb/n", "temp/n", "temp/inst-39/n"]

從前,我們告訴irb發送open信息給FileFile返回了一個打開的文件對象。但是irb並沒有打印出結果,我們還得告訴它發送另外一個信息,readlinesreadlines把文件的每一行轉換成一個字符串。打印出的“字符串”就是Ruby給字符序列命名的名稱。Readlines然後以數組形式返回這些字符串。在這個例子中,它們在文件中出現的順序是一致的。irb在打印這些字符串時,每個字符串都加上了雙引號,不同的字符串以逗號隔開,整個數組最後用方括號括起來。在本書後面的章節中,你將看到更多字符串和數組的例子。

如果仔細地檢查一下new-inventory.txt文件,你就會發現它包含的內容的確和打印出來的完全一致,甚至順序也是一致的。唯一的不同是,每個字符串都加了引號,而且每個結尾用的都是/n。這個符號相當於行結束符,表明/n後面的內容會另起一行。

可能你還注意到了,在輸入字符串“new-inventory.txt”的時候,我用的是單引號,但是 irb 打印字符串的時候使用的是雙引號。實際上,這是習慣問題,如果你喜歡,你可以在打字符串的時候,用雙引號,打文件名時用單引號。在本書中也遵循這個原則。

 

 

3.4 變量名對象

在得到文件內容的數組之後,我們可以給數組一個名稱 new_inventory。(請注意:這個名稱中我們用到的是下劃線_,而不是連字符-。)如下所示:

irb(main):003:0> new_inventory = File.open('new-inventory.txt').readlines

=> ["exercise-differences.rb/n", "inventory.rb/n", "new-inventory.txt/n", ←_

"old-inventory.txt/n", "recycler/n", "recycler/inst-39.tmp/n", "snapshots ←_

/n", "snapshots/differences-version-1.rb/n", "snapshots/differences-versi ←_

on-2.rb/n", "snapshots/differences-version-3.rb/n", "snapshots/difference ←_

s-version-4.rb/n", "snapshots/differences-version-5.rb/n", "snapshots/dif ←_

ferences-version-6.rb/n", "snapshots/differences-version-7.rb/n", "snapsh ←_

ots/differences-version-8.rb/n", "temp/n", "temp/inst-39/n"]

在術語上,Rubynew_inventory爲變量。(在當計算機在做數學運算時這就變得更有意義。)

項目inventory中包含了一個名爲old-inventory.txt的文件。這個文件的內容也是一份清單,只不過這份清單的內容比new-inventory.txt要早。我們可以這樣讀取它:

irb(main):004:0> old_inventory = File.open('old-inventory.txt').readlines

=> ["exercise-differences.rb/n", "inventory.rb/n", "old-inventory.txt/n", ←_

"financial-records.xls/n", "snapshots/n", "snapshots/differences-version- ←_

1.rb/n", "snapshots/differences-version-2.rb/n", "snapshots/differences-v ←_

ersion-3.rb/n", "snapshots/differences-version-4.rb/n", "snapshots/differ ←_

ences-version-5.rb/n", "snapshots/differences-version-6.rb/n", "snapshots ←_

/differences-version-7.rb/n", "snapshots/differences-version-8.rb/n", "te ←_

mp/n", "temp/junk/n"]

現在,我們已經得到了兩個數組,可以比較它們了。

 

3.5 數組比較

找出兩個數組不同的方法很簡單,就是給它們做“減法”運算。用其中的一個數組“減去”另外一個,就像這樣:

irb(main):005:0> new_inventory - old_inventory

=> ["new-inventory.txt/n", "recycler/n", "recycler/inst-39.tmp/n", "temp/ _

inst-39/n"]

以上顯示的是在新的inventory中存在,卻在老的inventory中不存在的字符串。如果想找出哪些文件從老的清單中刪除了,我們可以把被減數和減數調換位置。

irb(main):006:0> old_inventory - new_inventory

=> ["financial-records.xls/n", "temp/junk/n"]

不妨再回頭仔細審查一下文件的內容,你會發現輸出的結果是對的。

 

請注意,我們在做減法的時候,並沒有改變這兩個數組的內容。把old_inventory減去new_inventory實際上不會影響原來old_inventory的內容。它不僅沒有從old_inventory中刪掉任何內容,反而還產生了一個新的數組,這個數組的內容的就是做減法的結果。

 

命名遵循的原則

一個Ruby名稱可以包含字符、數字,還有下劃線(下劃線是“_”,不是連字符“-”)。名稱不能以數字開頭,也不能包含空格。在大小寫方面,my_shipmy_Ship是不同的。如果一個名稱以大寫字母開頭,你就在告訴Ruby,希望它是個常量,至始至終代表一個對象。如果你把這個名稱用在另外一個對象上,Ruby就會抱怨你,有沒搞錯啊!

irb(main):007:0> MyShip = "a cutter"

=> "a cutter"

irb(main):008:0> MyShip = "a bark"

(irb):4: warning: already initialized constant MyShip

=> "a bark"

(當然,抱怨歸抱怨,你要求的操作它還得照樣執行。)

 

當一個名稱由多個單詞組成,而且單詞的首字母是小寫的時候,爲了方便,最好用下劃線把它們隔開,像my_fine_name。如果首字母是大寫呢,那就把每個單詞的首字母都大寫,比如MyFineName。不過說實話,我不大明白在這兩種不同的寫法背後有何理論依據。

還有一種特殊的情況,消息名可以以問號(?)或感嘆號(!)結尾。如果以問號結尾。則表明這個信息向它的接收方問一個是非(truefalse)疑問句。如果是感嘆號結尾,則是表明這個信息做了些特殊的操作,也有可能進行的操作不是期望中的。

 

3.6 打印到屏幕

我們期望區分出兩份清單的所有的不同之處,現在萬事俱備,只欠一個可以把結果打印出來的腳本了。有個工具可以做到,就是稱作puts(“put string”的縮寫)的消息。如何生成一份報表來展示結果呢,這兒有部分代碼:

1_ irb(main):009:0> puts "The following files have been added:"

2_ The following files have been added:

3_ => nil

 

我們按順序來分析這三行:

1.儘管它看起來不像,實際上它是另外一個消息。不像什麼?對誰來說是另外一個消息?說得更清楚點,不像前面提到的,這個消息沒有用點(.)把接收消息的對象和消息名隔開。其實在Ruby中,聯繫上下文,如果消息的接收方已經很明確了,你根本就沒必要再加上消息的接收方。當從命令行運行一個Ruby腳本時,接收方當然就是它本身了。

還有一個原因,puts不像前面的消息那樣,把參數先加上單引號,然後用圓括號括起來。爲什麼呢?如果Ruby知道要把結果輸出到哪兒,是沒必要加上圓括號的。

2.打印字符串到屏幕。這兒的打印和irb打印結果到屏幕二者完全不相干。

儘管puts也是打印,不過它不像irb那樣自動爲打印結果加上引號。irb的打印結果是給你——編輯——看的, 而puts打印的結果是給最終讀者看的。如果你還是更偏好irb的那種輸出格式,好吧,使用inpect 消息可以達到你的要求:

irb(main):010:0> puts "I'd like some quotes, please".inspect

"I'd like some quotes, please"

=> nil

3.這一行是puts的返回值。每個Ruby消息都會有返回值。如果某個消息的確沒什麼好返回的,一般就返回“nil”了。(“nil”就意味着“空”。)

irb用來打印它發出的上一個消息的返回值。如果從命令行運行一個腳本,就不會這樣。你可以看到,腳本行消失了,取而代之的是puts打印的結果。

puts還可以打印數組:

irb(main):011:0> puts old_inventory

exercise-differences.rb

inventory.rb

old-inventory.txt

financial-records.xls

snapshots

snapshots/differences-version-1.rb

snapshots/differences-version-2.rb

snapshots/differences-version-3.rb

snapshots/differences-version-4.rb

snapshots/differences-version-5.rb

snapshots/differences-version-6.rb

snapshots/differences-version-7.rb

snapshots/differences-version-8.rb

temp

temp/junk

=> nil

 

請注意,數組的元素是如何逐行打印出來的。(這和字符串是否以/n結束無關,puts會自動地進行換行操作。)

 

 

3.7 創建腳本

到目前爲止,一切都準備就緒,我們可以開始創建一個腳本比較這兩份清單了。給這份腳本命名爲differences.rb,把這段後面的代碼行添加到腳本文件中。(如果你想偷懶,不從鍵盤中輸入代碼,你可以從下面的文件代碼中直接拷貝過去。不過我敢打賭,如果你手動輸入它們,你對它的理解肯定會更加深刻。)在第11頁,第二章的第5節 你的編輯器中,列出了你可能會用到的編輯器。不管你選擇哪個編輯器,最後別忘了把文件保存在inventory目錄中。

 

inventory/snapshots/differences-version-1.rb

old_inventory = File.open('old-inventory.txt').readlines

new_inventory = File.open('new-inventory.txt').readlines

puts "The following files have been added:"

puts new_inventory - old_inventory

puts ""

puts "The following files have been deleted:"

puts old_inventory - new_inventory

 

確保你已經退出irb命令狀態,然後通過以下命令運行腳本

prompt> ruby differences.rb

The following files have been added:

new-inventory.txt

recycler

recycler/inst-39.tmp

temp/inst-39

The following files have been deleted:

financial-records.xls

temp/junk

 

某些編輯器整合了運行功能,這樣讓你是用起來更方便。當你在使用SciTE的時候,按下F5你就可以運行它,並把結果顯示在另一半分開的窗口裏。如果你使用的是TextMate,當你按下#R,它也會運行這個腳本,不同的是,把結果顯示在一個新開的窗口裏。

不過,這個腳本還是有些不足。比如說,你必須把兩份清單的內容都輸入到文件名爲new-inventory.txtold-inventory.txt的文件中去,而這文件名是死的,不能改變。儘管如此,這是個好的開始,現在,我們回過頭來看看我們學到了些什麼。

 

3.8 小結

我們已經有了一個有用的腳本。如果你還沒有比較過清單,或者你以前都是手動去做的,現在你學到了一招。

現在,你已經學到了一點Ruby底層的理論:完成任何事情都是通過給對象發消息實現的。更重要的,你還學會了繁多的數據類型中的一種:數組。基本理論和豐富的內嵌工具讓使Ruby這個腳本語言變得非常的強大。

 

3.9 練習

在練習中,我們還得繼續使用代碼目錄中的inventory子目錄。

1.    length也是一個消息,這個消息的功能是得到數組的長度,如下:

irb(main):001:0> [1,2,3].length

=> 3

假設在腳本中加入如下一行:

x = (new_inventory - old_inventory).length

猜猜看,x的值是多少?它代表什麼意思?變量名用什麼代替x會更加直觀易懂?

2.在上一題中,如果我們把代碼改成

x = new_inventory - old_inventory.length

會發生什麼情況呢?爲什麼會這樣呢?

3.修改腳本,給他加入以下功能:new-inventory.txtold-inventory.txt相比,增加了幾個文件,刪除了幾個文件,還有幾個文件保持不變(這個有點技巧性)。

    4.我們注意到new-inventory.txtold-inventory.txt中的內容都是字母排序的,以它們命名的數組也是如此。它們之間有什麼聯繫沒?如果清單的內容是亂序的,結果又如何呢?

 

 

1:這個項目的構思來自Chris McMahon.

2:在Windows環境下,這個符號是回車換行符;在Linux/Unix下,它僅僅是個換行符;在Mac下,它又僅僅是回車符。它們之間的區別就像大陸交通法規要求靠右行駛,而香港靠左行駛一樣。這個規定有點專橫,如果所有的環境中規定都是一樣的,那麼就不會發生交通意外了,可惜現在改已經太遲了。Ruby儘量讓你不去關心操作系統:/n意味着“在這臺機器上無論如何都是對的”。

3:如果在Ruby的世界裏,只有對象、消息和名稱,那麼這兒消息又發送到哪兒去呢?不妨這樣理解這行代碼:給名稱爲old_inventory的對象發送名稱爲-的消息,而名稱爲new-inventory的就是參數。想象一下,如果Ruby沒有減法(數字或數組)運算,那麼它肯定沒有現在這麼流行。

4:如果你還沒有親自創建過一個腳本,但是你想運行snapshots目錄中的某個腳本,可以把它拷貝到inventory目錄中的differences.rb中。在windows下,這樣操作:

C:>copy snapshots/differences-version-1.rb differences.rb

Linux下,這樣操作:

prompt>cp snapshots/differences-version-1.rb differences.rb

 


(完)

發佈了62 篇原創文章 · 獲贊 59 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章