PDA

Ver la Versión Completa : Cual la capacidad de un TStringList?


halcon_rojo
12-04-2006, 18:45:45
Hola a todos mi duda es si el TStringList tiene alguna limitacion en cuanto al tamaño (o numero de lineas) del archivo con el que trabaja. Mi problema es que tengo una rutina que carga datos desde un archivo de texto a una tabla DBF y hasta ahora el archivo de texto no pasaba los 1.3 MB (como 29mil registros que tarda mas de 10 min) asi que mi rutina funciona bien, pero se presento que el archivo que necesito cargar es de aprox. 6MB con algo mas de 80mil registros. Mi rutina a parte de que tarda un monton, se corta a medias y no se por que.
este es mi codigo (algo resumido):
begin
Archivo:= TStringList.Create;
Linea:= TStringList.Create;
linea.Te
try
Linea.Delimiter:= chr(124); // Caracter pipe
Linea.QuoteChar:= ' ';
Archivo.LoadFromFile(filetxt);
progressbar1.Min := 0;
progressbar1.Max := Archivo.Count - 2;
for i:= 0 to Archivo.Count - 1 do
begin
// uso lo siguiente para capturar sin problemas datos con espacios en blanco
Linea.DelimitedText:= StringReplace(Archivo[i],chr(32),'(_)',[rfReplaceAll]);
with datamodule1.datos do
begin
compras.Append;
compras.edit;
comprasNIT.AsString := Linea[1];
comprasRSOCIAL.AsString := StringReplace(Linea[2],'(_)',chr(32),[rfReplaceAll]);
comprasNFACTURA.AsString := Linea[3];
// etc....
compras.Post;
end; // with
if i <= progressbar1.Max then progressbar1.Position := i;
Application.ProcessMessages;
end; //for
finally
Archivo.Free;
Linea.Free;
end;
se puede hacer esto de otra manera cosa que acepte archivos de texto bastante grandes y que el proceso no tarde mucho?

halcon_rojo
12-04-2006, 21:39:07
>Hello,
>>Someone knows the exact limits of TStringList.LoadFromFile ?
>>- file size ?
>- max lines ?
>- ctrl_Z in file ?
>>(I know it stops on nulls).

I don't think there should be specific filesize or maxline limits other
than those that are imposed by the
process. LoadFromFile calls LoadFromStream which calls Read which calls
HandleStream.Read which
calls FileRead which returns a pointer to a buffer and a count of the bytes
read ( a DWORD so there is a 2gb or is it 4gb limit there).

the Tstrings.LoadFromStream
looks like
Size := Stream.Size - Stream.Position;
SetString(S, nil, Size);
Stream.Read(Pointer(S)^, Size);
SetTextStr(S);
so it is clear that there is a 1 hit read of all the data on that file
until the end of file (which might not be what
you want if you have other stuff on the file). Anyway, all the data goes
into an ansistring S (which is local and will be deallocated on exit) but
afaik the memory for the underlying buffer is never freed until the
handle is finally freed .. this seems wasteful (as in, as soon as we have
finished the load we could release
the file buffer .. but I may have misread the code).

Next we have SetTextStr which is a loop that looks like so

P := Pointer(Value);
if P <> nil then
while P^ <> #0 do
begin
Start := P;
while not (P^ in [#0, #10, #13]) do Inc(P);
SetString(S, Start, P - Start);
Add(S);
if P^ = #13 then Inc(P);
if P^ = #10 then Inc(P);
end;

(I am sure Borland will forgive me for publishing this code snippet, in the
interests of science).

So, there are some possibilities of some oddities here particularly wrt
#0,#10,#13.
The #10 and #13 were put there by GetTextStr .. but you might have had
them embedded in
your original source strings. The #0 .. again, you may have had a reason
for having one or more
consecutive empty strings in your original list.

Note also the call to Add .. depending upon your settings of Sorted and, if
sorted, Duplicates this can end up
with another call to the bsearch find and Insert (even if you KNOW you are
restoring a sorted list), and some locale dependencies.


My point is that TstringList.SaveToFile and LoadFromFile are not the simple
mirror image dump and
restore of blocks of characters that one might expect. There is quite a
lot of looping and comparison and memory management going on under the
hood, so if your strings have unusual content or you need to do a lot of
this ,you might be better off considering alternative structures.

fwiw

alapaco
12-04-2006, 21:45:42
Acabo de probar de cargar un archivo de 10Mb en un TStringList y no tuve ningún problema.

EDIT: Vos lo explicaste mejor. :p

halcon_rojo
12-04-2006, 21:58:14
gracias Alvaro por tu interes, tal vez se deba a algun 'error' en el archivo de texto, de todos modos mi pregunta es si cargaste el archivo de 10Mb en el TStringList y lo recorriste? o solo lo cargaste?
acerca de mi otra duda de por que me demora tanto la carga y al tener en mi codigo una llamada repetitiva del ProcessMessages encontre este comentario:

In your loop you need to add a call to the Application.ProcessMessages method. This will allow your application to process Windows messages, including those generated by user actions. There are two significant caveats. First, since Windows messages often translate into calls to event handlers your program may begin to do things at inappropriate times. Make sure that the user can't initiate actions that will interfere with the loop while the loop is active. In particular, note the following sentence, taken from Delphi 3's help file on TApplication.Terminated: "For applications using calculation-intensive loops, call Application.ProcessMessages periodically, and also check Application.Terminated to determine whether or not to abort the calculation so that the application can terminate." The second caveat is that calling Application.ProcessMessages can be relatively expensive and may slow the program. In a fast (tight) loop you may not want to call the method on each iteration. If you only want to update the display and not handle user input you can use the Update method (Delphi 3 and up) of the control covering the part of the display you want to update. Remember that this will also slow down the loop!

alapaco
12-04-2006, 22:25:49
Exacto, eso que remarcaste en rojo es correcto, tal vez sería mejor llamar a Application.ProcessMessages cada X cantidad de registros procesados, no cada 1.

JCarlosas
16-05-2006, 01:45:27
Hola.
Me estuve leyendo el fragmento que esta en ingles pues casualmente soy uno de los que tiene el problema con el caracter #0.
Me estaba ocurriendo que cuando leia el archivo con
LoadFromFile
y dicho archivo contenia en algun lugar el caracter #0 se me cortaba ahi el archivo no seguia cargando.
Quizas la solucion del problema le sea util a alguien y aqui se las dejo.
En el fuente solamente lo que hice fue cambiar el caracter #0 que venia por un caracter A, quizas en otras aplicaciones se deba hacer otra cosa pero creo que el ejemplo vale.



unit UCustomTStrings;
{$R-,T-,X+,H+,B-}
{$IFDEF MSWINDOWS}
{ ACTIVEX.HPP is not required by CLASSES.HPP }
(*$NOINCLUDE ActiveX*)
{$ENDIF}
{$IFDEF LINUX}
{$DEFINE _WIN32}
{$ENDIF}
{$IFDEF MSWINDOWS}
{$DEFINE _WIN32}
{$ENDIF}

interface
{$IFDEF MSWINDOWS}
uses Windows, Messages, SysUtils, Variants, TypInfo, ActiveX, classes;
{$ENDIF}
{$IFDEF LINUX}
uses Libc, SysUtils, Variants, TypInfo, Types, classes;
{$ENDIF}
type
TCustomTStrings = class(TStringList)
Public
procedure LoadFromStream(Stream: TStream);Virtual;
procedure LoadFromFile(const FileName: string);Virtual;
end;
implementation
uses strutils;
procedure TCustomTStrings.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;

procedure TCustomTStrings.LoadFromStream(Stream: TStream);
var
Size: Integer;
S : string;
i: integer;
StringSize : integer;
begin
BeginUpdate;
try
Size := Stream.Size - Stream.Position;
SetString(S, nil, Size);
Stream.Read(Pointer(S)^, Size);
StringSize := Length(S);

//Este for es solamente para cambiar el caracter #0 por otro.
for i:= 1 to StringSize do
Begin
if S[i] = #0 then S[i] := 'A'; //Sustituyendo el #0 por una "A"
end;


SetTextStr(S);
finally
EndUpdate;
end;
end;
end.


Ah y la llamada y creacion del nuevo componente muy facil.


var
tsArchivo : TCustomTStrings;
Begin
tsArchivo := TCustomTStrings.create;
tsArchivo.LoadFromFile(LoteFileName);


Otra variante pudo ser redefinir el metodo
SetTextStr
en lugar de loadfromstream

Por cierto he cargado archivos de mas de 100MBytes con ese codigo

Espero que sea de utilidad a alguien.
Saludos
Juan Carlos