PDA

Ver la Versión Completa : Partir Archivos


jorge_mosquera
15-02-2007, 01:18:18
Delphi 7 Enterprise

Tengo un archivo plano bastante grande, y necesito partirlo en trozos mas pequeños para hacerlo mas manejable, y poder procesarlos de manera individual.

Intente con un procedimiento que utiliza BLOCKREAD y BLOCKWRITE, pero tengo un problema.

Si el registro es este

1,EPS028,CC,38471049,SALAZAR,CARDONA
2,EPS028,CC,29897965,MARTINEZ,DE
3,EPS028,RC,31600888,GARCIA,OSPINA
4,EPS028,CC,9990036,HERRERA,ROBLEDO

Si por ejemplo quiero partirlo en nuevos archivos de 2 registros cada uno, obtengo

1,EPS028,CC,38471049,SALAZAR,CARDONA
2,EPS028,CC,29897

Es decir, algunos registros quedan incompletos.

El archivo pude traer cualquier cantidad de campos (separados por comas), y generalmente son muy grandes (> 50 MB).

Como puedo hacer para partir una cantidad especifica de registros, pero con la tranquilidad de que estos queden completos?

Muchas gracias

seoane
15-02-2007, 01:44:00
Así de pronto, se me ocurre algo como esto:

EDITO: El código corregido se encuentra mas abajo

egostar
15-02-2007, 03:16:33
seoane, probe el código y si, me crea los dos archivos, solo que en el primero me hace una copia del original y el segundo me deja un archivo vacio.

Salvo tu mejor opinion, lo que yo haría es hacer un barrido del archivo para saber cuantas lineas tiene (quiero pensar que es una archivo con LFyCR).

Después sí, crear los archivos (2,3,4, los que se deseen) pero partiendolos por lineas y no por bytes.

Saludos

seoane
15-02-2007, 04:11:22
Efectivamente egostar, parece que la instrucción FileSize no funciona muy bien con los archivos de texto. Pero todo tiene solución:


var
Str: String;
Source, Dest: TextFile;
Size: Integer;
F: File;
begin
Size:= 0;
// Abrimos el archivo sin tipo para averiguar su tamaño en bytes
AssignFile(F,'C:\Origen.txt');
{$I-}
Reset(F,1);
{$I+}
if IOResult = 0 then
begin
Size:= FileSize(F) div 2;
CloseFile(F);
end;
// Ahora abrimos el archivo como texto
AssignFile(Source,'C:\Origen.txt');
{$I-}
Reset(Source);
{$I+}
if IOResult = 0 then
begin
// Creamos el primer trozo
AssignFile(Dest,'C:\Trozo1.txt');
{$I-}
Rewrite(Dest);
{$I+}
if IOResult = 0 then
begin
while (not Eof(Source)) and (Size > 0) do
begin
Readln(Source,Str);
Writeln(Dest,Str);
dec(Size,Length(Str)+2);
end;
CloseFile(Dest);
end;
// Creamos el segundo trozo
AssignFile(Dest,'C:\Trozo2.txt');
{$I-}
Rewrite(Dest);
{$I+}
if IOResult = 0 then
begin
while not Eof(Source) do
begin
Readln(Source,Str);
Writeln(Dest,Str);
end;
CloseFile(Dest);
end;
CloseFile(Source);
end
end;

egostar
15-02-2007, 04:19:21
Hombre, pues quedo de 10.

Hice dos pruebas.

1. Con lineas pares, es decir, 12 lineas

Creo los dos archivos con 6 lineas cada uno, perfecto.

2. Con lineas impares, 11 lineas

Debo reconocer que pense mal, pense que en lineas impares habria un bug y partiria una linea a la mitad, pero no, sorpresa, deja 6 lineas en uno y 5 lineas en el segundo.

Saludos y mis respetos

seoane
15-02-2007, 04:33:27
Y para dar mas alternativas. Probemos con TStringList:

Partiendo el archivo por numero de lineas:

var
Origen, Destino: TStringList;
i: integer;
begin
Origen:= TStringList.Create;
try
Destino:= TStringList.Create;
try
Origen.LoadFromFile('C:\Origen.txt');
for i:= 0 to Origen.Count div 2 do
Destino.Add(Origen[i]);
Destino.SaveToFile('C:\Trozo1.txt');
Destino.Clear;
for i:= (Origen.Count div 2) + 1 to Origen.Count - 1 do
Destino.Add(Origen[i]);
Destino.SaveToFile('C:\Trozo2.txt');
finally
Destino.Free;
end;
finally
Origen.Free;
end;
end;


y por tamaño en bytes:

var
Origen, Destino: TStringList;
i, Size: integer;
begin
Origen:= TStringList.Create;
try
Destino:= TStringList.Create;
try
Origen.LoadFromFile('C:\Origen.txt');
Size:= Length(Origen.Text) div 2;
i:= 0;
while (i < Origen.Count) and (Size > 0) do
begin
Destino.Add(Origen[i]);
dec(Size,Length(Origen[i])+2);
inc(i);
end;
Destino.SaveToFile('C:\Trozo1.txt');
Destino.Clear;
while i < Origen.Count do
begin
Destino.Add(Origen[i]);
inc(i);
end;
Destino.SaveToFile('C:\Trozo2.txt');
finally
Destino.Free;
end;
finally
Origen.Free;
end;
end;


Las ventaja de usar TStringList es que el se encarga de corregir los posibles problemas con los retornos de carro. La desventaja es el intenso trabajo sobre la memoria.

jorge_mosquera
15-02-2007, 14:01:24
Amigos, muchas gracias por las ideas que me han dado.
Le verdad SI he partido los archivos utilizando TStringList, y recorriendo el archivo original, y me ha funcionado muy bien, solo que es algo lento.

Definitivamente a nadie se le ocurre como utilizar blockread y blockwrite para este cometido?

gracias :o

basti
15-02-2007, 14:39:06
Trabajar con archivos de texto y blockread, es algo complicado, se me ocurre algo así, pero no sé si el rendimiento será superior al modelo con StringList:


const
MAX_REC_READ = 32000; // por ejemplo


// copia aproximadamente los bytes de MAX_REC_READ
// devuelve si no ha podido copiar todos
function CopyFile(var forg, fdest : file) : boolean;
var
bytesLeidos : integer;
buffer: array[1..MAX_REC_READ] of byte;
bEOL : byte;

begin
Result := true;
BlockRead(forg, buffer, MAX_REC_READ, bytesLeidos);
BlockWrite(fdest, buffer, bytesLeidos);
if bytesLeidos = MAX_REC_READ then // buscamos hasta el final del registro
begin
repeat
BlockRead(forg, bEOL, 1, bytesLeidos);
BlockWrite(fdest, bEOL, bytesLeidos);
until eof(forg) or (bEOL = 10) // cambiar por 13 si el final de línea es sólo con #13;

end
else Result := false;
end;

// programa principal
var
forg, fdest : File;
i : integer;
begin
AssignFile(forg, 'origen.txt');
Reset(forg, 1);
i := 0;
AssignFile(fdest, 'destino' + IntToStr(i) + '.txt');
rewrite(fdest, 1);
while CopyFile(forg, fdest) do
begin
CloseFile(fdest);
inc(i);
AssignFile(fdest, 'destino' + IntToStr(i) + '.txt');
rewrite(fdest, 1);
end;
CloseFile(fdest);
CloseFile(forg);
end.

seoane
15-02-2007, 14:59:08
Le verdad SI he partido los archivos utilizando TStringList, y recorriendo el archivo original, y me ha funcionado muy bien, solo que es algo lento.

No hubiera estado demás que mencionaras lo que habías intentado tu, así no hubiéramos perdido el tiempo. Incluso, tampoco hubiera sobrado un poco del código que utilizas.

De todas formas:

function Min(i,j: Integer): Integer;
begin
if i < j then
Result:= i
else
Result:= j;
end;

procedure Partir(Filename: string);
var
Source, Dest: File;
Size, Leidos, Escritos: Integer;
Buffer: PChar;
begin
AssignFile(Source,Filename);
{$I-}
Reset(Source,1);
{$I+}
if IOResult = 0 then
try
Size:= FileSize(Source) div 2;
// Creamos un buffer de 1Mb
GetMem(Buffer,1024 * 1024);
try
// Creamos el primer trozo
AssignFile(Dest,Filename + '.001');
{$I-}
Rewrite(Dest,1);
{$I+}
if IOResult = 0 then
try
while (not Eof(Source)) and (Size > 0) do
begin
BlockRead(Source,Buffer^,Min(1024 * 1024,Size),Leidos);
BlockWrite(Dest,Buffer^,Leidos,Escritos);
dec(Size,Escritos);
end;
// Ajustamos el final de linea
Buffer[0]:= #0;
while (not Eof(Source)) and (Buffer[0] <> #10) do
begin
Buffer[0]:= #0;
BlockRead(Source,Buffer^,1,Leidos);
BlockWrite(Dest,Buffer^,Leidos,Escritos);
end;
finally
CloseFile(Dest);
end;
// Creamos el segundo trozo
AssignFile(Dest,Filename + '.002');
{$I-}
Rewrite(Dest,1);
{$I+}
if IOResult = 0 then
try
while not Eof(Source) do
begin
BlockRead(Source,Buffer^,1024 * 1024,Leidos);
BlockWrite(Dest,Buffer^,Leidos,Escritos);
end;
finally
CloseFile(Dest);
end;
finally
FreeMem(Buffer);
end;
finally
CloseFile(Source);
end;
end;

// Por ejemplo
Partir('C:\Origen.txt');

egostar
16-02-2007, 06:14:30
Definitivamente a nadie se le ocurre como utilizar blockread y blockwrite para este cometido?

No hubiera estado demás que mencionaras lo que habías intentado tu, así no hubiéramos perdido el tiempo. Incluso, tampoco hubiera sobrado un poco del código que utilizas.

De todas formas:

Pues ahi esta, seoane, que puedo decir, te las sabes de todas todas.

Me pregunto porque forzosamente con el Block Read/Write:confused:

Saludos.

jorge_mosquera
16-02-2007, 14:31:43
Muchas gracias amigos por darme una luz en la solucion del problema. Lamento no haberme expresado bien desde el principio y no comentarles todo lo que habia intentado previamente.

¿Porque BlockRead y BlockWrite ? Porque por lo que he visto, y leido, es super eficiente, y eso es lo que busco con mis programas.

Reciban un fuerte abrazo desde mi hermosa tierra colombiana.

Gracias