【问题标题】:How can I loop through a delimited string and assign the contents of the string to local delphi variables?如何遍历分隔字符串并将字符串的内容分配给本地 delphi 变量?
【发布时间】:2012-08-14 14:36:55
【问题描述】:

我编写了一个 Delphi 函数,它将 .dat 文件中的数据加载到字符串列表中。然后它解码字符串列表并分配给一个字符串变量。字符串的内容使用“#”符号作为分隔符。

我怎样才能获取这个字符串的内容,然后将其内容分配给局部变量?

// Function loads data from a dat file and assigns to a String List.
function TfrmMain.LoadFromFile;
var 
  index, Count : integer;
  profileFile, DecodedString : string;
begin
  // Open a file and assign to a local variable.
  OpenDialog1.Execute;
  profileFile := OpenDialog1.FileName;
  if profileFile = '' then
    exit;
  profileList := TStringList.Create;
  profileList.LoadFromFile(profileFile);
  for index := 0 to profileList.Count - 1 do
  begin
    Line := '';
    Line := profileList[Index];
  end;
end;

解码后,var "Line" 包含如下内容:

示例:

Line '23#80#10#2#1#...255#'.

并非所有分隔符之间的值都具有相同的长度,并且每次调用函数 LoadFromFile 时,“Line”的值都会有所不同(例如,有时一个值在接下来的两个或三个等中可能只有一个数字等,所以我不能依赖于字符串或数组的 Copy 函数)。

我正在尝试找出一种循环遍历“Line”内容的方法,将其分配给名为“buffer”的局部变量,然后如果遇到“#”,则将缓冲区的值分配给局部变量,将缓冲区重新初始化为 '';然后移动到“Line”中的下一个值,重复下一个参数的过程,每次忽略'#'。

我想我已经纠结这个问题太久了,我似乎无法取得任何进展,需要休息一下。如果有人愿意看一看,我欢迎任何关于如何实现这一目标的建议。

非常感谢

KD

【问题讨论】:

  • 文件是单行还是多行#分隔的行?
  • 看来你没有释放profileList。那就是内存泄漏。如果您需要在解析每一行之前将文件拆分为行,那么您应该使用 try-finally 来释放对象。查看@UweRaabe 的答案 - 并使用创建和释放 profileList 的相同模式。外壳“profileList”表明您来自 Java 领域或类似的地方。这里没有垃圾收集是Delphi。如果你创造了一些东西——你有责任释放它。虽然有引用计数的接口,比如我的回答中提到的 IJclStringList 。它们将被编译器释放。
  • 如果文件不是多行,而是单行,那么你应该把它读入字符串变量而不是字符串列表。
  • "OpenDialog1.Execute;" - 你会检查结果吗?如果用户按下“取消”,他会对结果感到非常好笑……
  • @Arioch'The,我越是觉得您需要在 StackOverflow 上为您的 cmets 提供一个特殊选项卡。然后人们可以简单地与其他 cmets 一起阅读问题,并可以选择使用您的选项卡切换到选项卡:-)

标签: delphi loops delphi-xe2 tstringlist


【解决方案1】:

你需要第二个 TStringList:

  lineLst := TStringList.Create;
  try
    lineLst.Delimiter := '#';
    lineLst.DelimitedText := Line;
    ...
  finally
    lineLst.Free;
  end;

根据您的 Delphi 版本,您可以设置 lineLst.StrictDelimiter := true 以防该行包含空格。

【讨论】:

  • “取决于你的 Delphi 版本”——这就是我讨厌这种方法的地方。只是情绪上不喜欢:-)
  • 感谢您的回复 - 我应该说版本是 XE2。
  • @Arioch'The,Delphi 版本只在需要 StrictDelimiter 时才重要,只有在行数据中有空格时才需要。如果没有空格,也会被视为分隔符。
【解决方案2】:

你可以这样做:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, StrUtils;

var
  S : string;
  D : string;

begin
  S := '23#80#10#2#1#...255#';

  for D in SplitString(S,'#') do //SplitString is in the StrUtils unit
    writeln(D);

  readln;
end.

【讨论】:

    【解决方案3】:

    您没有标记您的 Delphi 版本,所以我不知道它是否适用。 那是特定于版本的。请做!

    按个人喜好排列:

    1:下载 Jedi CodeLib - http://jcl.sf.net。然后使用 TJclStringList。它有非常好的分割方法。之后,您只需要迭代。

    function Split(const AText, ASeparator: string; AClearBeforeAdd: Boolean = True): IJclStringList;

    uses JclStringLists;
    ...
    var s: string;  js: IJclStringList.
    begin
    ...
       js := TJclStringList.Create().Split(input, '#', True);
       for s in js do begin
          .....
       end;
    ...
    end;
    

    2:Delphi 现在的 StringSplit 例程功能有所减少。 http://docwiki.embarcadero.com/Libraries/en/System.StrUtils.SplitString 字符串数组 类型的错误特征可能与自身不兼容。你好,1949 年的帕斯卡规则……

    uses StrUtils;
    ...
    var s: string;  
        a_s: TStringDynArray; 
    (* aka array-of-string aka TArray<string>. But you have to remember this term exactly*)
    begin
    ...
       a_s := SplitString(input, '#');
       for s in a_s do begin
          .....
       end;
    ...
    end;
    

    3:使用 TStringList。它的主要问题是它被设计成空格或新行是内置的分隔符。在可以抑制的较新的 Delphi 中。总体而言,代码应根据您的确切 Delphi 版本进行定制。您可以轻松地在 Google 上搜索“使用 TStringlist 拆分字符串”之类的内容,并获取大量示例(例如 @Uwe 的示例)。

    但你可能会忘记在这里或那里压制。而且您可能在旧的 Delphi 上,无法做到这一点。你可能会误用不同 Delphi 版本的例子。而且...这很无聊 :-) 虽然您可以创建自己的函数来为您生成此类预先调整的字符串列表并仔细检查其中的 Delphi 版本 :-) 但是您必须在使用后小心地释放该对象。

    【讨论】:

    • 对不起,我应该说 Delphi XE2。感谢您的回复。
    • 我认为最好不要“说”,而是添加 delphi-xe2 标签。不客气。
    • “输入”是字符串。您应该将整个文件读入其中,但我相信您可以通过谷歌搜索如何做到这一点。将文件加载到 stringlist 是错误的,因为您告诉过您不要;不需要在不同的行上拆分。它会很脆弱,而且速度较慢。
    【解决方案4】:

    我使用我编写的函数Fetch。我想我前段时间从 Indy 图书馆偷了这个想法:

    function Fetch(var VString: string; ASeperator: string = ','): string;
    var   LPos: integer;
    begin
      LPos := AnsiPos(ASeperator, VString);
      if LPos > 0 then
      begin
        result := Trim(Copy(VString, 1, LPos - 1));
        VString := Copy(VString, LPos + 1, MAXINT);
      end
      else
      begin
        result := VString;
        VString := '';
      end;
    end;
    

    那么我会这样称呼它:

    var
      value: string;
      line: string;
      profileFile: string;
      profileList: TStringList;
      index: integer;
    begin
      if OpenDialog1.Execute then
      begin 
        profileFile := OpenDialog1.FileName;
        if (profileFile = '') or not FileExists(profileFile) then
          exit;
    
        profileList := TStringList.Create;
        try
          profileList.LoadFromFile(profileFile);
    
          for index := 0 to profileList.Count - 1 do
          begin
            line := profileList[index];
            Fetch(line, ''''); //discard "Line '"
            value := Fetch(line, '#')
            while (value <> '') and (value[1] <> '''') do //bail when we get to the quote at the end
            begin
               ProcessTheNumber(value); //do whatever you need to do with the number
               value := Fetch(line, '#');
            end;
          end;
        finally
          profileList.Free;
        end;
      end;
    end;
    

    注意:这是在浏览器中输入的,所以我没有检查它是否有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-09
      • 2017-03-03
      • 1970-01-01
      • 2011-07-13
      • 2019-02-08
      相关资源
      最近更新 更多