2016-03-01 88 views
6

我想讀取並從txt文件中刪除第一行(沒有複製,它是一個巨大的文件)。
我已經讀過網絡,但是每個人都只是將所需內容複製到一個新文件。我不能那樣做。從txt文件中讀取並刪除第一行(或最後一行)而不復制

低於第一次嘗試。這段代碼將被刪除,因爲沒有行被刪除。如果代碼將在每次開啓時刪除第一行文件,代碼將會結束。

#include <iostream> 
#include <string> 
#include <fstream> 
#include <boost/interprocess/sync/file_lock.hpp> 

int main() { 
    std::string line; 
    std::fstream file; 
    boost::interprocess::file_lock lock("test.lock"); 
    while (true) { 
     std::cout << "locking\n"; 
     lock.lock(); 
     file.open("test.txt", std::fstream::in|std::fstream::out); 
     if (!file.is_open()) { 
      std::cout << "can't open file\n"; 
      file.close(); 
      lock.unlock(); 
      break; 
     } 
     else if (!std::getline(file,line)) { 
      std::cout << "empty file\n"; // 
      file.close();    // never 
      lock.unlock();    // reached 
      break;      // 
     } 
     else { 
      // remove first line 
      file.close(); 
      lock.unlock(); 
      // do something with line 
     } 
    } 
} 
+1

文件只是不工作方式(類似於原始數組,你不能刪除第一個元素,而不是將所有剩餘元素移動一個時隙)。 – crashmstr

+0

'if'語句的每個分支都有'file.close(); lock.unlock();'。 'std :: file'對象的析構函數將關閉文件,所以你不需要顯式關閉它(當'file.is_open()'返回false時,不需要關閉它)。毫無疑問,Boost中的RAII類型用於管理該鎖,並使用析構函數將其解鎖。 –

+0

是的,「所有人」... https://www.google.com/search?q=c%2B%2B+modify+file+in+place –

回答

3

你想要做的事實的確不容易。

如果您打開相同的文件進行讀寫操作而不小心,最終會讀出剛寫入的內容,結果將不會是您想要的。

修改文件到位是可行的:只需打開它,在其中尋找,修改和關閉。但是,您希望複製文件的所有內容,但在文件的開頭部分除K字節外。這意味着你將不得不通過N字節的塊來反覆讀取和寫入整個文件。

現在一旦完成,K字節將保留在需要刪除的末尾。我認爲沒有辦法用流來完成。您可以使用ftruncatetruncate函數從unistd.h或使用Boost.Interprocesstruncate爲此。

下面是一個例子(沒有任何錯誤檢查,我讓你加吧):

#include <iostream> 
#include <fstream> 
#include <unistd.h> 

int main() 
{ 
    std::fstream file; 
    file.open("test.txt", std::fstream::in | std::fstream::out); 

    // First retrieve size of the file 
    file.seekg(0, file.end); 
    std::streampos endPos = file.tellg(); 
    file.seekg(0, file.beg); 

    // Then retrieve size of the first line (a.k.a bufferSize) 
    std::string firstLine; 
    std::getline(file, firstLine); 

    // We need two streampos: the read one and the write one 
    std::streampos readPos = firstLine.size() + 1; 
    std::streampos writePos = 0; 

    // Read the whole file starting at readPos by chunks of size bufferSize 
    std::size_t bufferSize = 256; 
    char buffer[bufferSize]; 
    bool finished = false; 
    while(!finished) 
    { 
    file.seekg(readPos); 
    if(readPos + static_cast<std::streampos>(bufferSize) >= endPos) 
    { 
     bufferSize = endPos - readPos; 
     finished = true; 
    } 
    file.read(buffer, bufferSize); 
    file.seekg(writePos); 
    file.write(buffer, bufferSize); 
    readPos += bufferSize; 
    writePos += bufferSize; 
    } 
    file.close(); 

    // No clean way to truncate streams, use function from unistd.h 
    truncate("test.txt", writePos); 
    return 0; 
} 

我真的希望能夠爲文件的就地改造提供一個清潔的解決方案,但我不確定有一個。

+0

只讀取和刪除最後一行會更容易嗎?這樣的解決方案對我來說也是足夠的。 – user1587451

+0

對於流,AFAIK,除非用截斷標誌打開,否則不能縮小文件的大小,只能增加它(但我可能是錯的)。 –

2

這是一個用C語言編寫的用於Windows的解決方案。 它將立即執行並完成一個700,000行,245MB的文件。 (0.14秒)

基本上,我內存映射文件,這樣我就可以使用用於原始內存訪問的函數訪問內容。一旦文件被映射後,我只需使用strchr函數來查找窗口(\ n和\ r)中用於表示EOL的一對符號之一的位置 - 這告訴我們第一行的字節數是多少。

從這裏,我只是從第二行的第一個字節回到內存映射區域(基本上是文件中的第一個字節)的開始處的memcpy。

完成此操作後,文件將被取消映射,關閉mem映射文件的句柄,然後使用SetEndOfFile函數將文件的長度減少第一行的長度。當我們關閉文件時,它縮小了這個長度,第一行消失了。

由於我剛剛創建並寫入文件,因此文件已經在內存中,顯然會稍微改變執行時間,但Windows緩存機制在這裏是'罪魁禍首' - 我們正在利用這一機制來製作操作很快完成。

測試數據是重複100,000次的程序的源代碼,並保存爲testInput2.txt(粘貼10次,全選,複製,粘貼10次 - 替換原來的10次,共計100次) - 重複直到輸出足夠大。我停下來這裏,是因爲更似乎讓記事本+ +一個「有點」不高興)

查錯該計劃實際上是不存在的,並輸入預計不會是UNICODE,即 - 輸入爲每個字符1個字節。 的EOL序列是0X0D,的0x0A(\ r \ n)的

代碼:

#include <stdio.h> 
#include <windows.h> 

void testFunc(const char inputFilename[]) 
{ 
    int lineLength; 

    HANDLE fileHandle = CreateFile(
            inputFilename, 
            GENERIC_READ | GENERIC_WRITE, 
            0, 
            NULL, 
            OPEN_EXISTING, 
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 
            NULL 
            ); 

    if (fileHandle != INVALID_HANDLE_VALUE) 
    { 
     printf("File opened okay\n"); 

     DWORD fileSizeHi, fileSizeLo = GetFileSize(fileHandle, &fileSizeHi); 

     HANDLE memMappedHandle = CreateFileMapping(
                fileHandle, 
                NULL, 
                PAGE_READWRITE | SEC_COMMIT, 
                0, 
                0, 
                NULL 
               ); 
     if (memMappedHandle) 
     { 
      printf("File mapping success\n"); 
      LPVOID memPtr = MapViewOfFile(
              memMappedHandle, 
              FILE_MAP_ALL_ACCESS, 
              0, 
              0, 
              0 
             ); 
      if (memPtr != NULL) 
      { 
       printf("view of file successfully created"); 
       printf("File size is: 0x%04X%04X\n", fileSizeHi, fileSizeLo); 

       LPVOID eolPos = strchr((char*)memPtr, '\r'); // windows EOL sequence is \r\n 
       lineLength = (char*)eolPos-(char*)memPtr; 
       printf("Length of first line is: %ld\n", lineLength); 

       memcpy(memPtr, eolPos+2, fileSizeLo-lineLength); 
       UnmapViewOfFile(memPtr); 
      } 

      CloseHandle(memMappedHandle); 
     } 
     SetFilePointer(fileHandle, -(lineLength+2), 0, FILE_END); 
     SetEndOfFile(fileHandle); 
     CloseHandle(fileHandle); 
    } 
} 

int main() 
{ 
    const char inputFilename[] = "testInput2.txt"; 
    testFunc(inputFilename); 
    return 0; 
}