首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Delphi中从文本文件中删除特定行

如何在Delphi中从文本文件中删除特定行
EN

Stack Overflow用户
提问于 2016-08-18 15:10:30
回答 6查看 6.4K关注 0票数 11

我有一个文本文件,用户信息逐行存储在其中。每一行都是格式:UserID#UserEmail#UserPassword,“#”是分隔符。

我尝试使用这种编码来执行以下任务:

代码语言:javascript
运行
复制
var sl:TStringList;
begin
  sl:=TStringList.Create;
  sl.LoadFromFile('filename');
  sl.Delete(Index);
  sl.SaveToFile('filename');
  sl.free;
end;

但我不知道在“索引”空间里放什么。

是否有任何方法可以接收用户ID作为输入,然后从包含此用户ID的文本文件中删除文本行?任何帮助都将不胜感激。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2016-08-18 15:31:32

您可以将NameValueSeparator设置为#,然后使用IndexOfName查找用户,只要用户名是文件中的第一个值。

代码语言:javascript
运行
复制
sl.NameValueSeparator := '#';
Index := sl.IndexOfName('455115')

所以在你的例子中,就像这样

代码语言:javascript
运行
复制
var sl:TStringList;
begin
  sl:=TStringList.Create;
  sl.LoadFromFile('filename');
  sl.NameValueSeparator := '#';
  Index := sl.IndexOfName('455115')
  if (Index  <> -1) then
  begin
      sl.Delete(Index);
      sl.SaveToFile('filename');
  end;
  sl.free;
end;

对于大型文件,这可能比较慢,因为IndexOfName循环遍历TStringList中的每一行,然后依次检查每个字符串,直到找到匹配的字符串为止。

免责声明:测试/工作与Delphi 2007,德尔菲7可能是不同的。

票数 6
EN

Stack Overflow用户

发布于 2016-08-18 20:45:37

我不明白为什么这么多人让这件事这么难。这很简单:

代码语言:javascript
运行
复制
function ShouldDeleteLine(const UserID, Line: string): Boolean;
begin    
  // Remember: Pos(Needle, Haystack)
  Result := Pos(UserID + '#', Line) = 1; // always 1-based!
end;

procedure DeleteLinesWithUserID(const FileName, UserID: string);
var
  SL: TStringList;
  I: Integer;
begin
  if not FileExists(FileName) then
    Exit;

  SL := TStringList.Create;
  try
    SL.LoadFromFile(FileName); // Add exception handling for the 
                               // case the file does not load properly.

    // Always work backward when deleting items, otherwise your index
    // may be off if you really delete.
    for I := SL.Count - 1 downto 0 do
      if ShouldDeleteLine(SL[I], UserID) then
      begin
        SL.Delete(I);
        // if UserID is unique, you can uncomment the following line.
        // Break;
      end;
    SL.SaveToFile(FileName);
  finally
    SL.Free;
  end;
end;

正如Arioch'The说的,如果您将保存保存到相同的文件名,那么当保存失败时,您可能会丢失数据,所以您可以这样做

代码语言:javascript
运行
复制
SL.SaveToFile(FileName + '.dup');
if FileExists(FileName + '.old') then
  DeleteFile(FileName + '.old');
RenameFile(FileName, FileName + '.old');
RenameFile(FileName + '.dup', FileName);

它将原始文件备份为FileName + '.old'__。

解释

向后工作

为什么向后工作?因为如果您有以下项目

代码语言:javascript
运行
复制
A B C D E F G
      ^

如果您在^删除该项目,则下列项目将向下移动:

代码语言:javascript
运行
复制
A B C E F G
      ^

如果您迭代前进,您现在将指向

代码语言:javascript
运行
复制
A B C E F G
        ^

E从未被检查过。如果您向后退,那么您将指向:

代码语言:javascript
运行
复制
A B C E F G
    ^

注意,EFG已经被检查过了,所以现在您确实将检查下一项C,并且不会遗漏任何项。此外,如果使用0 to Count - 1向上移动,并删除,Count将减少一个,最后,您将尝试访问超过列表的边界。如果您使用Count - 1 downto 0反向工作,这是不可能发生的。

使用+ '#'

如果您附加了'#'并测试了Pos() = 1,您将确保将整个UserID捕获到分隔符,而不是一个只有包含正在寻找的UserID的用户ID的行。IOW,如果UserID'velthuis',您不想删除像'rudyvelthuis#rvelthuis01#password''velthuisresidence#vr#password2'这样的行,但是您确实想要删除'velthuis#bla#pw3'

例如,在查找用户名时,您查找'#' + UserName + '#'的原因与此相同。

票数 1
EN

Stack Overflow用户

发布于 2016-08-18 15:57:10

实际上,“从文本文件中删除一行”的唯一方法是创建一个新文件,修改内容,重写它。

所以你最好直截了当地做。

别忘了保护自己免受错误的伤害。如果发生任何错误,当前代码可能只会销毁文件并泄漏内存.

代码语言:javascript
运行
复制
var sl: TStringList;
    s, prefix: string;
    i: integer; okay: Boolean;
    fs: TStream;

begin
  prefix := 'UserName' + '#';
  okay := false;

  fs := nil;
  sl:=TStringList.Create;
  Try   /// !!!!
    sl.LoadFromFile('filename');
    fs := TFileStream.Create( 'filename~new', fmCreate or fmShareExclusive );

    for i := 0 to Prev(sl.Count) do begin
      s := sl[ i ];

      if AnsiStartsStr( prefix, Trim(s) ) then
         continue;  // skip the line - it was our haunted user

      s := s + ^M^J;  // add end-of-line marker for saving to file

      fs.WriteBuffer( s[1], length(s)*SizeOf(s[1]) );  
    end; 
  finally 
    fs.Free;
    sl.Free;
  end;

  // here - and only here - we are sure we successfully rewritten 
  // the fixed file and only no are able to safely delete old file
  if RenameFile( 'filename' , 'filename~old') then
     if RenameFile( 'filename~new' , 'filename') then begin
        okay := true; 
        DeleteFile( 'filename~old' ); 
     end;

  if not okay then ShowMessage(' ERROR!!! ');
end;

注意1:查看用户名检查是区分大小写还是忽略大小写:

注2:在Delphi7中,SizeOf( s[1] )总是等于1,因为stringAnsiString的别名。但在较新的Delphi版本中,情况并非如此。这看起来既乏味又多余--但它可能会在将来省去很多麻烦。更好的方法是有一个临时的AnsiString类型变量,比如a := AnsiString( s + ^m^J ); fs.WriteBuffer(a[1],Length(a));

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39021743

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档