Самый быстрый способ удалить первые несколько байтов файла

Я использую телефон Windows Mobile Compact Edition 6.5 и записываю двоичные данные в файл из Bluetooth. Эти файлы становятся довольно большими, 16M+, и что мне нужно сделать, так это после того, как файл будет записан, мне нужно найти в файле начальный символ, а затем удалить все, что было раньше, тем самым устранив мусор. Я не могу сделать это встроенным, когда данные поступают из-за проблем с графикой и скоростью, поскольку я получаю много поступающих данных, и уже слишком много условий для входящих данных. Я решил, что лучше опубликовать процесс. В любом случае, вот моя дилемма: скорость поиска начальных байтов и перезаписи файла иногда занимает 5 минут или больше ... Я в основном перемещаю файл во временный файл, анализирую его и переписываю весь новый файл. Я должен делать это байт за байтом.

private void closeFiles() {
    try {

    // Close file stream for raw data.
    if (this.fsRaw != null) {
        this.fsRaw.Flush();
        this.fsRaw.Close();

        // Move file, seek the first sync bytes, 
        // write to fsRaw stream with sync byte and rest of data after it
        File.Move(this.s_fileNameRaw, this.s_fileNameRaw + ".old");
        FileStream fsRaw_Copy = File.Open(this.s_fileNameRaw + ".old", FileMode.Open);
        this.fsRaw = File.Create(this.s_fileNameRaw);

        int x = 0;
        bool syncFound = false;

        // search for sync byte algorithm
        while (x != -1) {
            ... logic to search for sync byte
            if (x != -1 && syncFound) {
                this.fsPatientRaw.WriteByte((byte)x);
            }
        }

        this.fsRaw.Close();

        fsRaw_Copy.Close();
        File.Delete(this.s_fileNameRaw + ".old");
    }


    } catch(IOException e) {
        CLogger.WriteLog(ELogLevel.ERROR,"Exception in writing: " + e.Message);
    }
}

Должен быть более быстрый способ, чем этот!

------------ Время тестирования с использованием ответа -------------

Начальный тест моего способа с одним байтом чтения и одним байтом записи:

27 Kb/sec

используя ответ ниже и буфер размером 32768 байт:

321 Kb/sec

используя ответ ниже и буфер размером 65536 байт:

501 Kb/sec

person JPM    schedule 09.09.2011    source источник
comment
Непроверено, но я несколько раз слышал следующую идиому: сопоставьте файл с памятью, используйте memmove() и сбросьте изменения, усекая.   -  person Kerrek SB    schedule 10.09.2011
comment
@Kerek- это невозможно сделать в управляемом коде, а Phone 7 поддерживает только управляемый код.   -  person ctacke    schedule 10.09.2011
comment
Управляемый код @ctacke поддерживает файлы с отображением памяти. -- См.: документы. microsoft.com/en-us/dotnet/api/   -  person BrainSlugs83    schedule 25.09.2020


Ответы (2)


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

ИЗМЕНИТЬ

Вам не нужно читать все содержимое, чтобы сделать копию. Что-то вроде этого (непроверенное, но вы поняли идею) будет работать.

private void CopyPartial(string sourceName, byte syncByte, string destName)
{
    using (var input = File.OpenRead(sourceName))
    using (var reader = new BinaryReader(input))
    using (var output = File.Create(destName))
    {
        var start = 0;
        // seek to sync byte
        while (reader.ReadByte() != syncByte)
        {
            start++;
        }

        var buffer = new byte[4096]; // 4k page - adjust as you see fit

        do
        {
            var actual = reader.Read(buffer, 0, buffer.Length);
            output.Write(buffer, 0, actual);
        } while (reader.PeekChar() >= 0);
    }

}

ИЗМЕНИТЬ 2

Сегодня мне действительно нужно было что-то подобное, поэтому я решил написать это без вызова PeekChar(). Вот ядро ​​того, что я сделал — не стесняйтесь интегрировать его со вторым циклом do...while выше.

            var buffer = new byte[1024];
            var total = 0;

            do
            {
                var actual = reader.Read(buffer, 0, buffer.Length);
                writer.Write(buffer, 0, actual);
                total += actual;
            } while (total < reader.BaseStream.Length);
person ctacke    schedule 09.09.2011
comment
За исключением двух доступных в CF функций: Read(byte[] a,int offset, int len) и ReadByte(). поэтому создание массива байтов размером более 16 МБ кажется ресурсоемким на телефоне. - person JPM; 10.09.2011
comment
Я только что убедился, что он отлично работает с проектами Phone 7 или CF 3.5. MSDN соглашается, что он также доступен для [телефона] и [простого CF 3.5] [1]msdn.microsoft.com/en-us/library/hf6bat53(v=VS.95).aspx [2]msdn.microsoft.com/en-us/library/hf6bat53(v=VS.90).aspx< /а> - person ctacke; 12.09.2011
comment
Ну, он недоступен в моем фреймворке 6.0 или 6.5, он не будет компилироваться, если я его вставлю (кстати, Target Framework выделен серым цветом, поэтому .Net недоступен). Документация должна быть неправильной... не в первый раз я вижу это с MSDN. - person JPM; 12.09.2011
comment
Нам нужно разъяснение. Нет ни CF 6.0, ни CF 6.5. Были версии 1.0, 2.0 и 3.5 (с некоторыми SP). На какую версию вы ориентируетесь? Как я уже сказал, я тестировал их в Studio (08 для CF и 2010 для телефона), и они отлично компилируются. - person ctacke; 13.09.2011
comment
@JPM: Просто следите за последним буфером данных - вряд ли он точно заполнит ваш 4096-байтовый буфер, поэтому вы можете получить мусор, добавленный к вашему файлу, если он не будет обработан правильно. - person Jason Williams; 13.09.2011
comment
@ Джейсон, поэтому ты пишешь actual байт, а не buffer.Length. - person ctacke; 13.09.2011
comment
@JPM: Что вы имеете в виду под «продолжать меняться»? Я редактировал дважды - оба раза, чтобы добавить код, чтобы уточнить ответ. Я редактировал совсем недавно, потому что добавил код, который не использовал PeekChar, поскольку у вас, похоже, возникли проблемы с его использованием. Опять же, я проверил это на реальном устройстве, поэтому PeekChar существует в CF 3.5, но если вы, возможно, используете что-то более старое или по какой-то причине просто не хотите использовать PeekChar, я обновил свой ответ решением, которое не использует Это. - person ctacke; 13.09.2011
comment
Извините, моя вина, я имел в виду Windows Mobile SDK 6.0 в том, что PeekChar не существует. Но если вы посмотрите, мой вопрос помечен как 6.0 или 6.5. - person JPM; 13.09.2011

Не сбрасывайте со счетов подход, потому что вы боитесь, что он будет слишком медленным. Попробуйте! Это займет всего 5-10 минут, и вы можете найти гораздо лучшее решение.

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

Как это сделать:

  • Используйте простое логическое значение, чтобы узнать, обнаружили ли вы начало данных. Если вы читаете мусор, не тратьте время на запись его в вывод, просто просканируйте его, чтобы обнаружить начало данных. Как только вы найдете начало, остановите сканирование начала и просто скопируйте данные на выход. Простое копирование хороших данных повлечет за собой не более if (found) проверки, которая на самом деле не окажет заметного влияния на вашу производительность.

Вы можете обнаружить, что это само по себе решает проблему. Но вы можете оптимизировать его, если вам нужно больше производительности:

  • Что вы можете сделать, чтобы свести к минимуму работу по обнаружению начала данных? Возможно, если вы ищете сложную последовательность, вам нужно только проверить одно конкретное значение байта, которое запускает последовательность, и только если вы найдете этот начальный байт, вам нужно выполнить более сложную проверку. Есть несколько очень простых, но эффективных алгоритмов поиска строк, которые могут помочь и в этом случае. Или, возможно, вы можете выделить буфер (например, 4 КБ) и постепенно заполнять его байтами из вашего входящего потока. Когда буфер заполнен, тогда и только тогда найдите конец «мусора» в вашем буфере. Группируя работу, вы можете использовать согласованность памяти и кэша, чтобы сделать обработку значительно более эффективной, чем если бы вы выполняли ту же работу байт за байтом.

  • Нужно ли постоянно проверять все остальные «условия входящих данных»? Как свести к минимуму объем работы, которую необходимо выполнить, но при этом достичь требуемых результатов? Возможно, некоторые из приведенных выше идей могут помочь и здесь?

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

person Jason Williams    schedule 12.09.2011
comment
Джейсон. Ваше утверждение «Простое копирование хороших данных потребует не более проверки «если (найдено)»» справедливо для большинства случаев, за исключением случаев, когда условие действительно влияет на скорость или если вы пытаетесь оптимизировать поток. Я обнаружил, что это верно, когда запускал поток Bluetooth SPP для чтения данных, поступающих с огромной скоростью, и из этого я выводил график на экран и сохранял в файл на телефоне. Так что любое небольшое условие повлияло на построение графика. Но я согласен, если вы можете скопировать хорошие данные, сделайте это... - person JPM; 13.09.2011
comment
@JPM: Тестирование логического значения добавит значительных накладных расходов только в том случае, если вы делаете это много тысяч раз. Хитрость заключается не в том, чтобы решить, как выполнять больше работы, а в том, как избежать выполнения ненужной работы для достижения тех же целей. На самом деле нет необходимости очень часто проверять bool. Просто посмотрите на разницу в скорости, которую вы указали, между чтением и записью по одному байту за раз и чтением и записью кусками по 64 КБ. Это просто потому, что вы выполняете одну операцию чтения/записи вместо 65536 отдельных операций, что устраняет много ненужных накладных расходов (устраняет 65535 вызовов функций и т. д.). - person Jason Williams; 14.09.2011
comment
Если вы дадите более подробную информацию о том, как вы обнаруживаете конец ненужных данных, я смогу предложить более конкретные предложения. Но тратить минуты на пост-обработку массивного файла, чтобы переместить все данные на несколько байтов вниз, — это очень пахнет строковым кодом — должен быть лучший способ. - person Jason Williams; 14.09.2011