Чтение и удаление первой (или последней) строки из 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 << "lockingn";
        lock.lock();
        file.open("test.txt", std::fstream::in|std::fstream::out);
        if (!file.is_open()) {
            std::cout << "can't open filen";
            file.close();
            lock.unlock();
            break;
        }
        else if (!std::getline(file,line)) {
            std::cout << "empty filen"; //
            file.close();                // never
            lock.unlock();               // reached
            break;                       //
        }
        else {
            // remove first line
            file.close();
            lock.unlock();
            // do something with line
        }
    }
}

2 ответов


что вы хотите сделать, действительно, непросто.

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

изменение файла на месте выполнимо: просто откройте его, найдите в нем, измените и закройте. Однако, вы хотите скопировать все содержимое файла, кроме K байт в начале файла. Это означает, что вам придется многократно читать и писать весь файл кусками N байт.

после этого, K байты останутся в конце, которые нужно будет удалить. Я не думаю, что есть способ сделать это с потоками. Вы можете использовать ftruncate или truncate функции unistd.h или использовать импульс.Interprocess truncate для этого.

вот пример (без какой-либо проверки ошибок, я позволю вам добавить его):

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

Я действительно хотел бы иметь возможность предоставить более чистое решение для на месте модификация файла, но я не уверен, что она есть.


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

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

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

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

С файл уже в памяти, так как я только что создал и написал, это, очевидно, несколько изменяет время выполнения, но механизм кэширования windows является "виновником" здесь - тот же самый механизм, который мы используем, чтобы сделать операцию очень быстро.

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

проверка ошибок в этой программе практически отсутствует, и ожидается, что вход не будет UNICODE, i.e-вход составляет 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;
}