## 19.文件
到目前為止,你已經將數據存儲在程序的變量中。 當程序停止執行或計算機關閉或從內存中刪除程序時,變量數據將丟失。 如果要永久存儲,則必須將其存儲在文件中。 當你將數據存儲在文件中時,即使程序已從內存中刪除,它也會保留在該文件中,并且你可以在再次運行數據時將其取回。 如果你正在計算機,kindle 或電子閱讀器上閱讀這本書,則該文件將永久存儲在你的計算機或互聯網上的其他計算機上。 在本章中,我們將看到如何使用 ruby 程序創建,操作和刪除文件。
### 19.1。 將輸出存儲到文件中
讓我們創建一個熟悉的 Ruby 程序。 在文本編輯器中鍵入以下程序
```rb
#!/usr/bin/ruby
# write_file.rb
puts "Hello World!"
puts "Ruby can write into files"
```
在執行它時,給出如下所示的命令
```rb
$ ruby write_file.rb > something.txt
```
現在轉到程序所在的工作目錄,你將看到一個名為 [something.txt](code/something.txt) 的文件。 打開它,這就是你將看到的內容
```rb
Hello World!
Ruby can write into files
```
好吧,這一次有點像作弊。 我們尚未在程序中寫入文件,而是指示 ruby 解釋器獲取 [write_file.rb](code/write_file.rb) 生成的輸出,并將其放入名為 [something.txt](code/something.txt) 的文件中 。 為此,我們使用`>`(大于符號)。
### 19.2。 以文件為輸入
在最后一個示例中,我們將輸出寫入文件。 現在讓我們將文件作為輸入并進行處理。 在文本編輯器中編寫以下代碼,并將其保存為 [line_count.rb](code/line_count.rb) 。
```rb
#!/usr/bin/ruby
# line_count.rb
puts "The file has #{readlines.length} line(s)"
```
要執行它,我們將給出如下所示的命令
```rb
$ ruby line_count.rb < something.txt
```
如果你猜對了,我們給 [something.txt](code/something.txt) 作為程序的輸入。 我們使用`<`(小于)符號向程序指示我們正在提供文件作為輸入。
該程序在執行時提供以下結果
```rb
The file has 2 line(s)
```
讓我們分析程序,以便我們知道會發生什么。 請參閱上面程序中的代碼`#{readlines.length}`。 `readlines`命令接收文件并讀取所有行并將其存儲在數組中,數組的每個元素都有一行。 我們要做的就是獲取數組的長度,該長度可以通過使用`length`函數獲得。 因此,`readlines.length`給出了長度作為輸出,我們將其嵌入到字符串中,因此我們通過編寫以下語句來完成長度
```rb
puts "The file has #{readlines.length} line(s)"
```
### 19.3。 文件復制–一種
好吧,這是文件復制程序,如果該程序不是一個真正的文件復制程序,它可能會使一些專家爭論不休。 在文本編輯器中鍵入以下程序
```rb
#!/usr/bin/ruby
# file_copy.rb
puts readlines.join
```
像這樣運行
```rb
$ ruby file_copy.rb < something.txt > everything.txt
```
輸出量
```rb
everthing.txt has got everything that something.txt has got. Just open it and see for yourself.
```
訣竅在命令行中。 在這里,我們將 [something.txt](code/something.txt) 傳遞給程序 [file_copy.rb](code/file_copy.rb) ,現在程序將 [something.txt](code/something.txt) 接收到,并在遇到`readlines`時讀取行 ]命令。 從 [something.txt](code/something.txt) 中讀取的行以數組的形式存儲。 現在我們要做的就是使用`join`命令連接存儲在數組中的行,因此我們通過將`.join`添加到`readlines`來實現,因此我們得到
```rb
readlines.join
```
現在我們將打印出結果,我們使用 puts 命令執行此操作,因此我們的程序采用以下形式
```rb
puts readlines.join
```
在運行程序時,我們通過向 Ruby 解釋器提供以下命令,告訴它從 [something.txt](code/something.txt) 輸入并將生成的結果寫入 [everything.txt](code/everything.txt) 。
```rb
$ ruby file_copy.rb < something.txt > everything.txt
```
因此,我們無需真正編寫文件復制程序即可獲得復制的效果。
### 19.4。 顯示文件
現在讓我們編寫一個顯示文件內容的程序。 為此,我們讀取文件中的所有行,并將其存儲在數組中。 接下來,我們獲取數組的每個元素并將其打印出來。 在下面輸入程序,然后
```rb
#!/usr/bin/ruby
# display_file.rb
readlines.each do |line|
puts line
end
```
通過鍵入以下命令執行它
```rb
$ ruby display_file.rb < something.txt
```
這就是你將得到的輸出
```rb
Hello World!
Ruby can write into files
```
那么我們在該程序中做了什么? 看一下這個代碼塊
```rb
readlines.each do |line|
puts line
end
```
當 ruby 遇到`readlines`時,它從傳遞給程序的文件中讀取,提取行并將其存儲在數組中。 使用`.each`運算符,我們一次提取一行,并將其存儲到`do` `end`塊內的一個名為`line`的變量中。 我們使用`puts`在代碼塊中打印此行。 `end`語句結束了代碼塊,表示一切都結束了。
讓我們看看另一個程序。 在此程序中,我們使用更清潔的方法。 在文本編輯器中鍵入程序并執行
```rb
#!/usr/bin/ruby
# display_file_1.rb
puts File.open("something.txt").readlines
```
Output
```rb
Hello World!
Ruby can write into files
```
查看程序中的單行。 我們有一個`puts`語句,該語句幾乎可以打印出所有內容。 這是已引入的新事物。 看`File.open("something.txt")`,`File.open`打開一個文件,但是什么文件? 我們必須提供文件名。 作為文件名,我們用雙引號 <sup class="footnote">[ [46](#_footnotedef_46 "View footnote.") ]</sup> 傳遞`something.txt`。 `File.open`打開它,連接到它的`.readlines`讀取行并將其存儲在數組中。 我們將數組扔給`puts`,將其打印出來。 而已!
### 19.5。 逐行讀取文件
直到最后一節,我們已經了解了如何一次性讀取文件并將其數據泵出到控制臺。 在此示例中,我們將看到如何逐行讀取文件。 輸入下面給出的示例代碼并執行
```rb
#!/usr/bin/ruby
# read_file_1.rb
File.open("something.txt").each { |line| puts line }
```
Output
```rb
Hello World!
Ruby can write into files
```
輸出看起來如上圖所示。 查看代碼`File.new("something.txt").each { |line| puts line }`。 在代碼中,我們使用`File.open`命令打開名為 [something.txt](code/something.txt) 的文件,該命令打開文件并將行存儲為數組元素。 現在我們需要做的就是提取數組中的每個元素,并將其打印在控制臺上,這由`.each { |line| puts line }`完成。
除了使用`File.open`,還可以使用`File.new`打開文件。 它將具有相同的結果。 編寫了一個使用`File.new`的程序,如下所示,執行該程序,你將獲得相同的結果。
```rb
#!/usr/bin/ruby
# read_file_2.rb
File.new("something.txt").each { |line| puts line }
```
Output
```rb
Hello World!
Ruby can write into files
```
### 19.6。 開放與新–區別
從前面的示例可以看出,`File.open`和`File.new`之間并沒有太大的區別,實際上是有區別的。 考慮下面的程序,鍵入并執行
```rb
#!/usr/bin/ruby
# file_open.rb
File.open("something.txt") do |f|
puts f.gets
end
```
Output
```rb
Hello World!
```
上面的程序打印出 [something.txt](code/something.txt) 中存在的內容,同樣的事情由 [file_new.rb](code/file_new.rb) 完成,如下所示
```rb
#!/usr/bin/ruby
# file_new.rb
f = File.new("something.txt", "r")
puts f.gets
f.close
```
Output
```rb
Hello World!
```
好,那有什么區別? `File.new`返回可以存儲在變量中的新文件對象或句柄。 在上面的程序中,我們將文件對象存儲到變量`f`中。 我們可以在程序中的任何位置使用此變量來訪問和操作文件。 使用變量`f`完成文件的所有必需操作后,我們最終使用`f.close`關閉文件。
讓我們編寫一個名為 [file_open_error.rb](code/file_open_error.rb) 的程序,如下所示
```rb
#!/usr/bin/ruby
# file_new.rb
f = File.new("something.txt", "r")
puts f.gets
f.close
```
Output
```rb
Hello World!
Reading file after File.open block is closed:
file_open_error.rb:8: undefined local variable or method `f' for main:Object (NameError)
```
參見突出顯示的代碼,我們在關閉代碼塊后嘗試讀取文件內容,并引發錯誤,這是因為`File.open`加載到`do` `end`代碼塊內的變量`f`的文件句柄中, 關閉該塊后,你將無法訪問該文件。
盡管差異很小,但仍存在差異。
### 19.7。 定義我們自己的行尾
直到一行一行地讀取為止,這意味著 Ruby 程序在遇到給定文件名時會進行搜索,加載并在遇到行尾字符`'\n'` <sup class="footnote">[ [47](#_footnotedef_47 "View footnote.") ]</sup> 在 Linux 系統上(在 Windows 上為`\r\n`),它識別行已結束,因此將其前面的字符打包到數組元素中。 如果我們想定義自己的行結束符怎么辦? 在英語中,句號被視為行尾字符。 我們為什么不能對 Ruby 解釋器說行號以句號結尾? 為此,我們創建一個名為 [line_endings.txt](code/line_endings.txt) 的簡單文本文件,并將以下文本放入其中
```rb
This is first line. This is second line. This
is the third. And fourth comes after third.
```
讓我們在文本編輯器中編寫一個如下所示的 Ruby 程序,將其另存為 [line_endings.rb](code/line_endings.rb)
```rb
#!/usr/bin/ruby
# line_endings.rb
File.open("line_endings.txt").each('.') do |line|
puts line
end
```
執行后,程序將輸出以下輸出
```rb
This is first line.
This is second line.
This
is the third.
And fourth comes after third.
```
仔細查看 [line_endings.txt](code/line_endings.txt) 。 這是第一行:_ 這是第一行。_ ,這是第二行:_ 這是第二行。_
兩者都在 [line_endings.txt](code/line_endings.txt) 中的同一行上,但是在執行程序時,它被打印為兩條不同的行。 這是因為語句`File.open("line_endings.txt")`將文件的全部內容加載到內存中,`.each('.')`將內容分割到每個點或句點字符('。')處,并將分割后的文本的每個塊放入數組中 元件。 因此,這里真正的英雄是`each`功能。 以類似的方式,你可以使用任何可以定義行結尾的字符。
如果你使用 Ruby 編寫 C 編譯器,則可以使用分號(;)作為行尾。
### 19.8。 逐字節讀取
有時你想按字節讀取文件字節 <sup class="footnote">[ [48](#_footnotedef_48 "View footnote.") ]</sup> ,而不是閱讀其中的普通英語。 到底為什么我們逐字節讀取文件? 好吧,并非所有文件都包含文本。 音樂文件,視頻等文件具有僅某些程序可以理解的原始數據。 如果要編寫音樂或視頻播放器或圖像查看器,則需要讀取原始數據并對其進行處理。 因此,為了讀取和顯示數據字節,我們使用`each_byte`函數。 看下面的代碼。 輸入并執行
```rb
#!/usr/bin/ruby
# byte_by_byte.rb
File.open("something.txt").each_byte { |byte| puts byte }
```
執行后,這就是輸出的樣子
```rb
72
101
108
108
111
32
87
111
.
.
some stuff is removed to save pages printed
.
.
105
108
101
115
10
```
在上面的程序中,我們使用`File.open`打開名為 [something.txt](code/something.txt) 的文件,所有內容都被加載,現在我們使用`each_byte`函數逐字節訪問內容,我們捕獲變量中的字節 稱為`byte`并打印出來。 請注意,在此程序中,我們使用了大括號`{`和`}`,它們可以代替`do`和`end`使用。 我更喜歡`do`和`end`,因為它們看起來更友好。
### 19.9。 一次讀取單個字符
下面的程序逐個字符讀取并打印。 我們使用一個稱為`each_char`的函數。 此`each_char`逐字符而不是逐行分割輸入文件。 該程序及其輸出如下。
```rb
#!/usr/bin/ruby
# char_by_char.rb
# To get this program to work, you must
# have ruby 1.9
File.open("something.txt").each_char { |a| puts a }
```
Output
```rb
H
e
l
l
o
W
o
r
l
d
!
R
u
b
y
c
a
n
w
r
i
t
e
i
n
t
o
f
i
l
e
s
```
### 19.10。 重命名文件
在 Ruby 中重命名文件非常容易,你要做的就是調用 File 類中的`rename`函數。 第一個參數將是需要重命名的文件的名稱,第二個參數將是新名稱。 它是如此簡單,你可以在 irb 上嘗試一下。 看一下下面給出的程序 [named.rb](code/rename.rb) 的源代碼。 在其中,我們將名為 noname.txt 的文件重命名為 somename.txt。 在運行程序之前,在工作目錄上放置一個名為 noname.txt 的文件。
```rb
#!/usr/bin/ruby
# rename.rb
File.rename("noname.txt", "somename.txt")
```
Output
```rb
The file noname.txt was renamed to somename.txt
```
### 19.11。 找出文件中的位置
你可能有時需要找出你在文件中的位置。 為此,可以使用方法`pos`。 讓我們看一個示例,該示例向我們解釋如何在文件中查找位置。 輸入并執行 fie_position.rb
```rb
#!/usr/bin/ruby
# file_position.rb
f = File.open "god.txt"
puts "At the beginning f.pos = #{f.pos}"
f.gets
puts "After reading first line f.pos = #{f.pos}"
f.gets
puts "After reading second line f.pos = #{f.pos}"
```
Output
```rb
At the beginning f.pos = 0
After reading first line f.pos = 43
After reading second line f.pos = 69
```
現在讓我們遍歷代碼,看看它是如何工作的。 首先,我們在`f = File.open "god.txt"`行中打開一個名為 [god.txt](code/god.txt) 的文件,然后使用`puts "At the beginning f.pos = #{f.pos}"`語句檢查位置,注意`f.pos`,`pos`方法用于獲取 讀取或寫入文件時我們所處的位置。 最初,當我們打開文件時,位置將為零,因此我們將獲得以下輸出
```rb
At the beginning f.pos = 0
```
在下一行中,我們使用`f.gets`讀取文件的第一行,因為我們已經讀取了文件,就像讀取指針的位置應該已更改 4,所以當我們打印`f.pos`時,它必須顯示除零以外的其他數字。 因此,語句`puts "After reading first line f.pos = #{f.pos}"`產生以下結果
```rb
After reading first line f.pos = 43
```
只是為了教育更多,我們使用另一個`f.gets`讀取第二行,現在我們打印新文件的位置,現在我們發現指針指向位置 69。
如果你想知道 [god.txt](code/god.txt) 有什么,這里是:
```rb
All things exists because it was created.
Then the creator exists.
Did man ever think how the creator exist?
If such a mighty creator can exist without creation,
then why can't this simple universe exist without
a creator
```
在接下來的示例中,我們將看到如何更改文件中的位置。 鍵入下面的示例( [file_changing_position.rb](code/file_changing_position.rb) )并執行它
```rb
#!/usr/bin/ruby
# file_changing_position.rb
f = File.open "god.txt"
puts "Reading file with f.pos = 0"
puts f.gets
puts "_"*40
f.pos = 12
puts "Reading file with f.pos = #{f.pos}"
puts f.gets
puts "Now f.pos = #{f.pos}"
```
Output
```rb
Reading file with f.pos = 0
All things exists because it was created.
________________________________________
Reading file with f.pos = 12
xists because it was created.
Now f.pos = 43
```
仔細閱讀程序并注意輸出。 首先,我們打開文件 [god.txt](code/god.txt) ,變量`f`有其句柄。
下一行
```rb
puts f.gets
```
我們正在讀取`f.pos`為零的文件,也就是說,我們正在從文件的開頭進行讀取。 如你所見,第一個`puts f.gets`的輸出顯示了整行`All things exists because it was created.`的輸出。 請注意下一行,現在我們使用`f.pos = 12`語句將文件中的位置更改為位置 12,這意味著我們的指針從頭開始是 12 個字節。 現在,在第二個 puts `f.gets`中,因為創建了輸出,所以它得到了存在的輸出。 這表明我們能夠成功更改文件在文件中的位置。
有些人可能會認為文件位置可能為負,例如,如果你想讀取文件的最后 20 個字節,則可以分配`f.pos = -20`,而在給定 f.gets 時將其打印出來。 恩,Ruby 無法做到這一點。 如果你想嘗試示例(file_negative_position.rb),并查看天氣,它將給出正確的結果。
```rb
#!/usr/bin/ruby
# file_negative_position.rb
# this example wont work
f = File.open "god.txt"
f.pos = -20
puts "Reading file with f.pos = #{f.pos}"
puts f.gets
```
### 19.12。 寫入文件
到現在為止,我們已經了解了如何從文件中讀取內容,現在我們還將看到如何將內容寫入文件中。 要學習如何寫入文件,請在文本編輯器中輸入以下示例( [write_file_1.rb](code/write_file_1.rb) )并執行它
```rb
#!/usr/bin/ruby
# write_file_1.rb
File.open "god.txt", "w" do |f|
some_txt = <<END_OF_TXT
All things exists because it was created.
Then the creator exists.
Did man ever think how the cretor exist?
If such a mighty creator can exist without creation,
then why can't this simple universe exist without
a creator.
END_OF_TXT
f.puts some_txt
end
```
執行后,打開文件 [god.txt](code/god.txt) ,你將在其中看到
```rb
All things exists because it was created.
Then the creator exists.
Did man ever think how the creator exist?
If such a mighty creator can exist without creation,
then why can't this simple universe exist without
a creator?
```
讓我們遍歷該程序,看看它是如何工作的。 首先在語句`File.open "god.txt", "w"`中,打開一個名為 [god.txt](code/god.txt) 的文件進行寫入。 我們表示我們通過傳遞`“w”`作為第二個參數來打開要寫入的文件。 第二個參數稱為標志。 下面給出了可用于文件操作的標志列表。
<colgroup><col style="width: 50%;"> <col style="width: 50%;"></colgroup>
| 旗 | 說什么 |
| --- | --- |
| [R | 該文件以只讀模式打開。 文件指針位于文件的開頭。 |
| r + | 在 r +模式下,允許讀寫。 文件指針位于文件的開頭 |
| w | 這意味著只寫。 如果文件不存在,則會創建一個新文件并將數據寫入其中。 如果文件存在,則先前的內容將被新的內容替換 |
| w + | 在這種模式下,讀寫都被允許。 如果該文件不存在,則創建一個新文件。 如果存在,則舊內容將丟失,而新內容將被寫入。 |
| 一種 | 該標志以追加模式打開文件。 追加模式是一種特殊的寫入模式,其中新添加的內容位于舊內容的末尾 5,這樣就不會丟失先前的信息。 |
| a + | 允許閱讀和書寫(即追加模式加上閱讀和書寫)。 任何新添加的數據都放置在文件的末尾。 |
| b | 二進制文件模式。 在此模式下,將讀取具有文本以外的數據的文件。 例如,打開音樂或視頻文件。 |
以寫模式打開文件后,我們現在打開了`do` `end`塊,在其中捕獲了變量`f`中的文件句柄。 我們需要做的就是將一個字符串寫入文件。
我們使用以下代碼創建一個字符串
```rb
some_txt = <<END_OF_TXT
All things exists because it was created.
Then the creator exists.
Did man ever think how the creator exist?
If such a mighty creator can exist without creation,
then why can't this simple universe exist without
a creator?
END_OF_TXT
```
現在`some_txt`有了一個字符串,我們需要將其寫入文件。 要將其寫入文件,我們使用以下語句
```rb
f.puts some_txt
```
`gets`獲取文件內容,`puts`向文件中寫入內容,因此我們將`some_txt`作為`puts`函數的參數傳遞,文件中包含的內容也將寫入文件中。 程序結束,文件關閉,僅此而已。 當你打開 [god.txt](code/god.txt) 時,你會看到其中寫的內容。
### 19.13。 將內容追加到文件中
到目前為止,我們已經看到了如何從文件中讀取內容并在其中寫入內容。 現在讓我們看看如何在其中添加內容。 在將內容追加到文件中時,舊內容保留在頁面底部的新內容上。
要了解其工作方式,請鍵入程序 [file_append.rb](code/file_append.rb) 并執行它。
```rb
#!/usr/bin/ruby
# file_append.rb
puts "Enter text to append into file: "
text = gets
f = File.new("log_file.txt", "a")
f.puts "\n"+Time.now.to_s+"\n"+text
```
當程序提示你輸入某些內容時,鍵入`It will be great if dinosaurs were still around`之類的內容,然后按 Enter。 幾次運行后,請運行幾次該程序,鍵入一些內容,然后再打開 [log_file.txt](code/log_file.txt) ,然后看一下其中包含的內容。 當我打開我的時,這就是我得到的:
```rb
Sat Mar 27 16:20:24 +0530 2010
This is my first log
Sat Mar 27 16:21:10 +0530 2010
This is my second log
Sat Mar 27 16:21:36 +0530 2010
This is my third log. Now I'm getting bored.
```
查看你的條目與時間戳的記錄情況。 要了解程序如何讓它遍歷。
第一行`puts "Enter text to append into file: "`會打印出`Enter text to append into file:`,控制會轉到下一行`text = gets`,在此階段程序等待你輸入內容,然后按 Enter 鍵。 當你按回車鍵時,你輸入的內容將存儲在變量`text`中。
下一行`f = File.new("log_file.txt", "a")`是我們程序的關鍵和重點。 在這一行中,我們以附加模式打開一個名為 [log_file.txt](code/log_file.txt) 的文件。 注意,我們將`“a”`作為第二個參數傳遞給`File.new`,這表明我們正在以附加模式打開它。 在這種模式下,先前存儲在文件中的內容不會被刪除和/或被覆蓋,而是在文件末尾寫入新添加的內容。
在追加模式下打開后,我們要做的就是將存儲在可變文本中的內容放入文件中。 由于文件句柄存儲在變量`f`中,我們可以通過編寫`f.puts text`來完成程序,但是我希望如果使用時間戳記錄數據會更好,并且在每次操作前后都留有換行符 記錄日志,以便于閱讀,因此,我編寫了代碼`f.puts "\n"+Time.now.to_s+"\n"+text`。
就是這樣,我們在程序提示符下編寫的內容以及時間戳將存儲到文件中。 在程序結束時,如果我們使用`f.close`關閉了文件,那就太好了,雖然我沒有在程序中完成此操作,但是它可以工作。
### 19.14。 將對象存儲到文件中
到目前為止,我們已經看到了讀取,寫入和追加到文件中,存儲和讀取的內容都是文本。 現在,我們將看到如何將對象或類的實例存儲到文件中。
#### 19.14.1。 Pstore
Pstore 是二進制文件格式,幾乎可以存儲任何格式。 在接下來的示例中,我們將存儲少量屬于 Square 類的對象。 首先,我們將為 Square 寫一個類,并將其放入一個名為 [square_class.rb](code/square_class.rb) 的文件中。 如果你懶惰地復制內容和下方內容并將其放入文件中,如果你是一個活躍的人/女孩,請自行輸入所有內容,最后你將得到相同的結果。
```rb
# square_class.rb
class Square
attr_accessor :side_length
def initialize side_length = 0
@side_length = side_length
end
def area
@side_length * @side_length
end
def perimeter
4 * @side_length
end
end
```
一旦準備好平方類,我們將在兩個不同的地方使用它。 第一個即將到來。 我們創建一個名為 [pstore_write.rb](code/pstore_write.rb) 的程序,在其中鍵入以下內容
```rb
#!/usr/bin/ruby
# pstore_write.rb
require './square_class.rb'
s1 = Square.new
s1.side_length = 4
s2 = Square.new
s2.side_length = 7
require 'pstore'
store = PStore.new('my_squares')
store.transaction do
store[:square] ||= Array.new
store[:square] << s1
store[:square] << s2
end
```
我們現在將通過該程序。 第一行`require './square_class.rb'`將平方類的代碼包含在程序中,這樣我們就可以像在同一文件中鍵入平方類的代碼一樣編寫代碼,這減少了很多輸入并使代碼看起來很整潔。
在下面顯示的下四行中,我們聲明兩個正方形`s1`和`s2`,我們將`s1`邊長指定為 4 個單位,將`s2`邊長指定為 7。
```rb
s1 = Square.new
s1.side_length = 4
s2 = Square.new
s2.side_length = 7
```
在下一行`require 'pstore'`中,我們包含讀取和寫入 pstore 文件格式所需的代碼。 我們不需要像已經為我們編寫的代碼那樣編寫代碼,我們要做的就是鍵入`require 'pstore'`,其中將包含代碼。
接下來,我們使用命令`store = Pstore.new('my_squares')`創建 pstore 文件。 這將創建一個名為`my_squares`的 pstore 文件,并將文件句柄傳遞給名為`store`的變量,使用該變量`store`我們可以讀取并操作該文件`my_squares`。 要開始寫入文件,我們需要啟動一個事務,該事務由以下代碼塊完成
```rb
store.transaction do
end
```
現在,我們可以使用`do` `end`塊中的 pstore 文件進行事務處理。 在代碼塊中,我們添加下面突出顯示的代碼
```rb
store.transaction do
store[:square] ||= Array.new
store[:square] << s1
store[:square] << s2
end
```
第一行創建一個名為`store[:square]`的數組,`||=`表示如果已經存在一個名為`store[:square]`的變量,則無需創建該變量,因為它已經存在。 如果不存在這樣的變量,那么我們需要創建它。 創建數組后,我們使用以下幾行向其中添加方形對象/實例變量`s1`和`s2`
```rb
store[:square] << s1
store[:square] << s2
```
完成后,我們使用 end 命令關閉事務。 只需查看你的工作目錄,你將能夠在其中看到一個名為`my_squares`的文件,如下圖所示:

因此,現在我們已成功寫入名為`my_square`的 pstore 文件。 我們需要做的就是閱讀并確認我們所做的是正確的。 要讀取寫入其中的數據,我們將編寫一個程序 [pstore_read.rb](code/pstore_read.rb) 。
創建一個名為 [pstore_read.rb](code/pstore_read.rb) 的文件,并將下面編寫的程序存儲在其中,執行并觀看輸出。
```rb
#!/usr/bin/ruby
# pstore_read.rb
require './square_class.rb'
require 'pstore'
store = PStore.new('my_squares')
squares = []
store.transaction do
squares = store[:square]
end
squares.each do |square|
puts "Area = #{square.area}"
puts "Perimeter = #{square.perimeter}"
puts "==============================="
end
```
Output
```rb
Area = 16
Perimeter = 16
===============================
Area = 49
Perimeter = 28
===============================
```
如你所見,兩個正方形的面積和周長已打印出來。 如果你覺得我在欺騙你,請使用計算器檢查我們的自我。 很好地了解[中發生了什么事 pstore_write.rb](code/pstore_write.rb) 讓我們遍歷了代碼。 在前兩行
```rb
require 'square_class.rb'
require 'pstore'
```
我們將代碼包含在 [square_class.rb](code/square_class.rb) 中,以及用于將 pstore 文件讀寫到我們程序中的代碼。 就像前面的示例一樣,我們打開 pstore 文件`my_squares`并將文件句柄存儲到下一行名為`store`的變量中
```rb
store = PStore.new('my_squares')
```
現在,我們在下面的行中創建一個名為`squares`的數組
```rb
squares = []
```
使用`store`變量(即`my_squares`句柄),我們打開一個事務,如下所示
```rb
store.transaction do
squares = store[:square]
end
```
在上面代碼所示的`transaction`中,我們使用`squares = store[:square]`將變量`store[:squares]`中的對象轉移到聲明的變量`squares`中,因此,此時變量`square`必須包含兩個正方形對象的內容, 我們在上一個示例中定義了 [pstore_write.rb](code/pstore_write.rb)
取出值后,我們可以使用`end`關鍵字關閉交易。
在下面的代碼中
```rb
squares.each do |square|
puts "Area = #{square.area}"
puts "Perimeter = #{square.perimeter}"
puts "==============================="
end
```
我們將每個對象放在數組正方形中,并將其加載到名為`square`的變量中,然后打印出正方形的周長和面積。
### 19.15。 YAML
YAML 代表 YAML,而不是 XML。 YAML 是一種標記語言,我們可以在其中存儲諸如 Ruby 對象中包含的數據之類的東西。 讓我們編寫一個程序,在其中將方形對象的數據存儲到 YAML 中并進行檢索。 請注意,在此程序中,我們沒有將輸出的 YAML 數據保存到文件中,為什么呢? 僅僅是因為我很懶。 在文本編輯器中輸入代碼 yaml_write.rb 并執行
```rb
#!/usr/bin/ruby
# yaml_write.rb
require 'yaml'
require './square_class.rb'
s = Square.new 17
s1 = Square.new 34
squares = [s, s1]
puts YAML::dump squares
```
執行后,程序將產生以下輸出
```rb
---
- !ruby/object:Square
side_length: 17
- !ruby/object:Square
side_length: 34
```
現在讓我們遍歷該程序。 前兩行
```rb
require 'yaml'
require 'square_class'
```
導入讀取和寫入 YAML 文件所需的代碼。 下一個將代碼加載到 [square_calss.rb](code/square_calss.rb) 中,以便你可以使用正方形對象進行編程。
在以下幾行
```rb
s = Square.new 17
s1 = Square.new 34
```
我們聲明兩個 Square 對象。 一個具有邊長或邊長為 17 個單位,另一個具有邊長為 34 個單位。 在下一行
```rb
squares = [s, s1]
```
我們將對象 s 和 s1 打包到一個稱為 Squares 的數組中。 在下一行
```rb
puts YAML::dump squares
```
我們將形成的數組轉儲到 YAML 中,并使用 puts 語句將其打印到屏幕上。
復制作為輸出出現的內容。 它將用于編寫下一個程序 [yaml_read.rb](code/yaml_read.rb) ,在文本編輯器中鍵入下面顯示的代碼 [yaml_read.rb](code/yaml_read.rb) 并執行它
```rb
#!/usr/bin/ruby
# yaml_read.rb
require 'yaml'
require './square_class'
yaml = <<END
---
- !ruby/object:Square
side_length: 17
- !ruby/object:Square
side_length: 34
END
squares = YAML::load(yaml)
squares.each do |square|
puts "Area = #{square.area}"
puts "Perimeter = #{square.perimeter}"
puts "==============================="
end
```
看一下輸出
```rb
Area = 289
Perimeter = 68
===============================
Area = 1156
Perimeter = 136
===============================
```
顯示的第一組面積和周長是正方形`s`,第二組是正方形`s1`。 讓我們遍歷代碼,了解正在發生的事情。 與往常一樣,這些行:
```rb
require 'yaml'
require './square_class'
```
導入 YAML 所需的代碼,第二個代碼導入 [square_class.rb](code/square_class.rb) 中的代碼,這使我們能夠處理`Square`對象。 接下來我們有一個多行字符串 yaml
```rb
yaml = <<END
---
- !ruby/object:Square
side_length: 17
- !ruby/object:Square
side_length: 34
END
```
yaml 的內容包含在`<<END`和`END`之間,請注意 yaml 的內容是前一個程序的輸出。 專注于這條線
```rb
squares = YAML::load(yaml)
```
這是所有魔術發生的地方。 在這里,Ruby 從 YAML 文件中神奇地發現了我們正在加載存儲在數組中的數據,該數組由兩個`Square`類的對象組成,第一個對象的邊長為 17 個單位,另一個邊長為 34 個單位。 因此`YAML::load`將其用短語表示為 Square 數組,并將其存儲到變量`squares`中。
在下面的代碼中:
```rb
squares.each do |square|
puts "Area = #{square.area}"
puts "Perimeter = #{square.perimeter}"
puts "==============================="
end
```
我們將數組的每個元素加載到變量`square`中,并打印其面積和周長。
- 前言
- 紅寶石
- 先決條件
- 1.安裝 Ruby
- 2.在線資源
- 3.入門
- 4.比較與邏輯
- 5.循環
- 6.數組
- 7.哈希和符號
- 8.范圍
- 9.功能
- 10.可變范圍
- 11.類&對象
- 12.安全導航
- 13.打破大型程序
- 14.結構和 OpenStruct
- 15. Rdoc
- 16. Ruby 樣式指南
- 17.模塊和混入
- 18.日期和時間
- 19.文件
- 20. Proc,Lambda 和塊
- 21.多線程
- 22.異常處理
- 23.正則表達式
- 24.寶石
- 25.元編程
- 26.基準
- 27.測試驅動開發
- 28.觀察者模式
- 29.模板模式
- 30.工廠模式
- 31.裝飾圖案
- 32.適配器模式
- 33.單例模式
- 34.復合模式
- 35.建造者模式
- 36.策略模式
- 贊助商
- 捐
- 人們怎么說
- 版權
- 取得這本書