在 Delphi 中使用现有数据库将数据写入文本文件?

u59ebvdq  于 2023-06-22  发布在  其他
关注(0)|答案(1)|浏览(152)

我试图交叉引用数据写入一个文本文件与现有的数据库IE(检查是否写入文本文件的数据已经存在于数据库中)。
我已经创建了一个程序,将用户登录数据(名称和密码)写入文本文件,然后我开始编写一个算法来从文本文件中读取数据,但我有点卡住了,我将名称存储在文本文件的第一行,密码(仅限字符串值)存储在下一行。
我不知道如何检查这些数据是否已经存在于数据库中,是否需要首先提取数据库的内容?还是直接和数据库交叉比对我已经创建了数据库(UserData.accdb),但尚未将其链接到表单。这是我目前所拥有的:

procedure TForm1.btnclickClick(Sender: TObject);

var
 tRegister      :    TextFile;
 Sline          :    String;
 Sname,SPword   :    String;

begin
  Assignfile(tRegister,'register.txt');

Try
 Reset(tRegister);

 except
   Showmessage('File Register.txt does not exist');
   Exit;
 end;

 While not EOF(tRegister) do
   ReadLn(tRegister,Sline);
     Sname:=Copy(Sline);

      // This is where i want to add code 

      end;


   end;

end.

请不要太苛刻,我还是新的 Delphi :)

tuwxkamq

tuwxkamq1#

我从你的问题中了解到,你目前正试图检查数据库中是否存在特定的记录。我会非常简短地回答这个问题,因为这个网站上有很多类似的问题,可以帮助你充实细节。
然而,您的问题的标题询问“交叉引用写入文本文件的数据与现有数据库”。从描述中看,听起来好像你在试图协调来自两个来源的数据,并找出哪些匹配,哪些不匹配。我会花更多的时间回答这个问题,因为我认为会有更多有价值的信息。

要检查数据库中的数据,需要:

  • 配置为指向数据库的连接组件。
  • 链接到连接组件的查询组件。
  • 查询文本将使用SQL语句从数据库中的特定表中选择行。
  • 我建议您的查询参数化,以便专门选择您要查找的行(稍后我将解释原因)
      • 注意**:您可以使用表组件而不是查询组件,这将改变您检查现有行的方式。它的优点是您不需要编写SQL代码。但是写得好的SQL将更具可伸缩性。

上述选项因您使用的数据库和组件而异。但正如我所说,现在已经有很多类似的问题。做点研究你应该能弄明白。
如果你被卡住了,你可以问一个更具体的问题,详细说明你试过什么,什么不起作用。(* 记住这不是免费的"为你服务",如果它看起来像是你所期望的,你会得到反弹 *。

文本文件与数据库的数据协调:

有几种不同的方法。你选择的那个很好。基本上可以归结为:
1.对于TheFile中的每个Entry
1.如果Entry存在于TheDatabase
1..... do something with Entry
1.....否则使用Entry执行其他操作
上面的步骤很容易理解,所以很容易相信算法是正确的。如果Delphi中没有一行程序来实现这些步骤也没关系。作为一个程序员,你有能力创建你需要的任何额外的函数/过程。保持程序的结构简单是很重要的。
上述任何一个步骤都不能很容易地实现,然后你想分解成更小的步骤:2.a. 2.b.; 3.a. 3.b. 3.c.;等等。(这就是自顶向下设计的含义。)

    • 提示**:您希望将所有不同的细分转换为它们自己的函数和过程。这将使维护程序和重用已经编写的例程变得更加容易。

我将专注于分解步骤2。如果您的数据库和文本文件变得非常大,那么如何做到这一点非常重要。例如,您可以实现以下内容:每次你调用这个函数来检查“如果Entry存在”,它会查看数据库中的每一条记录。这将是非常糟糕的,因为如果您的文件中有m条目,而数据库中有n条目,那么您将执行m x n检查。

  • 还记得我说过我会解释为什么我建议使用参数化查询 * 吗?

数据库是为管理数据而设计和编写的。存储和检索数据是它们的主要功能,因此让它来完成查找您要查找的条目是否存在的工作。例如,如果您编写查询以获取Delphi应用程序中的所有条目并在那里搜索:

  • 增加应用程序的内存需求。
  • 但更重要的是,没有额外的工作,让自己暴露在上面提到的m x n问题中。

使用参数化查询,每次调用if EntryExists(...)时,您都可以更改参数值,并有效地要求数据库查找记录。数据库完成了这项工作,并给了你一个答案。例如,你可以这样写你的函数:

function TForm1.EntryExists(const AName: string): Boolean;
begin
  qryFindEntry.Close;
  qryFindEntry.Parameters.ParamByName('EntryName').Value := AName;
  qryFindEntry.Open;

  Result := qryFindEntry.RecordCount > 0;
end;
    • 提示**:在数据库中的相应列上定义索引非常重要,否则每次打开查询时,它也会搜索每条记录。
    • 注意**:另一种非常类似的方法是在数据库上编写存储过程,并使用存储过程组件调用数据库。

补充备注:

    • 您处理文件的例程是硬编码的,使用register.txt**

这使得它在当前形式下不可重用。而是将代码移到一个单独的方法中:procedure ProcessFile(AFileName: string);.然后在按钮中单击事件处理程序调用:ProcessFile('register.txt');

    • 提示**:事实上,将大量代码从事件处理程序中移到具有适当参数的方法中通常是一个好主意。更改事件处理程序以调用这些方法。这样做将使您的代码更易于维护、测试和重用。
    • 您的异常处理错误**

这是一种非常糟糕的异常处理方式。

    • 首先**,你不想写不必要的异常处理。它只会使你的代码膨胀,使其更难阅读和维护。当出现异常时:
  • 程序开始退出代码到最里面的finally/except块。(因此,异常将已经退出您的例程-正如您添加的代码所做的那样。
  • 默认情况下,一个未处理的异常(意味着你还没有在某个地方吞下的异常)将由应用程序异常处理程序处理。默认情况下,这将只是显示一个错误对话框。(正如您添加了要执行的代码一样。)
  • 代码所做的唯一更改是显示与实际引发的消息不同的消息。问题是你做了一个错误的假设。"文件不存在"不是Reset(tRegister);引发异常的唯一可能原因:
  • 文件可能存在,但被独占锁定。
  • 该文件可能存在,但您没有权限访问它。
  • 可能有一个资源错误,意味着文件在那里,但 * 不能 * 打开。
  • 所以所有异常处理代码所做的唯一一件事就是引入一个bug,因为它现在有能力隐藏异常的真正原因。这会使故障排除更加困难。

如果您想提供有关异常的更多信息,下面是一个更好的方法:

try
  Reset(tRegister);
except
  on E: Exception do
  begin
    //Note that the message doesn't make any assumptions about the cause of the error.
    E.Message := 'Unable to open file "'+AFileName+'": ' + E.Message;
    //Reraise the same exception but with extra potentially useful information.
    raise;
  end;
end;

第二个问题是,即使你告诉了用户这个错误,你也对程序的其他部分隐藏了这个事实。让我们假设您已经发现ProcessFile方法的更多用途。你现在有一个例程:

  • 通过电子邮件接收文件。
  • 调用ProcessFile。
  • 然后删除文件和电子邮件。

如果在ProcessFile中引发异常,而您将其吞下(处理),则上述例程将删除未处理的文件。这显然是不好的。如果您没有吞下异常,上面的例程将跳过删除步骤,因为程序正在寻找下一个finally/except块。至少这样,您仍然有文件的记录,以进行故障排除和重新处理,一旦问题得到解决。
第三个**问题是你的异常处理程序假设你的例程总是有一个用户可以交互。这限制了可重用性,因为如果现在在服务器端应用程序中调用ProcessFile,将弹出一个对话框,但没有人关闭它。
让未解决的异常由应用程序异常处理程序处理意味着您只需要更改服务器应用程序中的默认应用程序异常处理程序,并且所有异常都可以记录到文件中-而不会弹出对话框。

相关问题