为什么我的代码导致I / O 104错误?

时间:2013-10-12 14:15:04

标签: delphi delphi-7

首次进入while循环时,此程序在EoF上引发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;

3 个答案:

答案 0 :(得分:6)

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

更新error 104file not open for input,这意味着Reset()没有打开文件,但没有引发异常(如果发生异常(这听起来像是RTL错误) Eof()正在引发异常,表示{I+}处于有效状态。

在任何情况下,使用AssignFile()和相关例程是执行文件I / O的旧方法。您应该使用更新的技术,例如FileOpen()FileRead()TFileStreamTStreamReaderTStringList等等。

更新:您的循环逻辑错误。您只比较第一行。如果它与用户不匹配,则表示您正在擦除文件,将用户写入新文件,关闭文件,然后继续循环。那时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;

答案 1 :(得分:0)

为了完整起见,此版本适用于我,但很难猜测代码的目的是什么。特别是while循环似乎有点移位,因为在重写案例被命中一次后文件将只包含一行。

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.

答案 2 :(得分: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。

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