¿Existe algún peligro al escribir bytes sin procesar en un archivo? [cerrado]

12

Estoy trabajando en un problema en Programming Pearls, específicamente, la implementación de un programa que clasifica un archivo que contiene, como máximo, 10,000,000 enteros (Columna 1, Problema 3). Dado que el libro no especifica cómo se deben almacenar los datos en el archivo, estoy considerando almacenar los enteros como bytes sin formato (existen otras restricciones que hacen que los bytes sin formato sean una buena opción). Nunca he trabajado en este nivel tan bajo antes, así que quiero saber si hay algo peligroso que deba tener en cuenta. ¿Debo preocuparme por utilizar accidentalmente algún tipo de secuencia de fin de archivo cuando escribo bytes sin formato en un archivo, por ejemplo?

Editar:

Ahora me doy cuenta de cuán amplia era mi pregunta. Realmente quise decir problemas del tipo más catastrófico, como sobrescribir accidentalmente otros archivos en el disco. Lo siento, no estaba más claro originalmente.

Drake Sobania
fuente
66
Tenga en cuenta que Programming Pearls es un libro muy antiguo; Podrías leer fácilmente los enteros enteros 10 ^ 7 en la memoria de una máquina de escritorio moderna, hacer el tipo y escribirlo de nuevo. Para obtener el punto original de ese capítulo, limite la cantidad que lee en cualquier momento a una fracción del número total. O bien, aumente el tamaño del archivo a alrededor de 10 ^ 10 enteros.
Caleb
3
En realidad, cuando escucho la palabra "peligroso", pienso en cosas que hacen explotar mi PC, borrar mis cuentas bancarias o algo así. Y supongo que probablemente sea seguro asumir que, siempre y cuando su programa no se use para controlar un Airbus o una planta de energía, no ocurrirá nada realmente "peligroso" cuando pruebe lo que tiene en mente.
Doc Brown
2
@delnan Hace años, cuando el mito del personaje EOF estaba de moda, recuerdo los sistemas de protección de copia que se basaban en 'copiar hasta el personaje EOF' que muchos programas de copia de la época hicieron. Algunos programas pondrían datos adicionales que verificarían después del marcador EOF de un archivo de texto asociado, pero antes del final asignado del archivo. El programa de copia no copiaría los datos adicionales validando una instalación limpia ... ahh ... nostalgia.
¿peligro? Como en "¿mi computadora explotará si hago esto"? No
Jwenting

Respuestas:

11

El único peligro con el que te encontrarás es pequeño contra gran endianess (si el byte más o menos significativo se escribe primero). Sin embargo, si permanece en el mismo entorno, no habrá problemas. Además de la garantía general de escritura / análisis de ida y vuelta.

El sistema de archivos está diseñado para manejar cualquier secuencia de bytes.

monstruo de trinquete
fuente
2
+1 para la última línea. No estoy seguro de que el problema grande / pequeño sea el único problema: el OP podría, por ejemplo, confundirse sobre dónde están los límites entre los enteros. Pero buena respuesta de todos modos.
Caleb
27

No, de hecho, así es como funcionan muchos formatos de archivo. Ejemplos comunes de archivos binarios como este incluyen imágenes y archivos de música / audio.

Para mantener la integridad del archivo y los datos leídos, asegúrese de seguir estas pautas:

  • Siempre abra el archivo (lectura o escritura) usando el mismo modo: texto o binario. La principal diferencia es que el modo de texto se preocupa por las nuevas líneas y puede "cortar" los caracteres de las nuevas líneas al leer un archivo (dependiendo de la biblioteca específica que se utilice). El modo de texto también puede realizar traducciones Unicode que probablemente ahogarán los datos que no sean Unicode.
  • Al leer datos que no son cadenas, asegúrese de leer con el mismo tipo de datos que escribe. Por ejemplo, si los primeros cuatro bytes del archivo son un número entero descriptivo, asegúrese de leer y escribir utilizando un método que tome / proporcione un número entero para garantizar que se trate de manera coherente. El mismo tipo de datos puede tener un tamaño diferente en diferentes máquinas, y mezclar tipos de datos en la misma máquina también puede cambiar el significado de los datos (por ejemplo, interpretar un bit en medio de un entero más largo como un bit de signo).
  • Endianness: si la biblioteca que está utilizando no maneja esto de manera consistente, es posible que deba manejarlo usted mismo. Por ejemplo, Java siempre usa el orden de bytes de la red (big endian) para los tipos de varios bytes. C y C ++ usan lo que decida el implementador de la biblioteca, generalmente el mismo que el procesador (little endian en Intel, big endian en la mayoría de los demás). Si este es un ejercicio rápido en un sistema, no es tan importante, pero sigue siendo un buen hábito prestar atención a esto y codificarlo si es necesario.

Los detalles específicos variarán según el marco, la plataforma y el idioma, pero esto debería cubrir las "trampas" básicas con el archivo de E / S.


fuente
3
Un punto adicional para datos que no son cadenas: asegúrese de utilizar un número constante de bytes para cada tipo. En C y C ++, un intpuede estar entre 2 y 8 o más bytes (octetos realmente).
Bart van Ingen Schenau
Eso está implícitamente incluido con mi segundo punto, por ejemplo, entero de 32 v. 64 bit. Serían diferentes tipos de datos.
Es posible que desee hacerlo explícito. No es obvio que inten dos máquinas diferentes se puedan considerar diferentes tipos de datos.
Bart van Ingen Schenau
9

Además de todos los problemas ya mencionados, si está creando un nuevo formato de archivo binario en lugar de leer y escribir datos en un formato existente, es absolutamente vital que incluya un encabezado de archivo : un bloque de datos al principio del archivo que identifica inequívocamente el formato del archivo y registra los metadatos que puedan ser necesarios.

Los buenos encabezados de archivo incluyen al menos tres cosas:

  • Un " número mágico ", de al menos cuatro bytes. El número mágico DEBE rfc2119 ser los primeros N bytes en el archivo, NO DEBE haber sido utilizado para ningún otro formato de archivo que pueda desenterrar, y DEBE contener al menos un byte que no sea un carácter ASCII imprimible. Consulte la especificación PNG para saber cómo diseñar un número mágico realmente completo . Vea el código fuente del file(1)comando para obtener una base de datos de números mágicos existentes que es tan completa como es probable que encuentre.

    El objetivo de un número mágico es etiquetar sin ambigüedad el archivo, en banda, con su formato. Si no incluye un número mágico, o no es lo primero en el archivo, corre el riesgo de que los programas identifiquen erróneamente su archivo como algún otro tipo de archivo, lo que conduce a la pérdida de datos, la detección de virus que escapan y otros catástrofes

  • Una indicación de la versión del formato de archivo. Incluso si cree que nunca tendrá que revisar su formato de archivo drásticamente, haga los siguientes dos bytes después del número mágico 00 00y documente que se trata de un número de versión de 16 bits con una definición definitiva (lo que quiera, pero elija uno y manténgalo en todo el archivo ) y se incrementará si el significado de los datos posteriores cambia radicalmente. Tu futuro yo te lo agradecerá.

    (La especificación PNG toma una ruta diferente aquí, especificando que los formatos de fragmentos están congelados, y que todos los cambios futuros al formato tomarán la forma de nuevos tipos de fragmentos. Eso también es válido, pero recomiendo el enfoque de número mágico simple + número de versión para principiantes en el procesamiento de datos binarios. Las personas que diseñaron PNG se basaban en décadas colectivas de experiencia con formatos de imagen).

  • Algún tipo de mecanismo para incrustar metadatos arbitrarios en el archivo. Esto puede ser tan simple como hacer que los siguientes dos bytes sean un desplazamiento de 16 bits desde el final del encabezado hasta el comienzo de los datos reales, con todo lo que debe interpretarse como pares clave-valor UTF-8 a la RFC 822 (es decir, " Tag: value\n" - si sigue esta ruta, le recomiendo no permitir el plegado de líneas largas). Nuevamente, PNG es considerablemente más inteligente.

zwol
fuente
No es necesario crear su propio formato de archivo ... simplemente almacene los datos como una imagen. Es posible que deba cambiar la dimensionalidad (por ejemplo, 10k x 1k) para que sea compatible. O podrías usar FITS . Si sus datos son más complejos que una simple matriz, puede usar HDF , CDF o NetCDF .
Joe
Sugeriría que sea simple. Bastarán 256 versiones diferentes y, de lo contrario, se pueden diseñar versiones adicionales como subversiones de la versión 255. De manera similar para los metadatos, es suficiente agregarlos en la versión cuando realmente se necesitan. @Joe Image ??? ¡Estás evitando la posible confusión de formatos al confundir a todos de antemano!
maaartinus
@maaartinus Hacer que el campo de versión sea de dos bytes obliga al diseñador de formato a comprometerse con un endianness por adelantado. El espacio para metadatos siempre debe estar en la versión 0 de un formato binario, de lo contrario, terminará con horribles errores como ID3. Siento una gran simpatía por la lógica de las especificaciones de PNG con respecto a la extensibilidad a través de nuevos tipos de fragmentos en lugar de golpes de versión de formato. Sin embargo, los archivos estructurados en fragmentos aportan una gran complejidad propia, así que dudo en recomendarlos para casos simples. Yo estaba tentado a recomendar HDF como un formato genérico que de tratarse con una gran cantidad de estas cuestiones ya.
zwol
2

Diferentes arquitecturas tienen diferentes representaciones para enteros. El riesgo principal aquí es guardar la representación de bytes de un número entero en la máquina A y luego intentar leerlo e interpretar los contenidos como números enteros en la máquina B. Si las máquinas A y B tienen tamaños diferentes para números enteros y / o diferente endianness , usted ' Lo más probable es que provoque un comportamiento indefinido (por ejemplo, en C) o una excepción.

Dado que este es solo un ejemplo de programación y no un programa "real", en realidad no es un problema. Si este fuera un programa real, rodar su propio formato binario específico de la aplicación generalmente no es una buena idea; existen mejores soluciones, como SQLite o formatos de serialización basados ​​en cadenas como JSON, YAML, XML, etc. Para valores únicos sería suficiente convertirlo en una cadena; para listas simples, puede guardar una cadena por línea y simplemente dividir la entrada en las nuevas líneas cuando la vuelva a leer.

Doval
fuente
De acuerdo en general, pero JSON o XML aumentarían significativamente el tamaño de un archivo que contiene 10 ^ 7 números. Además, generalmente se leen y analizan de una vez, pero el capítulo en cuestión trata de ordenar archivos que contienen más datos de los que puede caber en la memoria disponible.
Caleb
Depende de lo que estés haciendo. A veces, el impacto de rendimiento de SQL frente a un roll-your-own es importante. La última vez que lo hice, tenía pequeños registros y había muchas posibilidades de que quisiera vecinos. Leer un bloque más grande del disco generalmente no costaría casi nada, así que si quisiera un registro, leería 1000 en un caché. Es casi seguro que mis registros estaban uno al lado del otro, con SQL, la cabeza del disco rebotaba por todas partes.
Loren Pechtel