简而言之,我是 delphi 新手,我想实现以下目标:
- 我将表定义为 .cds 文件 {index, data, date},以及一些 .csv 格式的数据。
- 我想将 .csv 文件加载到表中并显示日志它的更改和错误(例如:无效的日期格式)。
问题
如何优雅地解决这个任务?
您可以从 .csv 中逐行读取,将每一行设置为 StringList 的“DelimitedText”,将记录附加到数据集,循环字符串列表以设置每个字段的值,然后发布到数据集。
您可以将 'field value assinging'/'posting' 放在 try-except 块中,并记录引发异常的任何错误消息以及您喜欢的信息(例如格式错误的字段值/名称、行号和/或整行等。 ) 到文件 fi
(我不明白您所说的“更改”是什么意思,据我了解,来自 .csv 的行将被插入到数据集中,因此所有更改都将被插入。)
编辑:为了能够讨论具体的事情(我很难掌握任务:))
示例数据(CodeGear 示例“Clients.cds”的一部分):
Davis;Jennifer;1023495,0000;100 Cranberry St.;Wellesley;MA;02181;516-292-3945;01.01.93 Jones;Arthur;2094056,0000;10 Hunnewell St;Los Altos;CA;94024;415-941 -4321;07.02.81 Parker;Debra;1209395,0000;74 South St;Atherton;CA;98765;916-213-2234;23.10.90 Sawyer;Dave;3094095,0000;101 Oakland St;Los Altos;CA; 94022;415-948-9998;21.12.89 White;Cindy;1024034,0000;1 Wentworth Dr;Los Altos;CA;94022;415-948-6547;01.10.92
procedure TForm1.FormCreate(Sender: TObject);
begin
CDS.FieldDefs.Add('LAST_NAME', ftString, 20);
CDS.FieldDefs.Add('FIRST_NAME', ftString, 20);
CDS.FieldDefs.Add('ACCT_NBR', ftInteger);
CDS.FieldDefs.Add('ADDRESS_1', ftString, 30);
CDS.FieldDefs.Add('CITY', ftString, 15);
CDS.FieldDefs.Add('STATE', ftString, 2);
CDS.FieldDefs.Add('ZIP', ftString, 5);
CDS.FieldDefs.Add('TELEPHONE', ftString, 12);
CDS.FieldDefs.Add('DATE_OPEN', ftDate);
CDS.CreateDataSet;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
csv: TextFile;
Rec: string;
Fields: TStringList;
LineNo: Integer;
i: Integer;
begin
Fields := TStringList.Create;
try
Fields.StrictDelimiter := True;
Fields.Delimiter := ';';
AssignFile(csv, ExtractFilePath(Application.ExeName) + 'clients.csv');
try
Reset(csv);
LineNo := 0;
while not Eof(csv) do begin
Inc(LineNo);
Readln(csv, Rec);
Fields.DelimitedText := Rec;
CDS.Append;
for i := 0 to Fields.Count - 1 do
try
CDS.Fields[i].Value := Fields[i]; // Variant conversion will raise
// exception where conversion from string fails
except
on E:EDatabaseError do begin
CDS.Cancel; // Failed, discard the record
// log the error instead of showing a message
ShowMessage(Format('Cannot set field "%s" at line %d' + sLineBreak +
'Error: %s', [CDS.Fields[i].FieldName, LineNo, E.Message]));
Break; // Continue with next record
end;
end;
if CDS.State = dsInsert then // It's not dsInsert if we Cancelled the Insert
try
CDS.Post;
except
on E:EDatabaseError do begin
// log error instead of showing
ShowMessage(Format('Cannot post line %d' + sLineBreak + 'Error: %s',
[LineNo, E.Message]));
CDS.Cancel;
end;
end;
end;
finally
CloseFile(csv);
end;
finally
Fields.Free;
end;
end;
procedure TForm1.CDSBeforePost(DataSet: TDataSet);
begin
// Superficial posting error
if CDS.FieldByName('LAST_NAME').AsString = '' then
raise EDatabaseError.Create('LAST_NAME cannot be empty');
end;
我会使用 JvCsvDataSet(JEDI JVCL 组件),因为它可以正确解析 CSV 文件,然后使用数据泵组件将数据移动到客户端数据集中,并进行一些验证。
但是,如果您真正需要做的只是为数据感知控件提供一个 CSV 文件,我将完全省略 ClientDataSet,而只使用为您尝试执行的目的而构建的组件。不要把螺丝当钉子,也不要把钉子当螺丝钉。它们都是由金属制成的,但它们的工作不同。
CSV 文件表定义的目的与 CDS 表定义完全不同,JvCsvDataSet 提供了一个简单的字符串属性,您可以设置它来提供元数据(字段数据类型,如整数或字符串或日期时间,以及相关的字段名称,对于缺少标题行的 CSV 文件)比您希望在 ClientDatSet 中执行的操作更容易。
AFAIK,没有直接的方法将 .csv 数据加载到TClientDataset
.
我能想到的最简单的方法是使用TTextDataSet
(在 中找到Demos\Delphi\Database\TextData
,从中获得Start->All Programs->Embarcadero RAD Studio XE->Samples
)。您可以像使用任何其他 TDataSet 一样使用它,这意味着您可以从中读取Fields
或使用FieldByName
,并且它支持Bof
、Eof
、Next
和Prior
。
您可以简单地迭代并尝试分配给您的 CDS 列,它会生成您可以处理或记录的错误。
您可以TTextDataset
像任何其他组件一样安装,或者只是将单元添加到uses
子句并在运行时创建它。文件夹中有一个readme.htm
文件,解释不多;关键属性是FileName
和Active
。:)
它包括一个预先设计的包 ( TextPkg.dproj
) 和一个测试应用程序 ( TextTest.dproj
)。还有一个项目组 ( TextDataGroup.groupproj
) - 您可以简单地在 IDE 中打开它,构建并安装TextPkg
包,然后编译并运行测试应用程序。测试应用程序的源代码很好地显示了使用情况。
如果您的数据库是 DBISAM,您可以简单地使用 IMPORT SQL 语句。
import table "tablename" from "myinputfile.csv" Delimiter ',';
其他数据库可能具有类似的功能。