Вопрос

У меня есть огромный файл, в который мне нужно вставить определенные символы в определенное место.Каков самый простой способ сделать это на С#, не переписывая весь файл заново.

Это было полезно?

Решение

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

Возможно, вы захотите взглянуть на SQLite или БерклиДБ.

Опять же, вы можете работать с текстовым файлом или устаревшим двоичным файлом.В этом случае ваш единственный вариант — переписать файл, по крайней мере, от точки вставки до конца.

я бы посмотрел на Файловый поток класс для выполнения случайного ввода-вывода в C#.

Другие советы

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

Невозможно вставить символы в файл без их перезаписи.В C# это можно сделать с любыми классами Stream.Если файлы большие, я бы рекомендовал вам использовать GNU Core Utils внутри кода C#.Они самые быстрые.Раньше я обрабатывал очень большие текстовые файлы с помощью основных утилит (размером 4 ГБ, 8 ГБ или более и т. д.).Такие команды, как head, Tail, Split, Csplit, Cat, Shuf, Shred, uniq, действительно очень помогают в манипулировании текстом.

Например, если вам нужно поместить несколько символов в файл размером 2 ГБ, вы можете использовать команду Split -b BYTECOUNT, поместить результат в файл, добавить к нему новый текст, получить остальную часть содержимого и добавить к нему.Предполагается, что это должно быть быстрее, чем любой другой способ.

Надеюсь, что это работает.Попробуйте.

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

Если вы знаете конкретное место, в которое хотите записать новые данные, используйте класс BinaryWriter:

using (BinaryWriter bw = new BinaryWriter (File.Open (strFile, FileMode.Open)))
{
    string strNewData = "this is some new data";
    byte[] byteNewData = new byte[strNewData.Length];

    // copy contents of string to byte array
    for (var i = 0; i < strNewData.Length; i++)
    {
        byteNewData[i] = Convert.ToByte (strNewData[i]);
    }

    // write new data to file
    bw.Seek (15, SeekOrigin.Begin);  // seek to position 15
    bw.Write (byteNewData, 0, byteNewData.Length);
}

Вы можете посмотреть этот проект:Выиграть инспектор данных

По сути, код следующий:

// this.Stream is the stream in which you insert data

{

long position = this.Stream.Position;

long length = this.Stream.Length;

MemoryStream ms = new MemoryStream();

this.Stream.Position = 0;

DIUtils.CopyStream(this.Stream, ms, position, progressCallback);

ms.Write(data, 0, data.Length);

this.Stream.Position = position;

DIUtils.CopyStream(this.Stream, ms, this.Stream.Length - position, progressCallback);

this.Stream = ms;

}

#region Delegates

public delegate void ProgressCallback(long position, long total);

#endregion

DIUtils.cs

public static void CopyStream(Stream input, Stream output, long length, DataInspector.ProgressCallback callback)
{
    long totalsize = input.Length;
    long byteswritten = 0;
    const int size = 32768;
    byte[] buffer = new byte[size];
    int read;
    int readlen = length < size ? (int)length : size;
    while (length > 0 && (read = input.Read(buffer, 0, readlen)) > 0)
    {
        output.Write(buffer, 0, read);
        byteswritten += read;
        length -= read;
        readlen = length < size ? (int)length : size;
        if (callback != null)
            callback(byteswritten, totalsize);
    }
}

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

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

Файловые системы обычно не предназначены для этой операции.Если вам нужно быстро выполнять вставки, вам действительно нужна более общая база данных.

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

Вам всегда придется перезаписывать оставшиеся байты от точки вставки.Если эта точка равна 0, то вы перепишете весь файл.Если до последнего байта 10 байт, то вы перепишете последние 10 байт.

В любом случае нет функции прямой поддержки «вставки в файл».Но следующий код может сделать это точно.

var sw = new Stopwatch();
var ab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";

// create
var fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
fs.Seek(0, SeekOrigin.Begin);
for (var i = 0; i < 40000000; i++) fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);
fs.Dispose();

// insert
fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
byte[] b = new byte[262144];
long target = 10, offset = fs.Length - b.Length;
while (offset != 0)
{
    if (offset < 0)
    {
        offset = b.Length - target;
        b = new byte[offset];
    }
    fs.Position = offset; fs.Read(b, 0, b.Length);
    fs.Position = offset + target; fs.Write(b, 0, b.Length);
    offset -= b.Length;
}
fs.Position = target; fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);

Чтобы повысить производительность файлового ввода-вывода, поиграйте с «магическими числами с двумя цифрами», как в приведенном выше коде.При создании файла используется буфер размером 262144 байт (256КБ), что совершенно не помогает.Тот же буфер для вставки выполняет «работу по производительности», как вы можете видеть по результатам StopWatch, если запустите код.Черновой тест на моем ПК дал следующие результаты:

13628,8 мс на создание и 3597,0971 мс на вставку.

Обратите внимание, что целевой байт для вставки равен 10, а это означает, что почти весь файл был перезаписан.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top