2011-07-30 35 views
3

我試圖做一個簡單的查找/替換目錄中的所有文本文件,修改任何[RAVEN_START:實例插入一個字符串(在這裏,'烏鴉在這裏')在行之前。爲什麼不會gsub!改變我的檔案?

這裏是整個Ruby程序:

#!/usr/bin/env ruby 
require 'rubygems' 
require 'fileutils' #for FileUtils.mv('your file', 'new location') 

class RavenParser 

    rawDir = Dir.glob("*.txt") 
    count = 0 
    rawDir.each do |ravFile| 
    #we have selected every text file, so now we have to search through the file 
    #and make the needed changes. 
    rav = File.open(ravFile, "r+") do |modRav| 
     #Now we've opened the file, and we need to do the operations. 
     if modRav    
     lines = File.open(modRav).readlines 
     lines.each { |line| 
     if line.match /\[RAVEN_START:.*\]/ 
      line.gsub!(/\[RAVEN_START:/, 'raven was here '+line) 
      count = count + 1 
     end 
     } 
     printf("Total Changed: %d\n",count) 
     else 
     printf("No txt files found. \n") 
     end 
    end 
    #end of file replacing instructions. 
    end 

    # S 
end 

的程序運行和編譯罰款,但是當我打開文本文件時,出現了任何文件中的文字沒有變化。 count正確遞增(即,它等於所有文件中[RAVEN_START:的實例數),但實際的替換無法進行(或至少不保存更改)。

我的gsub!語法錯誤嗎?我在做別的事嗎?

回答

6

您正在讀取數據,更新數據,然後忽略將其寫回文件。你需要的東西,如:

# And save the modified lines. 
File.open(modRav, 'w') { |f| f.puts lines.join("\n") } 

之前或之後這樣的:

printf("Total Changed: %d\n",count) 

由於DMG下面的注意事項,只是覆蓋該文件沒有正確偏執,你可以在寫中間被打斷並丟失數據。如果你想成爲偏執(我們所有人都應該是因爲他們真的外出讓我們),那麼你要寫入temporary file,然後做一個原子重命名,以替換原文件的新的。重命名一般只有當你與一個Tempfile留一個單一的文件系統中,因爲無法保證操作系統的臨時目錄(Tempfile默認使用)將是相同的文件系統modRav就這麼File.rename甚至可能不是一個選項的作用除非採取預防措施。但Tempfile構造函數採用tmpdir參數,所以我們要保存:

modRavDir = File.dirname(File.realpath(modRav)) 
tmp  = Tempfile.new(modRav, modRavDir) 
tmp.write(lines.join("\n")) 
tmp.close 
File.rename(tmp.path, modRav) 

你可能想堅持,在一個單獨的方法(safe_save(modRav, lines)也許),以避免進一步弄亂塊。

+3

當然,如果程序在正確的地方中斷,你可能最終丟失數據這樣......這是更好地輸出到一個臨時文件,然後再移動文件回到原來的名稱。請參閱:http://ruby-doc.org/stdlib/libdoc/tempfile/rdoc/classes/Tempfile.html和http://pleac.sourceforge.net/pleac_ruby/fileaccess.html – DGM

+0

@DGM:的確,一個臨時文件後跟原子重命名將是適當的偏執狂。但是,Tempfile上的重命名可能不是原子的,因爲臨時文件可能位於另一個文件系統上,因此它最終可能是文件副本而不是簡單的「rename」系統調用。因此,在構建Tempfile時,需要將'tmpdir'設置爲匹配'modRav'。我將添加一個關於完整性的更新。 –

+1

完全正確。我知道那裏有知識,但我沒有提出任何更好的睡眠方面的鏈接。但是這個小例程**應該是編程世界的常識。如果不是,它需要更多的曝光。 :) – DGM

1

您未使用gsub!,您正在使用gsubgsub!gsub不同的方法,一個替換對象本身,另一個替換然後返回結果。

更改此

​​

這樣:

line.gsub!(/\[RAVEN_START:/, 'raven was here '+line)

或本:

line = line.gsub(/\[RAVEN_START:/, 'raven was here '+line)

String#gsub獲取更多信息

+0

我已更新原始帖子。我*是*使用gsub !.愚蠢的複製+粘貼錯誤在我的部分。 –

5
  1. 該帖子中沒有gsub!(標題和問題除外)。我實際上推薦而不是使用gsub!,而是使用gsub的結果 - 避免可變性可以幫助減少一些微妙的錯誤。

  2. 從文件中讀取流字符串是拷貝和修改也不會影響該文件的內容。 (一般的方法是讀取一行,處理行並寫入行,或者一次完成所有行:讀取所有行,處理所有行,寫入所有處理過的行。無論哪種情況,都不會將任何內容寫回文件中的代碼;-)

快樂編碼。

+0

Re @ 2。 - 我該如何解決它,以便我*寫回文件? –

+0

@Raven Dreamer'File.open'返回['class IO']的對象(http://www.ruby-doc.org/core/classes/IO.html)。一種方法是讀取所有行(完成後),*截斷IO(清除所有數據,保持文件打開),然後在處理每行時寫入處理後的內容 - 在這種情況下,簡單的' gsub'沒問題,因爲結果被傳遞給'write'並且沒有另外存儲:)確保文件在正確的模式下打開並附加適當的行結束符。 – 2011-07-30 06:30:12

+0

糟糕,我忘了Ruby沒有'IO.truncate'。在這種情況下,關閉文件(打開閱讀以獲取行),然後重新打開文件進行寫入 - 「w」標誌 - 不指定附加。 – 2011-07-30 06:38:00

相關問題