Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Dividir archivos en un TFileStream (https://www.clubdelphi.com/foros/showthread.php?t=90852)

Ramsay 19-09-2016 21:35:17

Dividir archivos en un TFileStream
 
Hola , tengo este codigo :

Código Delphi [-]
var
  InStream, OutStream: TFileStream;
....
OutStream := TFileStream.Create(OutFileName, fmCreate);
try
  InStream := TFileStream.Create(InFileName1, fmOpenRead);
  try
    OutStream.CopyFrom(InStream, InStream.Size);
  finally
    InStream.Free;
  end;
  InStream := TFileStream.Create(InFileName2, fmOpenRead);
  try
    OutStream.CopyFrom(InStream, InStream.Size);
  finally
    InStream.Free;
  end;
finally
  OutStream.Free;
end;

Sirve para unir dos archivos en uno solo , queria saber como podia dividir el archivo final con los dos archivos para tener acceso al segundo archivo usado en la union , ¿ alguien conoce como hacerlo ?

roman 20-09-2016 00:15:09

En un caso general, no queda una "marca" que divida los dos archivos. Sin embargo, si conoces el tamaño del primero, podrías usar el método Seek del Stream para colocar el puntero al principio de lo que sería el segundo archivo y a partir de ahí copiar a otro Stream.

LineComment Saludos

ecfisa 20-09-2016 01:06:53

Hola.

Tal como te comenta roman, si previamente no agregas una marca, vas a tener que enviar los nombres y tamaños de los archivos (bytes) a extraer, por ejemplo:
Código Delphi [-]
procedure ExtractFiles(const SourceName: string; const TargetNames: array of string;
  const SizeTargets: array of Integer);
var
  Source, Target: TStream;
  Buffer: array of Byte;
  i : Integer;
begin
  Source := TFileStream.Create(SourceName, fmOpenRead);
  try
    for i := Low(TargetNames) to High(TargetNames) do
    begin
      Target := TFileStream.Create(TargetNames[i], fmCreate);
      try
        SetLength(Buffer, SizeTargets[i]);
        Source.ReadBuffer(Pointer(Buffer)^, Length(Buffer));
        Target.WriteBuffer(Buffer[0], Length(Buffer));
      finally
        Target.Free;
      end;
    end;
  finally
    Source.Free;
  end;
end;

Llamada ejemplo:
Código Delphi [-]
 ExtractFiles('concatenacion.txt', ['a1.txt', 'a2.txt', 'a3.txt'], [204, 192, 327 {bytes}]);

Saludos :)

Ramsay 20-09-2016 01:32:23

hola , gracias por la ayuda a los dos , efcisa , una pregunta , el tamaño de los archivos en bytes seria lo mismo que "InStream.Size" , ¿ verdad ?

ecfisa 20-09-2016 02:21:24

Hola
Cita:

Empezado por Ramsay (Mensaje 508924)
.. una pregunta , el tamaño de los archivos en bytes seria lo mismo que "InStream.Size" , ¿ verdad ?

El tamaño es el que tiene cada archivo previo a la concatenación.

Deberías obtener y guardar esos valores de tamaño en algún sitio, por que desde el archivo compuesto, es imposible obtener los tamaños de los diferentes archivos agregados.

De otro modo, habría que poner marcas para poder identificar la finalización de cada archivo.

Saludos :)

Delphius 20-09-2016 04:58:24

Cita:

Empezado por ecfisa (Mensaje 508925)
Hola

El tamaño es el que tiene cada archivo previo a la concatenación.

Deberías obtener y guardar esos valores de tamaño en algún sitio, por que desde el archivo compuesto, es imposible obtener los tamaños de los diferentes archivos agregados.

De otro modo, habría que poner marcas para poder identificar la finalización de cada archivo.

Saludos :)

O bien armar una especie de Header a modo tabla que contenga la cantidad de items/archivos y sus respectivos tamaños. Luego es cosa de posicionarse en el lugar correcto y leer buffers de sus respectivos tamaños. Naturalmente este Header debe ser lo primero del archivo del archivo.

El header debe contener en este orden:
0.Cantidad de archivos
1. Tamaño del archivo #1
2. Tamaño del archivo #2
...
N. Tamaño del archivo #N

Luego viene la data del primer archivo, etc.

Lo que debe de cuidarse es de leer la cantidad justa de bytes. Por eso debe definirse adecuadamente que tipo de dato se va a usar para almacenar esta información... ¿Cuántos archivos se tiene pensado poder concatenar? Para 255 solo es suficiente con leer un byte... pero si se necesita de más se debe usar un SmallInt por lo menos.
Luego debe cuidarse también el tamaño de los archivos... Al menos las funciones que tiene Delphi y que actúan de indirección hacia la API de Windows, devuelven un Int64... ahí ya tienes unos cuantos bytes más por leer :D

Asi que de pronto, mínimo necesitas:
1. Leer el primer byte para saber la cantidad de archivos // si vas a permitir más de 255 necesitas 2 o más bytes... tu pones el límite!
2. Leer los siguientes 8 bytes para saber el tamaño del archivo
3. Ir a la posición correspondiente y leer el buffer de ese mismo tamaño
4. Repetir 2-4 según cantidad de archivos

Saludos,

roman 20-09-2016 15:52:56

Cita:

Empezado por Delphius (Mensaje 508931)
O bien armar una especie de Header a modo tabla que contenga la cantidad de items/archivos y sus respectivos tamaños. Luego es cosa de posicionarse en el lugar correcto y leer buffers de sus respectivos tamaños. Naturalmente este Header debe ser lo primero del archivo del archivo.

Exacto. Y tener así un archivo estructurado. El caso es que habría que saber qué es lo que se pretende al unir y desunir archivos y quizá entonces se pueda proponer una solución más ad hoc.

LineComment Saludos

Casimiro Notevi 20-09-2016 15:55:58

Así llegamos a la firma de Neftali: Si quieres obtener mejores respuestas, haz mejores preguntas.
O algo así :)

Ramsay 21-09-2016 15:37:05

Hola ecfisa gracias por la ayuda , tu codigo funciona perfecto para cuando junto un ejecutable con otro archivo y luego los desuno , se descomprime bien pero cuando ejecuto el ejecutable desunido se muestra :

Cita:

Windows no puede encontrar el archivo "la ruta..." , Asegurese que el nombre este escrito correctamente e intetelo de nuevo
o

Cita:

La version de este archivo no es compatible con la version de windows que esta ejecutando ... (y despues habla de 32 o 64 bits)
Eh comprobado los size de los archivos y estan perfectos , ¿ conoces alguna solucion para corregir este problema ?

Ramsay 22-09-2016 00:25:43

Estoy tratando de desunir con este codigo (intente con el tuyo pero intente solucionar el problema yo mismo y falle ...) :

Código Delphi [-]
var
  Source, Target: TStream;
  SourceName, TargetName: string;
  Size: Int64;
begin
  SourceName := 'salida.out';
  TargetName := 'programa.exe';

  Source := TFileStream.Create(SourceName, fmOpenRead);
  try
    Source.ReadBuffer(Size, SizeOf(Size));
    Source.Seek(2232832, 10751); // Bytes de los programas

    Source.ReadBuffer(Size, SizeOf(Size));

    Target := TFileStream.Create(TargetName, fmCreate);
    try
      Target.CopyFrom(Source, Size);
    finally
      Target.Free;
    end;
  finally
    Source.Free;
  end;
end;

En el codigo estoy tratando de guardar solo el segundo programa pero el ide me devuelve "stream read error" , en el codigo leo a partir del primer archivo para poder leer el segundo programa , ¿ como se soluciona ?

ecfisa 22-09-2016 01:11:24

Hola Ramsay
Cita:

Empezado por Ramsay (Mensaje 508984)
Hola ecfisa gracias por la ayuda , tu codigo funciona perfecto para cuando junto un ejecutable con otro archivo y luego los desuno , se descomprime bien pero cuando ejecuto el ejecutable desunido se muestra :
Cita:

Windows no puede encontrar el archivo "la ruta..." , Asegurese que el nombre este escrito correctamente e intetelo de nuevo
o
Cita:

La version de este archivo no es compatible con la version de windows que esta ejecutando ... (y despues habla de 32 o 64 bits)
Eh comprobado los size de los archivos y estan perfectos , ¿ conoces alguna solucion para corregir este problema ?

Acabo de hacer la prueba (bajo Windows 32 bits) uniendo y extrayendo un archivo ejecutable con uno de texto sin ningún problema usando este código:
Código Delphi [-]
procedure ConcatFiles(const SourceNames: array of string; const TargetName: string);
var
  Source, Target: TFileStream;
  i : Integer;
begin
  if Length(SourceNames) < 1 then
    raise Exception.Create('Se requieren dos archivos al menos');
  Target := TFileStream.Create(TargetName, fmCreate);
  try
    for i := Low(SourceNames) to High(SourceNames) do
    begin
      Source := TFileStream.Create(SourceNames[i], fmOpenRead);
      try
        Target.CopyFrom(Source, Source.Size);
      finally
        Source.Free;
      end;
    end;
  finally
    target.Free;
  end;
end;

procedure ExtractFiles(const SourceName: string; const TargetNames: array of string;
  const SizeTargets: array of Integer);
var
  Source, Target: TStream;
  Buffer: array of Byte;
  i : Integer;
begin
  Source := TFileStream.Create(SourceName, fmOpenRead);
  try
    for i := Low(TargetNames) to High(TargetNames) do
    begin
      Target := TFileStream.Create(TargetNames[i], fmCreate);
      try
        SetLength(Buffer, SizeTargets[i]);
        Source.ReadBuffer(Pointer(Buffer)^, Length(Buffer));
        Target.WriteBuffer(Buffer[0], Length(Buffer));
        Form1.Memo1.Lines.Add(PChar(Buffer));
      finally
        Target.Free;
      end;
    end;
  finally
    Source.Free;
  end;
end;
El ejecutable funciona sin problema luego de ser extraido y el archivo de texto mantiene su contenido original.

En cuanto a los errores que comentas, el primero da toda la impresión que está provocado por un error al escribir alguna ruta o nombre de archivo.

Si bien el segundo mensaje advierte sobre alguna incompatibilidad de S.O., es muy probable que se produzca por enviar a la rutina de extracción un tamaño de archivo erróneo.

Saludos :)

Delphius 22-09-2016 01:50:44

Al estar involucrados diferentes bitness debe cuidarse bien los tipos de enteros elejidos...
Pasar de 32bits a 64bits tiene sus cosas.
No es lo mismo hacer un SizeOf(mivariable) siendo mivariable un tipo entero NativeInt en 32bits que en 64bits. En 32 son 4 bytes, en 64bits es el doble.
No descartaría que sea un error de mal pasado los parámetros como sugiere Ecfisa, pero yo miraría más allá.

Y también hay que tener mucho cuidado con los archivos cuando se trabajan con equipos de diferentes endianness y se estará intercambiando entre ellos. Lo que se suele estilar es escribir en un endian prefijado y hacer la conversión de ser necesario. Lazarus está bien preparado en esto, ya tiene las funciones LEToN y BEToN para convertir Little Endian y Big Endian al tipo Nativo, como su contraparte NToLE y NtoBE. Delphi no las tiene... o al menos no se las encontré yo. :rolleyes:
Si bien es cierto que la enorme mayoría de las PC son LE, hoy de moda está usar los celulares y estos son ¡en su mayoría BE!

Saludos,


La franja horaria es GMT +2. Ahora son las 03:34:33.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi