2013-07-04 40 views
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次在存儲器:
如何將字符串複製到另一個字符在第一個位置留下空白字符?

在方法調用(通過複製)
在插入(「」,S,1)
在級聯:S:= S +分隔符;

我考慮在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','')的字符串列表。

+0

實例中使用了什麼分隔符 - ';'或空間? – MBo

+0

我相信根本不需要移動字符串。使它成爲常量參數,並用簡單的有限自動機算法處理數據,其初始狀態取決於第一個符號。 – MBo

+0

@MBo我不能想到一個算法,不需要修改字符串就可以做我所需要的。如果你有東西請給我看,我會欣賞 – EProgrammerNotFound

回答

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; 

依此類推。我會留給你來打磨它。

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; 
+0

非常好,這個代碼在任何情況下都比我的表現要好。 [1] – EProgrammerNotFound

相關問題