Cómo insertar caracteres en un archivo usando C #

Tengo un archivo enorme, donde tengo que insertar ciertos caracteres en una ubicación específica. ¿Cuál es la manera más fácil de hacer eso en C # sin volver a escribir el archivo completo otra vez?

Los sistemas de archivos no admiten la “inserción” de datos en el medio de un archivo. Si realmente necesita un archivo que se pueda escribir de una manera ordenada, le sugiero que investigue el uso de una base de datos integrada.

Es posible que desee echar un vistazo a SQLite o BerkeleyDB .

Por otra parte, es posible que esté trabajando con un archivo de texto o un archivo binario heredado. En ese caso, su única opción es volver a escribir el archivo, al menos desde el punto de inserción hasta el final.

Me gustaría ver la clase FileStream para hacer E / S aleatorias en C #.

Probablemente necesite volver a escribir el archivo desde el punto donde inserte los cambios hasta el final. Es mejor escribir siempre al final del archivo y usar herramientas como sort y grep para obtener los datos en el orden deseado. Supongo que está hablando de un archivo de texto aquí, no de un archivo binario.

No hay forma de insertar caracteres en un archivo sin reescribirlos. Con C # se puede hacer con cualquier clase de Stream. Si los archivos son enormes, le recomendaría que use GNU Core Utils dentro del código C #. Ellos son los más rápidos. Solía ​​manejar archivos de texto muy grandes con los recursos básicos (de tamaños 4GB, 8GB o más, etc.). Los comandos como head, tail, split, csplit, cat, shuf, shred, uniq realmente ayudan mucho en la manipulación del texto.

Por ejemplo, si necesita colocar algunos caracteres en un archivo de 2GB, puede usar BYTECOUNT dividido, colocar el salto en un archivo, agregarle el texto nuevo, obtener el rest del contenido y agregarlo. Supuestamente, esto debería ser más rápido que cualquier otra forma.

Espero que funcione. Darle una oportunidad.

Puede usar el acceso aleatorio para escribir en ubicaciones específicas de un archivo, pero no podrá hacerlo en formato de texto, tendrá que trabajar con bytes directamente.

Puede echar un vistazo a este proyecto: Win Data Inspector

Básicamente, el código es el siguiente:

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

Dependiendo del scope de su proyecto, es posible que desee decidir insertar cada línea de texto con su archivo en una estructura de datos de tabla. Más o menos como una tabla de base de datos , de esa manera puede insertar en una ubicación específica en cualquier momento dado, y no tener que leer, modificar y generar el archivo de texto completo cada vez. Esto se debe al hecho de que sus datos son “enormes”, como usted dice. Todavía podría volver a crear el archivo, pero al menos crea una solución escalable de esta manera.

Puede ser “posible” dependiendo de cómo el sistema de archivos almacena archivos para insertar rápidamente (es decir, agregar) bytes adicionales en el medio. Si es remotamente posible, solo será factible hacerlo en bloque completo a la vez, y solo haciendo modificaciones de bajo nivel en el propio sistema de archivos o utilizando una interfaz específica del sistema de archivos.

Los sistemas de archivos generalmente no están diseñados para esta operación. Si necesita hacer inserciones rápidamente, realmente necesita una base de datos más general.

Dependiendo de su aplicación, un punto medio sería juntar sus insertos, de modo que solo haga una reescritura del archivo en lugar de veinte.

Si conoce la ubicación específica en la que desea escribir los datos nuevos, use la clase 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); } 

Siempre tendrá que volver a escribir los bytes restantes desde el punto de inserción. Si este punto está en 0, entonces reescribirá todo el archivo. Si son 10 bytes antes del último byte, entonces reescribirá los últimos 10 bytes.

En cualquier caso, no hay función para apoyar directamente “insertar en el archivo”. Pero el siguiente código puede hacerlo con precisión.

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

Para obtener un mejor rendimiento para el archivo IO, juegue con "magic two powered numbers" como en el código anterior. La creación del archivo utiliza un búfer de 262144 bytes (256 KB) que no ayuda en absoluto. El mismo búfer para la inserción hace el "trabajo de rendimiento" como puede ver en los resultados de Cronómetro si ejecuta el código. Un borrador de prueba en mi PC dio los siguientes resultados:

13628.8 ms para la creación y 3597.0971 ms para la inserción.

Tenga en cuenta que el byte objective para la inserción es 10, lo que significa que casi todo el archivo fue reescrito.

¿Por qué no pones un puntero al final del archivo (literalmente, cuatro bytes sobre el tamaño actual del archivo) y luego, al final del archivo escribes la longitud de los datos insertados, y finalmente los datos que deseas insertar? sí mismo. Por ejemplo, si tiene una cadena en el medio del archivo y desea insertar algunos caracteres en el medio de la cadena, puede escribir un puntero al final del archivo sobre unos cuatro caracteres en la cadena, y luego escribir esos cuatro caracteres hasta el final junto con los personajes que primero quisiste insertar. Se trata de ordenar datos. Por supuesto, puede hacer esto solo si está escribiendo todo el archivo usted mismo, quiero decir que no está usando otros códecs.