2015-06-10 81 views
7

我正在製作一個文本編輯器並編輯一個文件我真的需要某種方式來只讀取文件中的某些字節,使用fs.createReadStream uisng實現startend選項。Node.js v0.10:替換文件中的某些字節而不讀取整個文件

我還需要替換文件中的某些字節。我不確定如何做到這一點。到目前爲止,我提出的最佳解決方案是使用流讀取文件,然後寫入新文件,當遇到要查找的字節時,我會寫入新的內容,從而替換舊的內容新的東西。

這不是最好的方法,因爲你可能知道。要編輯4個字節,我正在讀取一個巨大的2GB文件並寫入2GB(假設我正在編輯一個2GB文件),效率不高。

達到此目的的最佳方法是什麼?我花了兩週的時間來做這件事,我也想過使用緩衝區,但是緩衝區會將整個文件加載到內存中,如果它是2GB的文件,這又是無效的。

如何在不讀取整個文件的情況下替換文件中的某些字節,並且不安裝某些具有C++代碼的npm包。我不希望我的編輯器必須編譯C++代碼。

如果這樣做並不直截了當,如何在不讀取整個文件的情況下從文件中刪除某些字節?如果我能做到這一點,那麼我可以刪除要替換的字節,並使用類似fs.write()的東西來添加我希望它們替換的字節。

編輯#1:

玩弄後,我發現,如果我打開一個文件fs.open與標誌r+然後fs.write取代東西。所以如果文本是「Lorem ipsum」,並且我fs.write「!!!!」結果將是「!!!! m ipsum」。

如果只有我要寫的所有東西都是完美的長度,這將工作正常。 :/

我知道如果新內容不是完美的長度,但我不知道如何做。 :/也許,如果有某種「空字節」的...

編輯#2:

所以如上所說,fs.open(與r+標誌選項)+ fs.write讓我重寫內容在一個文件中沒有讀取整個文件,這是了不起的。現在,我遇到了一個新問題。讓我們以下列文件:

one\n 
two\n 
three\n 

如果我fs.open在0字節,然後fs.write「是」,我結束了:

yes\n 
two\n 
three\n 

如果我做同樣的,而是fs.write「Niet的」,我結束了:

niettwo\n 
three\n 

通知的\n人物是如何與「T」所取代,這是因爲如何fs.write作品代表的當在fs.open中使用r+時拉緊字節。這是我現在要解決的問題。

一個會如何做這樣的事情「從這個字節到該字節,這些其他字節替換」所以我的功能可能是像function replaceBytes(filePath, newBytes, startByte, endByte),並且將取代僅從startByteendByte,無論多久newBytes ,無論是短於還是長於endByte - startByte的長度。

編輯#3:

OK,我想通了其中的新內容長於被替換舊的內容的情況下。感謝\x00,我已經能夠弄清楚了。如果新內容和舊內容的長度相同,則不難發現,因爲這裏沒有任何內容。

但是,舊內容比新內容短的情況下,仍然沒有解決。

對於那些好奇,這是舊的內容不是新的內容不再是工作代碼:https://github.com/noedit/file/blob/592a35134440a03d3e3c3e366f6cda7f565c11aa/lib/replaceBytes.js#L27-L34

雖然它擺在那裏,一個空字節這取決於編輯器,它可能會顯示爲一個字符,因此看起來很奇怪。 :/

+0

聽起來好像你在問什麼是不可能的,因爲文件系統不能在文件中間展開和收縮。你有任何跡象表明這是由其他程序完成的嗎? –

+0

@AaronDufour我認爲*用C編寫的文本編輯器之前已經完成了這項工作,否則編輯大文件的效率非常低下......除非所有的文本編輯器都做臨時文件,他們會將整個文件寫入臨時文件,並進行修改,然後rm舊文件和mv新文件到位。或類似的東西。這聽起來令人難以置信的效率不高...... – greduan

+0

C stdlib沒有提供任何可以讓你去做你要求的東西(再一次,因爲文件系統不允許它)。但是,寫入文件可以在後臺完成,因此不應該有任何明顯的影響。 –

回答

6

正如你已經發現,fs.writer+模式,可以覆蓋字節。這足夠用於添加和刪除的部分長度完全相同的情況。

當添加的文本短於刪除的文本時,我建議您不要填寫\x00字節,正如您在其中一次編輯中所建議的那樣。這些在大多數類型的文件中都是完全有效的字符(在源代碼中,它們通常會導致編譯器/解釋器拋出錯誤)。

簡而言之,這通常是不可能的。這不是一個抽象問題;在文件系統級別,文件以連續字節塊存儲。沒有通用的方法來從文件的中間插入/刪除。

正確的方法是找到需要更改的第一個字節,然後編寫其餘的文件(除非您已經添加/刪除了相同數量的字節,在這種情況下你可以停止寫作)。

爲了避免在長時間寫入或類似情況下發生崩潰問題,通常會寫入臨時文件位置,然後寫入臨時文件來代替您希望保存的實際文件。

+1

在我看來,這是正確的答案,最重要的部分是最後一句話 – Paolo

2

如果您手動打開一個文件(fs.open()並使用適當的文件模式,如r+),則可以使用fs.write()在文件中的特定位置進行寫入。

如果你需要更靈活的文件中定位上有故宮提供fs.seek(),使您可以尋求一些例如從當前位置偏移ň字節模塊,如fs-ext

+0

不幸的是'fs-ext'有C++編譯。我研究了'fs.open()'和'fs.write()',但它似乎並沒有提供從文件中刪除特定字節的東西,我正在尋找。 :( – greduan

+0

那麼刪除(實際刪除,而不是標記爲0x00')單個字節沒有單一功能。爲此,您需要手動執行該操作,方法是在待刪除的字節之後覆蓋要刪除的* n *個字節(字節*)(並截斷文件末尾的* n *個字節)。 – mscdex

+0

因此,基本上我會將要刪除的字節標記爲0x00,從文件截取* n個字節(根據添加的數量),然後添加我想用fs替換它的新內容.open'和'fs.write'? – greduan

2

試試下面的代碼片段:

新的解決方案:

var fs = require('fs'); 
var startByte = 3, 
     endByte = 6, 
    newBytes ='replacing with this line', 
    filePath ='sample.txt'; 

function replaceBytes(filePath, startByte, endByte, newBytes) 
{ 

    var fsWriteStream = fs.createWriteStream('temp.txt', {flags: 'w+'}); 
    var fsReadStream = fs.createReadStream(filePath, {start: endByte+1}); 
    fsReadStream.pipe(fsWriteStream); 

    fsWriteStream.on('finish', function(){ 
    var fsReadStream2 = fs.createReadStream('temp.txt'); 
    var fsWriteStream2 = fs.createWriteStream(filePath, {start: startByte, flags: 'r+'}); 
    fsWriteStream2.write(newBytes); 
    fsReadStream2.pipe(fsWriteStream2); 
    //fsWriteStream2.end(); 
    }); 


} 

replaceBytes(filePath, startByte, endByte, newBytes); 

老辦法:

秒 - 起始字節

的R - 文本與

被替換

文件 - 文件,其中文本必須更換

var fs = require('fs'); 
var s = 3, 
    R ='replacing with this line', 
    file ='sample.txt'; 

function replace(file, s, R) 
{ 
    var N = R.length; 
    var fsWriteStream = fs.createWriteStream(file, {start: s, flags: 'r+'}); 
    fsWriteStream.write(R); 
    fsWriteStream.end(); 
} 

replace(file, s, R); 
+0

試過了,這似乎與我發佈的關於使用fs.open和fs.write的相同。我錯了嗎?另外,因爲該標誌是'a +',一個版本'a',其中Linux不支持位置寫入,如果您使用'a'標誌並期望您的代碼被使用,則'start'選項不可用在Linux中(我這樣做)。 :/ – greduan

+0

我在Mac(OSX)及其工作環境中測試了它。你可以嘗試在Linux上使用r +選項嗎? –

+0

我目前沒有訪問Linux的機器,但是我正在接受文檔的文字,並假設'r +'按預期工作,但'a'或'a +'不能。儘管如此,這並不能解決我目前遇到的問題。很快會更新這個問題。 :) – greduan

相關問題