2016-03-01 7 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 останутся в конце, которые необходимо будет удалить. Я не думаю, что есть способ сделать это с потоками. Вы можете использовать ftruncate или truncate функции от 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, если вы не открываете флагом truncate, вы не можете уменьшить размер файла, вы можете только увеличить его (но я могу ошибаться). –

2

Вот решение, написанное на C для Windows. Он выполнит и закончит на 700 000 строк, 245 МБ файла в мгновение ока. (0.14 секунд)

В принципе, я храню карту памяти, чтобы получить доступ к содержимому с помощью функций, используемых для доступа к необработанной памяти. Как только файл был сопоставлен, я просто использую функцию strchr, чтобы найти местоположение одной из пары символов, используемых для обозначения EOL в окнах (\ n и \ r) - это говорит нам, как долго в байтах первая строка ,

Отсюда я просто memcpy от первого байта f второй строки до начала области отображения памяти (в основном, первый байт в файле).

После этого файл не отображается, дескриптор файла mem-mapped закрыт, и затем мы используем функцию SetEndOfFile, чтобы уменьшить длину файла по длине первой строки. Когда мы закрываем файл, он уменьшился на эту длину, и первая строка исчезла.

Имея файл уже в памяти, так как я только что создал и написал, он, очевидно, немного меняет время выполнения, но механизм кэширования окон является «виновником» здесь - тем же самым механизмом, который мы используем для создания работа завершена очень быстро.

Данные испытаний являются источником программы, дублированной 100 000 раз и сохраняются как testInput2.txt (вставьте его 10 раз, выберите все, скопируйте, вставьте 10 раз - замените оригинал 10, в общей сложности 100 раз - повторите до выхода достаточно большой.Я остановился здесь, потому что больше, похоже, делало Notepad ++ «бит» недовольным)

Ошибка проверки в этой программе практически не существует, и ожидается, что вход не будет UNICODE, т. Е. Вход 1 байт на символ. Последовательность EOL является 0x0D, 0x0A (\ г \ п)

Код:

#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; 
}