2

我正在使用 Windows Mobile Compact Edition 6.5 手机并将二进制数据从蓝牙写入文件。这些文件变得非常大,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
4

2 回答 2

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);
于 2011-09-09T21:18:40.057 回答
1

不要因为害怕它太慢而打折一种方法。尝试一下!试一试只需要 5-10 分钟,可能会产生更好的解决方案。

如果数据开始的检测过程不是太复杂/太慢,那么在你点击开始之前避免写入数据实际上可能会使程序更有效地跳过垃圾数据。

这该怎么做:

  • 使用简单的 bool 来了解您是否检测到数据的开始。如果您正在阅读垃圾邮件,那么不要浪费时间将其写入输出,只需扫描它以检测数据的开始即可。找到开始后,停止扫描开始并将数据复制到输出。仅仅复制好的数据只会引起if (found)检查,这实际上不会对您的表现产生任何明显的影响。

您可能会发现它本身就解决了问题。但是如果您需要更高的性能,您可以对其进行优化:

  • 您可以做些什么来最小化您为检测数据开始所做的工作?也许如果您正在寻找一个复杂的序列,您只需要检查一个开始该序列的特定字节值,并且只有当您找到该起始字节时,您才需要进行更复杂的检查。有一些非常简单但有效的字符串搜索算法也可能在这种情况下有所帮助。或者,也许您可​​以分配一个缓冲区(例如 4kB)并逐渐用传入流中的字节填充它。当缓冲区被填满时,只有在你的缓冲区中搜索“垃圾”的末尾。通过对工作进行批处理,您可以利用内存/缓存的一致性来使处理比逐字节执行相同工作时效率更高。

  • 是否需要不断检查所有其他“传入数据的条件”?你怎样才能最大限度地减少你需要做的工作量,但仍然达到所需的结果?也许上面的一些想法在这里也有帮助?

  • 在跳过垃圾邮件时,您真的需要对数据进行任何处理吗?如果没有,那么您可以将整个事情分成两个阶段(跳过垃圾,复制数据),并且在真正重要时跳过垃圾不会花费您任何费用。

于 2011-09-12T21:17:33.273 回答