【问题标题】:Replace sequence of bytes in binary file替换二进制文件中的字节序列
【发布时间】:2011-06-29 18:48:11
【问题描述】:

将二进制文件中的字节序列替换为与其他字节相同长度的最佳方法是什么?二进制文件将非常大,大约 50 mb,不应立即加载到内存中。

更新:我不知道需要替换的字节位置,我需要先找到它们。

【问题讨论】:

  • 以写入模式打开文件,将指针移动到旧字节的位置,写入新字节。
  • 如何知道要修改的字节的确切位置?是固定偏移量吗?

标签: c# replace byte binaryfiles


【解决方案1】:

假设您正在尝试替换文件的已知部分

  • 打开具有读/写权限的FileStream
  • 找对地方
  • 覆盖现有数据

示例代码来了...

public static void ReplaceData(string filename, int position, byte[] data)
{
    using (Stream stream = File.Open(filename, FileMode.Open))
    {
        stream.Position = position;
        stream.Write(data, 0, data.Length);
    }
}

如果您正在有效地尝试执行 string.Replace 的二进制版本(例如,“总是将字节 { 51, 20, 34} 替换为 { 20, 35, 15 } 那么它会更难。作为对你会做什么:

  • 分配一个至少为您感兴趣的数据大小的缓冲区
  • 反复读入缓冲区,扫描数据
  • 如果找到匹配项,请返回正确的位置(例如 stream.Position -= buffer.Length - indexWithinBuffer; 并覆盖数据

到目前为止听起来很简单......但棘手的一点是数据是否在缓冲区末尾附近开始。您需要记住所有 潜在 匹配项以及到目前为止匹配的距离,以便在读取 next 缓冲区的值时获得匹配项,您可以检测到它。

可能有一些方法可以避免这种棘手的问题,但我不想尝试随便想出它们:)

编辑:好的,我有一个想法可能会有所帮助......

  • 保留至少两倍于您需要的缓冲区
  • 反复:
    • 将缓冲区的后半部分复制到前半部分
    • 从文件中填充缓冲区的后半部分
    • 在整个整个缓冲区中搜索您要查找的数据

这样,在某个时候,如果数据存在,它将完全在缓冲区内。

您需要注意流的位置才能返回正确的位置,但我认为这应该可行。如果您试图找到 所有 个匹配项,那就更麻烦了,但至少第一个匹配项应该相当简单......

【讨论】:

  • 我忘了在我原来的问题中指出我不知道替换字节的位置。
  • @Tomas:那么我的最后一句话是对你所追求的内容的合理描述吗?
  • @Jon,抱歉我忘了说明。我们应该在文件中找到第一个字节序列,然后替换它们。我知道这很难做到,而且我在谷歌上搜索也没有任何运气。所以在这里发布了问题。
  • @Tomas:好的,我已经大致描述了你会如何去做。
  • 再次感谢您的想法。我已经构建了方法,您可以在stackoverflow.com/questions/6536400/… 找到
【解决方案2】:

我的解决方案:

    /// <summary>
    /// Copy data from a file to an other, replacing search term, ignoring case.
    /// </summary>
    /// <param name="originalFile"></param>
    /// <param name="outputFile"></param>
    /// <param name="searchTerm"></param>
    /// <param name="replaceTerm"></param>
    private static void ReplaceTextInBinaryFile(string originalFile, string outputFile, string searchTerm, string replaceTerm)
    {
        byte b;
        //UpperCase bytes to search
        byte[] searchBytes = Encoding.UTF8.GetBytes(searchTerm.ToUpper());
        //LowerCase bytes to search
        byte[] searchBytesLower = Encoding.UTF8.GetBytes(searchTerm.ToLower());
        //Temporary bytes during found loop
        byte[] bytesToAdd = new byte[searchBytes.Length];
        //Search length
        int searchBytesLength = searchBytes.Length;
        //First Upper char
        byte searchByte0 = searchBytes[0];
        //First Lower char
        byte searchByte0Lower = searchBytesLower[0];
        //Replace with bytes
        byte[] replaceBytes = Encoding.UTF8.GetBytes(replaceTerm);
        int counter = 0;
        using (FileStream inputStream = File.OpenRead(originalFile)) {
            //input length
            long srcLength = inputStream.Length;
            using (BinaryReader inputReader = new BinaryReader(inputStream)) {
                using (FileStream outputStream = File.OpenWrite(outputFile)) {
                    using (BinaryWriter outputWriter = new BinaryWriter(outputStream)) {
                        for (int nSrc = 0; nSrc < srcLength; ++nSrc)
                            //first byte
                            if ((b = inputReader.ReadByte()) == searchByte0
                                || b == searchByte0Lower) {
                                bytesToAdd[0] = b;
                                int nSearch = 1;
                                //next bytes
                                for (; nSearch < searchBytesLength; ++nSearch)
                                    //get byte, save it and test
                                    if ((b = bytesToAdd[nSearch] = inputReader.ReadByte()) != searchBytes[nSearch]
                                        && b != searchBytesLower[nSearch]) {
                                        break;//fail
                                    }
                                    //Avoid overflow. No need, in my case, because no chance to see searchTerm at the end.
                                    //else if (nSrc + nSearch >= srcLength)
                                    //    break;

                                if (nSearch == searchBytesLength) {
                                    //success
                                    ++counter;
                                    outputWriter.Write(replaceBytes);
                                    nSrc += nSearch - 1;
                                }
                                else {
                                    //failed, add saved bytes
                                    outputWriter.Write(bytesToAdd, 0, nSearch + 1);
                                    nSrc += nSearch;
                                }
                            }
                            else
                                outputWriter.Write(b);
                    }
                }
            }
        }
        Console.WriteLine("ReplaceTextInBinaryFile.counter = " + counter);
    }

【讨论】:

  • 对我来说效果很好。
【解决方案3】:

您可以使用我的 BinaryUtility 来搜索和替换一个或多个字节,而无需像这样将整个文件加载到内存中:

var searchAndReplace = new List<Tuple<byte[], byte[]>>() 
{
    Tuple.Create(
        BitConverter.GetBytes((UInt32)0xDEADBEEF),
        BitConverter.GetBytes((UInt32)0x01234567)),
    Tuple.Create(
        BitConverter.GetBytes((UInt32)0xAABBCCDD),
        BitConverter.GetBytes((UInt16)0xAFFE)),
};
using(var reader =
    new BinaryReader(new FileStream(@"C:\temp\data.bin", FileMode.Open)))
{
    using(var writer =
        new BinaryWriter(new FileStream(@"C:\temp\result.bin", FileMode.Create)))
    {
        BinaryUtility.Replace(reader, writer, searchAndReplace);
    }
}

BinaryUtility 代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public static class BinaryUtility
{
    public static IEnumerable<byte> GetByteStream(BinaryReader reader)
    {
        const int bufferSize = 1024;
        byte[] buffer;
        do
        {
            buffer = reader.ReadBytes(bufferSize);
            foreach (var d in buffer) { yield return d; }
        } while (bufferSize == buffer.Length);
    }

    public static void Replace(BinaryReader reader, BinaryWriter writer, IEnumerable<Tuple<byte[], byte[]>> searchAndReplace)
    {
        foreach (byte d in Replace(GetByteStream(reader), searchAndReplace)) { writer.Write(d); }
    }

    public static IEnumerable<byte> Replace(IEnumerable<byte> source, IEnumerable<Tuple<byte[], byte[]>> searchAndReplace)
    {
        foreach (var s in searchAndReplace)
        {
            source = Replace(source, s.Item1, s.Item2);
        }
        return source;
    }

    public static IEnumerable<byte> Replace(IEnumerable<byte> input, IEnumerable<byte> from, IEnumerable<byte> to)
    {
        var fromEnumerator = from.GetEnumerator();
        fromEnumerator.MoveNext();
        int match = 0;
        foreach (var data in input)
        {
            if (data == fromEnumerator.Current)
            {
                match++;
                if (fromEnumerator.MoveNext()) { continue; }
                foreach (byte d in to) { yield return d; }
                match = 0;
                fromEnumerator.Reset();
                fromEnumerator.MoveNext();
                continue;
            }
            if (0 != match)
            {
                foreach (byte d in from.Take(match)) { yield return d; }
                match = 0;
                fromEnumerator.Reset();
                fromEnumerator.MoveNext();
            }
            yield return data;
        }
        if (0 != match)
        {
            foreach (byte d in from.Take(match)) { yield return d; }
        }
    }
}

【讨论】:

    【解决方案4】:
        public static void BinaryReplace(string sourceFile, byte[] sourceSeq, string targetFile, byte[] targetSeq)
        {
            FileStream sourceStream = File.OpenRead(sourceFile);
            FileStream targetStream = File.Create(targetFile);
    
            try
            {
                int b;
                long foundSeqOffset = -1;
                int searchByteCursor = 0;
    
                while ((b=sourceStream.ReadByte()) != -1)
                {
                    if (sourceSeq[searchByteCursor] == b)
                    {
                        if (searchByteCursor == sourceSeq.Length - 1)
                        {
                            targetStream.Write(targetSeq, 0, targetSeq.Length);
                            searchByteCursor = 0;
                            foundSeqOffset = -1;
                        }
                        else 
                        {
                            if (searchByteCursor == 0)
                            {
                                foundSeqOffset = sourceStream.Position - 1;
                            }
    
                            ++searchByteCursor;
                        }
                    }
                    else
                    {
                        if (searchByteCursor == 0)
                        {
                            targetStream.WriteByte((byte) b);
                        }
                        else
                        {
                            targetStream.WriteByte(sourceSeq[0]);
                            sourceStream.Position = foundSeqOffset + 1;
                            searchByteCursor = 0;
                            foundSeqOffset = -1;
                        }
                    }
                }
            }
            finally
            {
                sourceStream.Dispose();
                targetStream.Dispose();
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-11
      • 2021-09-24
      • 1970-01-01
      • 1970-01-01
      • 2011-04-14
      • 2010-12-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多