1
procedure Split(S: String; List: TStringList; Separator: Char);
var
  P, C: PAnsiChar;
  S, Buff: String;
begin
  List.Clear;

  if S = '' then
    Exit;

  List.BeginUpdate;

  (* [Ajusting size  - Slow *)
  if S[1] = Separator then
    Insert('', S, 1);

  S := S + Separator;
  (* Adjusting size] *)

  //Get Pointer to data
  P := PChar(S);

  //initial position
  C := P;
  while P^ <> #0 do //check if reached the end of the string
  begin
    //when found a separator
    if P^ = Separator then
    begin
      if P = C then //check if the slot is empty
        Buff := ''
      else //when it is not empty, make an string buffer
        SetString(Buff, C, P-C);

      List.Add(Buff); //add the string into the list
      Inc(C, P-C+1); //moves the pointer C to the adress of the pointer P
    end;

    Inc(P); //go to next char in the string
  end;

  List.EndUpdate;
end;

此代码工作正常,但在内存中移动字符串 3 次:

在方法调用中(通过复制)
在 Insert('', S, 1)
中串联: S := S + Separator;

我考虑在 S 参数中添加 const 关键字,创建一个内部字符串来复制数据或多或少像这样:

  if S[1] = Separator then
  begin
    SetLength(Str, Length(S)+2);
    //HERE!! how to copy the string  
    Str[1] := ' ';
  end
  else
  begin
    SetLength(Str, Length(S)+1);
    //HERE!! how to copy the string   
  end;

  //Add Separator in the last position
  Str[Length(Str)] := Separator;

因此:

如果 S 包含 ';'
它将创建一个包含 2 个项目('','')的字符串列表。
如果 S 包含“;A”
,它将创建一个包含 2 个项目(“”,“A”)的字符串列表。
如果 S 包含'A;A'
,它将创建一个包含 2 个项目('A','A')的字符串列表。
如果 S 包含 'A;'
它将创建一个包含 2 个项目('A','')的字符串列表。

4

2 回答 2

2

像这样:

if S[1] = Separator then
begin
  SetLength(Str, Length(S)+2);
  Move(Pointer(S)^, Str[2], Length(S)*SizeOf(Char));
  S[1] := ' '; // surely you mean Str[1] := ' '
end
else
begin
  SetLength(Str, Length(S)+1);
  Move(Pointer(S)^, Str[1], Length(S)*SizeOf(Char));
end;

//Add Separator in the last position
Str[Length(Str)] := Separator;

重新工作以避免重复是很容易的。

var
  dest: PChar;

if S[1] = Separator then
begin
  SetLength(Str, Length(S)+2);
  dest := @Str[2];
  S[1] := ' '; // surely you mean Str[1] := ' '
end
else
begin
  SetLength(Str, Length(S)+1);
  dest := @Str[1];
end;
Move(Pointer(S)^, dest^, Length(S)*SizeOf(Char));

//Add Separator in the last position
Str[Length(Str)] := Separator;

等等。我会把它留给你擦亮它。

于 2013-07-04T13:32:01.060 回答
1

以下例程是我为 Delphi 7 编写的(更准确地说,改编自SetDelimitedTextExtractStrings)来处理缺少TStrings.StrictDelimiter属性的例程。给定正确的参数,它将完全返回您想要的结果。

{
SplitString will expand the delimited string S into its component parts and
store them in Strings.  The primary difference between this routine and
Classes.ExtractStrings and TStrings.DelimitedText is that it does not treat
spaces, tabs, and CR/LF as delimiters whether you like it or not.  If Quotes
is non-empty, then quoted strings will be handled correctly.

Leading and Trailing whitespace is significant if TrimStrings is False.

If you want to eliminate empty tokens, set SkipEmptyStrings to True.

If you want Strings to be cleared before parsing, set ClearStrings to True.

This procedure is especially useful for dealing with CSV files exported from
Excel, since Excel does not quote a string unless it contains a comma.
Using ExtractStrings or TStrings.CommaText will fail with such files.

In Delphi 2006+, TStrings has the StrictDelimiter property that renders this
routine largely useless.
}
procedure SplitString(const S: string; Separators, Quotes: TSysCharSet; const Strings: TStrings; ClearStrings, TrimStrings, SkipEmptyStrings: Boolean);
var
  Head, Tail: PChar;
  Item: string;
  StringExists: Boolean;

  {$IF NOT Declared(CharInSet)}
  function CharInSet(C: Char; const CharSet: TSysCharSet): Boolean;
  begin
    Result := C in CharSet;
  end;
  {$IFEND}

begin
  StringExists := False;
  Strings.BeginUpdate;
  try
    if ClearStrings then
      Strings.Clear;
    if S = '' then
      Exit;

    Tail := PChar(S);
    while Tail^ <> #0 do begin
      if CharInSet(Tail^, Quotes) then
        Item := AnsiExtractQuotedStr(Tail, Tail^)
      else begin
        // Mark beginning of token
        Head := Tail;
        // Look for end of token, delineated by end of string or separator
        while (Tail^ <> #0) and not CharInSet(Tail^, Separators) do
          Inc(Tail);
        SetString(Item, Head, Tail - Head);
        if TrimStrings then begin
          Item := Trim(Item);
          Head := PChar(Item);
          if CharInSet(Head^, Quotes) then
            Item := Trim(AnsiExtractQuotedStr(Head, Head^));
        end;
        if not (SkipEmptyStrings and (Item = '')) then
          Strings.Append(Item);
      end;
      // If the last character in a string is a separator, then we need to mark
      // that another string exists, otherwise the next Inc(Tail) call will
      // place Tail^ at #0, we'll exit the while loop, and never know that there
      // was an empty string there to add.
      // --AAF
      StringExists := Tail^ <> #0;
      // Skip Separator
      if StringExists then
        Inc(Tail);
    end;
    // This can only happen if the very last character is a separator
    if StringExists and not SkipEmptyStrings then
      Strings.Append('');
  finally
    Strings.EndUpdate;
  end;
end;
于 2013-07-05T15:00:01.973 回答