1

该程序EoF在第一次进入 while 循环时引发 I/O 104 错误。

该程序的目的是查找用户名是否已被使用。现有用户名存储在文本文件中。

procedure TForm1.btnRegisterClick(Sender: TObject);
begin
  sCUser := edtUserName.Text;
  AssignFile(tNames, 'Names.txt');
  begin
    try
      Reset(tNames);
    except
      ShowMessage('File not found');
      Exit;
    end;
  end;
  rewrite(tNames);
  while not EoF(tNames) do // I get a I/O 104 Error here `
  begin
    Readln(tNames, sLine);
    iPosComme := Pos(',', sLine);
    sUser     := Copy(sLine, 1, iPosComme - 1);
    Delete(sLine, 1, iPosComme - 1);
    if sCUser = sUser then begin
      ShowMessage('Username taken');
    end
    else
    begin
      rewrite(tNames);
      Writeln(tNames, sCUser + ',' + '0');
      CloseFile(tNames);
    end;
  end;
end;
4

3 回答 3

6

删除对Rewrite()之前的调用Eof()。即使您没有收到 IO 错误,您的代码仍然会失败,因为Rewrite()关闭您打开的文件Reset()然后它会创建一个新的银行文件,因此Eof()始终为 True。

更新错误 104file not open for input,这意味着Reset()没有打开文件但没有引发异常(如果Eof()引发异常,这听起来像 RTL 错误,表明它{I+}是活动的)。

无论如何,使用AssignFile()和相关的例程是进行文件 I/O 的旧方法。您应该使用更新的技术,例如FileOpen()with FileRead()TFileStreamwith TStreamReaderTStringList等...

更新:你的循环逻辑是错误的。您只比较第一行。如果它与用户不匹配,则您正在清除文件,将用户写入新文件,关闭文件,然后继续循环。EoF()届时将失败。您需要将循环重写为以下内容:

procedure TForm1.btnRegisterClick(Sender: TObject
var
  SCUser, sUser: String;
  tNames: TextFile;
  iPosComme: Integer;
  Found: Boolean;
begin
  sCUser := edtUserName.Text;
  AssignFile(tNames,'Names.txt');
  try
    Reset(tNames);
  except
    ShowMessage('File not found');
    Exit;
  end;
  try
    Found := False;
    while not EoF(tNames) do
    begin
      Readln(tNames,sLine);
      iPosComme := Pos(',', sLine);
      sUser := Copy(sLine ,1,iPosComme -1);
      if sCUser = sUser then
      begin
        ShowMessage('Username taken') ;
        Found := True;
        Break;
      end;
    end;
    if not Found then
      Writeln(tNames,sCUser + ',0');
  finally
    CloseFile(tNames);
  end;
end;
于 2013-10-12T17:26:30.470 回答
0

我编写了这个方法的一个版本,它使用了更新的TStreamReaderTStreamWriter类。

当然,这不适用于 Delphi 7,它只是为了展示如何在较新版本的 Delphi 中做到这一点。

该代码深受 Remys 回答的启发。

procedure TForm1.btnRegisterClick(Sender: TObject);
var
  Stream: TStream;
  Reader: TStreamReader;
  Writer: TStreamWriter;
  Columns: TStringList;
  UserName: string;
  Found: Boolean;
  FileName: string;
  Encoding: TEncoding;
begin
  FileName := ExpandFileName('Names.txt'); // An absolute path would be even better
  UserName := edtUsername.Text;
  Found    := False;
  Encoding := TEncoding.Default; // or another encoding, e.g. TEncoding.Unicode for Unicode
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Reader := TStreamReader.Create(Stream, Encoding);
    try
      Columns := TStringList.Create;
      try
        Columns.Delimiter := ',';
        Columns.StrictDelimiter := True; // or False, depending on the file format
        while not Reader.EndOfStream do
        begin
          Columns.DelimitedText := Reader.ReadLine;
          if Columns.Count > 0 then 
          begin
            if AnsiSameStr(Columns[0], UserName) then // or AnsiSameText if UserName is not case-sensitive
            begin
              ShowMessage('Username taken') ;
              Found := True;
              Break;
            end;
          end;
        end;
      finally
        Columns.Free;
      end;
    finally
      Reader.Free;
    end;
  finally
    Stream.Free;
  end;
  if not Found then
  begin
    Writer := TStreamWriter.Create(FileName, True, Encoding);
    try
      // Warning: This will cause problems when the file does not end with a new line
      Writer.WriteLine(UserName + ',0');
    finally
      Writer.Free;
    end;
  end;
end;

如果性能和内存使用不是问题:

procedure TForm1.btnRegisterClick(Sender: TObject);
var
  Rows: TStringList;
  Columns: TStringList;
  UserName: string;
  Found: Boolean;
  FileName: string;
  Encoding: TEncoding;
  Row: string;
begin
  FileName := ExpandFileName('Names.txt'); // An absolute path would be even better
  UserName := edtUsername.Text;
  Found    := False;
  Encoding := TEncoding.Default; // or another encoding, e.g. TEncoding.Unicode for Unicode
  Rows := TStringList.Create;
  try
    Rows.LoadFromFile(FileName, Encoding);
    Columns := TStringList.Create;
    try
      Columns.Delimiter := ',';
      Columns.StrictDelimiter := True; // or False, depending on the file format
      for Row in Rows do
      begin
        Columns.DelimitedText := Row;
        if Columns.Count > 0 then
        begin
          if AnsiSameStr(Columns[0], UserName) then // or AnsiSameText if UserName is not case-sensitive
          begin
            ShowMessage('Username taken') ;
            Found := True;
            Break;
          end;
        end;
      end;
    finally
      Columns.Free;
    end;
    if not Found then
    begin
      Rows.Add(UserName + ',0');
      Rows.SaveToFile(FileName, Encoding);
    end;
  finally
    Rows.Free;
  end;
end;

Encoding该解决方案可以通过删除变量来适应 Delphi 7 。

如果它是更大数据库的一部分,它应该存储在真正的数据库管理系统中,而不是文本文件中。

于 2013-10-14T16:52:35.397 回答
0

为了完整起见,这个版本对我有用,但很难猜出代码的用途。尤其是 while 循环似乎有点移位,因为在 rewrite-case 被击中一次后,文件将只包含一行。

program wtf;

{$APPTYPE CONSOLE}
{$I+}

uses
  SysUtils;

procedure Sample( sCUser : string);
var sUser, sLine : string;
    iPosComme : Integer;
    tnames : textfile;
begin
  AssignFile(tNames,'Names.txt');

  try
    Reset(tNames);
  except
    Writeln('File not found');
    Exit;
  end;

  while not EoF(tNames) do
  begin
    Readln(tNames,sLine);
    iPosComme := Pos(',', sLine);
    sUser := Copy(sLine ,1,iPosComme -1);
    Delete( sLine,1, iPosComme -1);
    if sCuser = sUser then begin
      Writeln('Username taken') ;
    end
    else begin
      Rewrite(tNames);
      Writeln(tNames,sCUser + ',' + '0');
      CloseFile(tNames);
      Break; // file has been overwritten and closed
    end;
  end;
end;

begin
  try
    Sample('foobar');
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;
end.
于 2013-10-13T15:43:24.907 回答