2011-06-29 11 views
11

¿Cuál es el mejor método para reemplazar la secuencia de bytes en el archivo binario con la misma longitud que otros bytes? Los archivos binarios serán bastante grandes, aproximadamente 50 mb y no se deben cargar a la vez en la memoria.Reemplazar la secuencia de bytes en el archivo binario

Actualización: No sé la ubicación de los bytes que debe reemplazarse, necesito encontrarlos primero.

+0

archivo abierto en modo escritura, mueva el puntero a la ubicación de las antiguas bytes, escribir nuevos bytes. –

+0

¿Cómo se sabe la posición exacta de los bytes para modificar? ¿Es una corrección de corrección? –

Respuesta

14

Suponiendo que está intentando reemplazar un sección conocida del archivo.

  • Abrir una FileStream con acceso de lectura/escritura
  • Se desplaza hasta el lugar correcto
  • los datos existentes de sobrescritura

Código de la muestra viene ...

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); 
    } 
} 

Si' Estamos tratando de hacer una versión binaria de un string.Replace (por ejemplo, "siempre reemplace los bytes {51, 20, 34 } con {20, 35, 15}, entonces es bastante más difícil. Como una breve descripción de lo que haría:

  • asignar un búfer de al menos el tamaño de los datos que le interesa
  • repetidamente lee en la memoria intermedia, la exploración de los datos
  • Si encontrar una coincidencia, buscará volver al lugar correcto (por ejemplo stream.Position -= buffer.Length - indexWithinBuffer; y sobrescribir los datos

sonidos simples hasta ahora ... pero el truco es si los datos comienza cerca del final del búfer. Necesitas recordar todo potenciales coincidencias y cuán lejos ha coincidido hasta el momento, de modo que si obtiene una coincidencia al leer siguiente buffer's-worth, puede detectarlo.

Probablemente hay formas de evitar este astucia, pero no me gustaría tratar de llegar a la ligera con ellos :)

EDIT: Bueno, tengo una idea que podría ayudar ...

  • Mantener un tampón que es al menos dos veces tan grande como sea necesario
  • repetidamente:
    • Copiar el segundo la mitad de su cuestión en la primera mitad
    • Llenar la segunda mitad del buffer del archivo
    • búsqueda en la memoria intermedia todade los datos que está buscando

De esa manera, en algún momento, si los datos son presente, estará completamente dentro del buffer.

Tendría que tener cuidado acerca de dónde estaba la corriente para volver al lugar correcto, pero creo que debería funcionar.Sería más difícil si estuviera tratando de encontrar todos los partidos, pero al menos el primer partido debe ser razonablemente sencilla ...

+0

Olvidé indicar en mi pregunta original lo que no sé sobre la posición de reemplazar bytes. – Tomas

+0

@Tomas: ¿Así que mi última oración es una descripción razonable de lo que buscas? –

+0

@Jon, lo siento, me olvidé de indicar eso. Deberíamos encontrar la primera secuencia de bytes en los archivos y luego reemplazarlos. Sé que es difícil de hacer y he buscado en Google sin suerte. Así que publiqué la pregunta aquí. – Tomas

5

Mi solución:

/// <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); 
    } 
+1

No funciona realmente. –

+0

Funcionó bien para mí. – quilkin

3

Puedes usar mi BinaryUtility a buscar y reemplazar uno o más bytes sin cargar el archivo en la memoria de esta manera:

var searchAndReplace = new List<Tuple<byte[], byte[]>>() 
{ 
    Tuple.Create(
     BitConverter.GetBytes((UInt32)0xDEADBEEF), 
     BitConverter.GetBytes((UInt32)0x)), 
    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 c oda:

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; } 
     } 
    } 
} 
0
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(); 
     } 
    } 
Cuestiones relacionadas