从分隔符分隔值文件恢复数据集

发布于 2024-11-15 22:43:34 字数 180 浏览 5 评论 0原文

简而言之,我是delphi新手,我想实现以下目标:

  • 我将表定义为.cds文件{index,data,date},以及一些.csv格式的数据。
  • 我想将 .csv 文件加载到表中并显示其更改和错误的日志(例如:无效的日期格式)。

问题

如何优雅地解决这个任务?

In short, i'm new to delphi and I want to achieve the following:

  • I have table definition as .cds file {index, data, date}, and some data in .csv format.
  • I want to load the .csv file to the table and show log it's changes and errors (ex: invalid date format).

Question

How to solve this task elegantly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

故乡的云 2024-11-22 22:43:34

我会使用 JvCsvDataSet(JEDI JVCL 组件),因为它可以正确解析 CSV 文件,然后使用数据泵组件将数据移动到客户端数据集中,并进行一些验证。

但是,如果您真正需要做的只是向数据感知控件提供 CSV 文件,我会完全省略 ClientDataSet,而只使用为您想要实现的目的而构建的组件。不要将螺丝当钉子使用,也不要将钉子当螺丝使用。它们都是由金属制成的,但它们的工作不同。

CSV 文件表定义与 CDS 表定义在用途上有很大不同,JvCsvDataSet 提供了一个简单的字符串属性,您可以设置该属性来提供元数据(字段数据类型,如整数、字符串或日期时间,以及关联的字段名称,对于缺少标题行的 CSV 文件)比您希望在 ClientDatSet 中完成的操作更容易。

I would use JvCsvDataSet (JEDI JVCL component) because it parses CSV files properly, and then use a data-pump component, to move the data into the client dataset, along with some validation.

But if all you really need to do is provide a CSV file, to a data-aware control, I would leave out the ClientDataSet completely, and just use a component built for the purpose you are trying to do. Don't use a screw as a nail, or a nail as a screw. They are both made of metal, but they do different jobs.

CSV file table definitions are quite different in purpose, to a CDS table definition, and the JvCsvDataSet provides a simple string property which you can set up to give the metadata (field datatypes like integer or string or date-time, and associated field names, for CSV files that lack a header row) more easily, than you could hope to do it in ClientDatSet.

爱*していゐ 2024-11-22 22:43:34

您可以从 .csv 中逐行读取,将每一行设置为 StringList 的“DelimitedText”,将记录附加到数据集,循环字符串列表以设置每个字段的值,然后发布到数据集。
您可以将“字段值分配”/“发布”放在 try- except 块中,并记录引发异常的任何错误消息以及您喜欢的信息(例如格式错误的字段值/名称、行号和/或整个行等)到文件 fi

(我不明白你所说的“更改”是什么意思,根据我的理解,.csv 中的行将被插入到数据集中,因此所有更改都将被插入。)

编辑:为了能够讨论具体的事情(我很难掌握任务:))

示例数据(CodeGear示例“Clients.cds”的一部分):

戴维斯;詹妮弗;1023495,0000;100
蔓越莓
圣;韦尔斯利;马萨诸塞州;02181;516-292-3945;01.01.93
琼斯;阿瑟;2094056,0000;10 亨尼韦尔
圣洛斯
阿尔托斯;CA;94024;415-941-4321;07.02.81
帕克;黛布拉;1209395,0000;74 南
圣阿瑟顿;CA;98765;916-213-2234;23.10.90
索耶;戴夫;3094095,0000;101 奥克兰
圣洛斯
阿尔托斯;CA;94022;415-948-9998;21.12.89
怀特;辛迪;1024034,0000;1 温特沃斯
洛斯博士
阿尔托斯;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;

You can read line by line from the .csv, set each line to 'DelimitedText' of a StringList, append a record to the dataset, loop the string list to set each field's value and then post to the dataset.
You can put the 'field value assinging'/'posting' in a try-except block and log any error message of raised exceptions together with information you like (e.g. malformed field value/name, line number, and/or entire line etc.) to a file f.i.

(I don't understand what you mean by 'changes', from what I understood, lines from the .csv will be inserted to a dataset, hence all changes will be inserts.)

edit: To be able to discuss on something concrete (I'm having a hard time grasping the task :))

Sample data (part of CodeGear sample '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;
So要识趣 2024-11-22 22:43:34

AFAIK,没有直接的方法将 .csv 数据加载到 TClientDataset 中。

我能想到的最简单的方法是使用TTextDataSet(在Demos\Delphi\Database\TextData中找到,可从开始->所有程序- >Embarcadero RAD Studio XE->示例)。您可以像任何其他 TDataSet 一样使用它,这意味着您可以从它的 Fields 中读取或使用 FieldByName,并且它支持 BofEof下一个先前

您可以简单地迭代并尝试分配给您的 CDS 列,它将生成您可以处理或记录的错误。

您可以像任何其他组件一样安装 TTextDataset,或者只是将单元添加到 uses 子句并在运行时创建它。文件夹中有一个readme.htm文件,不多解释;关键属性是 FileNameActive。 :)

它包括预先设计的包 (TextPkg.dproj) 和测试应用程序 (TextTest.dproj)。还有一个项目组 (TextDataGroup.groupproj) - 您只需在 IDE 中打开它,构建并安装 TextPkg 包,然后编译并运行测试应用程序。测试应用程序的源代码很好地显示了使用情况。

AFAIK, there's no direct way to load .csv data into a TClientDataset.

The easiest way I can think of would be to use the TTextDataSet (found in Demos\Delphi\Database\TextData, available from Start->All Programs->Embarcadero RAD Studio XE->Samples). You can use it just like any other TDataSet, meaning you can read from it's Fields or use FieldByName, and it supports Bof, Eof, Next, and Prior.

You can simply iterate through and try to assign to your CDS columns, and it will generate errors you can then handle or log.

You can install TTextDataset like any other component, or just add the unit to the uses clause and create it at runtime. There's a readme.htm file in the folder that doesn't explain much; the key properties are FileName and Active. :)

It includes both a pre-designed package (TextPkg.dproj) and a test app (TextTest.dproj). There's also a project group (TextDataGroup.groupproj) - you can simply open this in the IDE, build and install the TextPkg package, and then compile and run the test app. The source for the test app shows usage pretty well.

云朵有点甜 2024-11-22 22:43:34

如果您的数据库是 DBISAM,您可以简单地使用 IMPORT SQL 语句。

import table "tablename" from "myinputfile.csv" Delimiter ',';

其他数据库可能具有类似的功能。

In the off-chance that your database is DBISAM, you can simply use the IMPORT SQL statement.

import table "tablename" from "myinputfile.csv" Delimiter ',';

Other databases may have a similar feature.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文