PDA

Ver la Versión Completa : Lectura de fichero errónea


DarthGomis
04-02-2019, 20:23:23
Hola,

Estoy desarrollando una app que lea un fichero con la forma:

N:Nombre;D:0000;F:00/00/0000

Donde N: es un string, D: es un entero y F: es una fecha. (cabe destacar que el orden puede estar alterado y puede que alguna linea no esté bien escrita)

El problema reside en que el método en el cual almaceno los contenidos de la línea en una lista, al llegar al segundo ';' se revoluciona y hace cosas extrañas.

procedure TForm1.CreateList(fileName: string);
var
tempLine: string; //Line with all data
line: TLine; //Line with data fragmented
cha: char;
Data: TextFile;
tipe: string; //Tipe of data
temp: string; //temporally data to storage
nameData: string; //name data
numberData: integer; //number data
dateData: TDateTime;
lineData: integer; //date data

begin
fileList := TList.Create;
wrongLines := 0;
rightLines := 0;
lineData := 1;

AssignFile(Data, fileName);
Reset(Data);

while not Eof(Data) do
begin
while not Eoln(Data) do
begin
Read(Data, cha);
//Select tipe of data
if UpperCase(temp) = 'N:'
then
begin
tipe := temp;
temp := '';
end
else if UpperCase(temp) = 'D:'
then
begin
tipe := temp;
temp := '';
end
else if UpperCase(temp) = 'F:'
then
begin
tipe := temp;
temp := '';
end;
if (cha = ';') or (lineData = 3)
then
begin
//ShowMessage(tipe);
if tipe = 'N:'
then
begin
try
nameData := temp;
temp:='';
except
Inc(wrongLines);
temp:='';
break;
end;
end
else if tipe = 'D:'
then
begin
try
numberData := StrToInt(temp);
temp:='';
except
Inc(wrongLines);
temp:='';
break;
end;
end
else if tipe = 'F:'
then
begin
try
//ShowMessage(temp);
dateData := StrToDate(temp);
temp:='';
except
//ShowMessage('puto');
Inc(wrongLines);
temp:='';
break;
end;
end
else
ShowMessage(IntToStr(lineData));
Inc(lineData);
end
else temp := temp + cha;
ShowMessage('Tipo: ' + tipe + #13 + 'Temp: ' + temp + #13 + 'Caracter: ' + cha
+ #13 + 'N: ' + nameData + #13 + 'D: ' + IntToStr(numberData) + #13 +
'F: ' + DateToStr(dateData));
end;
if not (nameData = '')
then
begin
if not (numberData = 0)
then
begin
if not (dateData = 0)
then
begin
//ShowMessage(DateToStr(dateData));
line := TLine.Create(nameData, numberData, dateData);
Inc(rightLines);
fileList.Add(line);
end;
end;
end;
nameData := '';
numberData := 0;
dateData := 0;
temp := '';
lineData := 1;
ReadLn(Data,tempLine);
end;
CloseFile(Data);

end;

TList es una clase creada para que me diferencie qué es cada cosa

TLine = class

//Data fields of a line
private
n: string; //N:
d: integer; //D:
f: TDateTime; //F:

//Properties to read the data values
public

line : string;

property NewName: string
read n;
property NewNumber: integer
read d;
property NewDate: TDateTime
read f;

//Constructor
constructor Create(const n : string;
const d : integer;
const f : TDateTime);
end;


Sería de gran ayuda que me orientaseis un poco porque soy nuevo en delphi y no tengo mucha idea.

Neftali [Germán.Estévez]
05-02-2019, 10:30:06
Hola y bienvenido al club.
Es difícil porder ayudarte sólo con el código y sin ver una muestra de datos.

Una opción que tal vez nos ayude, es que coloques un fichero de ejemplo (si es posible) o al menos una porción que simule la parte del fichero que te da problemas. De esa forma se puede ejecutar el código con los datos "problemáticos" y darte una idea para solucionarlo.

Puedes comprimir el fichero y el proyecto si lo necesitas y subirlo al mensaje. Si no te deja por permisos contacta con algún moderador/adminsitrador por privado y te ayudarán a hacerlo.

bucanero
05-02-2019, 12:58:33
Hola a todos,

Para realizar la separación de los distintos campos por un separador determinado, como es este caso, a mi me gusta utilizar un TStringList por su sencillez y no necesitar utilizar otros componentes, y la forma de funcionamiento es simple:


var
sl: TstringList;
datos:string;
i: longint;
begin
// lista de campos en formato string, separados por un delimitador (;)
datos := 'N:Nombre;D:0000;F:00/00/0000';
try
sl := TStringList.Create;
sl.StrictDelimiter := True;
sl.Delimiter := ';'; // delimitador de campos

sl.DelimitedText := datos; // <-- aqui se pasan los datos
for i := 0 to sl.count - 1 do
MessageDlg(sl.strings[i], mtInformation, [mbOK], 0);
finally
// se libera el objeto
sl.Free;
end;


Y en tu caso en concreto prueba el siguiente código:

type
// estructura de datos que se va a recuperar
TDataRec = record
nameData: string; //name data
numberData: integer; //number data
dateData: TDateTime;

procedure clear; //inicializa los valores
function loadFromStrings(SL: TStrings): Boolean; //lee los valores desde una lista de strings
function IsValidData: Boolean; //comprueba si los valores son validos
end;

implementation

{ TDataRec }
procedure TDataRec.clear;
begin
// se inicializan los datos
nameData := '';
numberData := 0;
dateData := 0;
end;

function TDataRec.IsValidData: Boolean;
begin
// aqui se realizan las comprobaciones de los datos
Result := (nameData <> '') and
(numberData <> 0) and
(dateData <> 0);
end;

function TDataRec.loadFromStrings(SL: TStrings): Boolean;
var
tipe, DataStr: string; //Tipe of data
APosicion, i: integer;
begin
Result := False;
try
clear;
// se recorren el numero de campos leidos
for i := 0 to SL.Count - 1 do begin
APosicion := Pos(':', SL.strings[i]);
if APosicion > 0 then begin

tipe := UpperCase(Copy(SL.strings[i], 1, APosicion));
DataStr := Copy(SL.strings[i], APosicion + 1, Length(SL.strings[i]));

//Select tipe of data
if tipe = 'N:' then
nameData := DataStr
else if tipe = 'D:' then
numberData := StrToInt(DataStr)
else if tipe = 'F:' then
dateData := StrToDate(DataStr);
end;
end;
Result := IsValidData;
except
on E: Exception do
MessageDlg(E.message, mtError, [mbOK], 0);
end;
end;


y aquí el proceso del fichero:

procedure TForm2.Button1Click(Sender: TObject);
begin
with OpenDialog1 do
if execute then
try
memo1.lines.BeginUpdate;
ProcessFile(FileName);
finally
memo1.lines.EndUpdate;
end;
end;

function TForm2.ProcessData(const DataRec: TDataRec): boolean;
begin
Result := false;

/// Aqui procesas cada uno de los datos ya una vez leidos
with DataRec do begin
// yo solo muestro los datos en un memo, para verificar que son correctos
memo1.Lines.Add(Format('N:' + #9 + '%s ' + #9 + 'D:%d ' + #9 + 'F:' + #9 + '%s', [nameData, numberData, formatdatetime('dd/mm/yyyy', dateData)]));

// line := TLine.Create(nameData, numberData, dateData);
// fileList.Add(line);

end;
Result := true;
end;

procedure TForm2.ProcessFile(const AFileName: string);
var
TxtF: TextFile;
begin
try
AssignFile(TxtF, AFileName);
Reset(TxtF);
ParseFile(TxtF);
finally
CloseFile(TxtF);
end;
end;

procedure TForm2.ParseFile(var TxtF: TextFile);
var
tempLine: string;
sl: TstringLIst;
DataRec: TDataRec;
rightLines, wrongLines, lineData: integer; //date data
begin
wrongLines := 0;
rightLines := 0;
lineData := 0;
try
//se crea un StringList configurado para serparar los elementos por un delimitador
// en este caso el ";"
sl := TStringList.Create;
sl.StrictDelimiter := True;
sl.Delimiter := ';';

while not Eof(TxtF) do begin
Readln(TxtF, tempLine);
sl.DelimitedText := tempLine;
inc(lineData);
if DataRec.loadFromStrings(sl) and ProcessData(DataRec) then
Inc(rightLines)
else
Inc(wrongLines);
end;
finally
// se libera el objeto
sl.Free;
end;
end;


De esta forma aunque los campos te vengan en distinto orden los debes de poder leer sin problemas y en la validación de los datos ya puedes comprobar si te son útiles o no

Un saludo

DarthGomis
05-02-2019, 19:41:48
Muchisimas graciaas, me ha servido de mucha ayuda

Un saludo