Эффективный способ удалить строку из текстового файла

Мне нужно удалить определенную строку из текстового файла. Как это сделать наиболее эффективно? Файл может быть потенциально большим (более миллиона записей).

ОБНОВЛЕНИЕ: ниже приведен код, который я использую в настоящее время, но я не уверен, что он хороший.

internal void DeleteMarkedEntries() {
    string tempPath=Path.GetTempFileName();
    using (var reader = new StreamReader(logPath)) {
        using (var writer = new StreamWriter(File.OpenWrite(tempPath))) {
            int counter = 0;
            while (!reader.EndOfStream) {
                if (!_deletedLines.Contains(counter)) {
                    writer.WriteLine(reader.ReadLine());
                }
                ++counter;
            }
        }
    }
    if (File.Exists(tempPath)) {
        File.Delete(logPath);
        File.Move(tempPath, logPath);
    }
}

person Valentin V    schedule 10.02.2009    source источник
comment
Если у вас такое большое хранилище данных, почему вы не используете настоящую базу данных? Это ограничение в имеющихся у вас инструментах, ваших текущих навыках или спецификациях вашего проекта?   -  person Tomas Aschan    schedule 10.02.2009
comment
Это требование сверху. Мне было бы проще использовать настоящую базу данных, но, к сожалению, я не могу ее использовать.   -  person Valentin V    schedule 10.02.2009
comment
Это нехорошо, есть ошибка - извините :( - См. Мой ответ ниже   -  person Binary Worrier    schedule 10.02.2009


Ответы (8)


Самый простой способ сделать это, вероятно, является лучшим: записать весь файл в новый файл, записав все строки, кроме одной (ей), которая вам не нужна.

Или откройте файл для произвольного доступа.

Прочтите до того места, где вы хотите «удалить» строку. Пропустите строку, которую нужно удалить, и прочтите это количество байтов (включая CR + LF - при необходимости), запишите это количество байтов поверх удаленной строки, переместите оба местоположения на это количество байтов и повторите до конца файла.

Надеюсь это поможет.

ИЗМЕНИТЬ - теперь я вижу ваш код.

if (!_deletedLines.Contains(counter)) 
{                            
    writer.WriteLine(reader.ReadLine());                        
}

Не сработает, если это строка, которую вы не хотите, вы все равно хотите ее прочитать, просто не пишите. Приведенный выше код не будет ни читать, ни писать. Новый файл будет точно таким же, как и старый.

Вы хотите что-то вроде

string line = reader.ReadLine();
if (!_deletedLines.Contains(counter)) 
{                            
    writer.WriteLine(line);                        
}
person Binary Worrier    schedule 10.02.2009

Текстовые файлы являются последовательными, поэтому при удалении строки вам придется переместить все следующие строки вверх. Вы можете использовать сопоставление файлов (win32 api, который вы можете вызвать через PInvoke), чтобы сделать эту операцию немного менее болезненной, но вы, безусловно, должны подумать об использовании непоследовательной структуры для вашего файла, чтобы вы могли пометить строку как удаленную без реального удаления это из файла ... Особенно, если это должно случаться нечасто.

Если я помню, File Mapping Api нужно добавить в .Net 4.

person thinkbeforecoding    schedule 10.02.2009

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

Затем используйте периодическую «компактную» процедуру, такую ​​как Outlook, как и в большинстве систем баз данных, которая перезаписывает весь файл, за исключением удаленных строк.

Я бы решительно согласился с ответом Think Before Coding, рекомендующим базу данных или другой структурированный файл.

person Bork Blatt    schedule 10.02.2009
comment
да, требование состоит в том, чтобы иметь возможность иметь файл, читаемый человеком (но я не уверен, как любой человек может пролистать миллион строк!). Я ничего не могу поделать с этим требованием. - person Valentin V; 10.02.2009

В зависимости от того, что именно считается «удалением», лучшим решением может быть перезапись ошибочной строки пробелами. Для многих целей (включая потребление людьми) это эквивалентно удалению строки полностью. Если полученная пустая строка представляет собой проблему, и вы уверены, что никогда не удалите первую строку, вы можете добавить пробелы к предыдущей строке, также перезаписав CRLF двумя пробелами.

(На основе комментария к ответу Борка Блатта)

person MSalters    schedule 10.02.2009

Переместите файл в память с помощью сопоставления файлов, например Think Before Coding сделал и удалил память и после записи на диск.
Прочтите это Тесты чтения файлов - C #
C # доступ к файлу карты памяти

person lsalamon    schedule 10.02.2009

В своем блоге я протестировал различные методы ввода-вывода из C #, чтобы определить наиболее эффективный способ выполнения файлового ввода-вывода. В общем, вам лучше использовать функции Windows ReadFile и WriteFile. Следующий самый быстрый способ чтения файлов - это FileStream. Чтобы получить хорошую производительность, читайте файлы по блокам, а не по строкам, а затем выполняйте собственный синтаксический анализ. Код, который вы можете скачать из моего блога, дает вам пример того, как это сделать. Существует также класс C #, который инкапсулирует функции Windows ReadFile / WriteFile и довольно прост в использовании. См. Подробности в моем блоге по адресу:

http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp

Боб Брайан MCSD

person Bob Bryan    schedule 04.03.2011

Прочтите файл в словаре на не удаляемых строках, установите для int значение 0 в строке, которую необходимо пометить как удаленную, установите для int значение 1. Используйте KeyValuePair для извлечения строк, которые не нужно удалять, и записи их в новый файл. .

Dictionary<string, int> output = new Dictionary<string, int>();

// read line from file

...

// if need to delete line then set int value to 1

// otherwise set int value to 0
if (deleteLine)
{
    output[line] = 1;
}
else
{
    output[line] = 0;
}

// define the no delete List
List<string> nonDeleteList = new List<string>();

// use foreach to loop through each item in nonDeleteList and add each key
// who's value is equal to zero (0) to the nonDeleteList.
foreach (KeyValuePair<string, int> kvp in output)
{

    if (kvp.Value == 0)

    {

        nonDeleteList.Add(kvp.Key);

    }
}

// write the nondeletelist to the output file
File.WriteAllLines("OUTPUT_FILE_NAME", nonDeleteList.ToArray());

Вот и все.

person willjr20    schedule 05.05.2010
comment
Использование словаря - не самый эффективный способ. - person Shrivallabh; 06.01.2014

person    schedule
comment
Ваш ответ может быть улучшен кратким описанием того, чем ваша программа отличается от кода, опубликованного в вопросе. - person Henrik Aasted Sørensen; 04.12.2012