2010-04-12 202 views
68

我試圖找到一個Delphi函數將輸入字符串拆分爲基於分隔符的字符串數組。我在谷歌上發現了很多,但似乎都有他們自己的問題,我還沒有能夠讓他們工作。將字符串拆分爲基於分隔符的字符串數組

我只需要分割一個字符串,如: "word:doc,txt,docx"到基於':'的數組中。結果將是 ['word', 'doc,txt,docx']

有沒有人有他們知道的功能?

謝謝

回答

71

可以使用TStrings.DelimitedText屬性來分割字符串

檢查該樣本

program Project28; 

{$APPTYPE CONSOLE} 

uses 
    Classes, 
    SysUtils; 

procedure Split(Delimiter: Char; Str: string; ListOfStrings: TStrings) ; 
begin 
    ListOfStrings.Clear; 
    ListOfStrings.Delimiter  := Delimiter; 
    ListOfStrings.StrictDelimiter := True; // Requires D2006 or newer. 
    ListOfStrings.DelimitedText := Str; 
end; 


var 
    OutPutList: TStringList; 
begin 
    OutPutList := TStringList.Create; 
    try 
    Split(':', 'word:doc,txt,docx', OutPutList) ; 
    Writeln(OutPutList.Text); 
    Readln; 
    finally 
    OutPutList.Free; 
    end; 
end. 

UPDATE

StrictDelimiter的說明,請參見本link

+18

不幸的是,在很多「老」的Delphi版本中存在一個錯誤(不確定哪個版本得到了修復),這會導致空格字符總是被**用作分隔符。所以小心處理! – Leo 2010-04-12 22:09:48

+12

是的。您需要將StrictDelimiter設置爲true,並且如果StrictDelimiter屬性在您的Delphi版本中不可用,請不要使用此技術!但如果是這樣,那麼這是非常有用的。 – 2010-04-12 22:24:47

+3

這不是一個錯誤,它是一個(令人討厭的)D1或D2設計決策方式。 CommaText應該用帶引號的空格來包圍任何字段。如果輸入在帶空格的字段周圍有雙引號,則結果是正確的。 – 2010-04-12 22:40:19

4

Here是爆炸功能的實現是在很多其他編程語言作爲標準功能可供選擇:

type 
    TStringDynArray = array of String; 

function Explode(const Separator, S: string; Limit: Integer = 0): TStringDynArray; 
var 
    SepLen: Integer; 
    F, P: PChar; 
    ALen, Index: Integer; 
begin 
    SetLength(Result, 0); 
    if (S = '') or (Limit < 0) then Exit; 
    if Separator = '' then 
    begin 
    SetLength(Result, 1); 
    Result[0] := S; 
    Exit; 
    end; 
    SepLen := Length(Separator); 
    ALen := Limit; 
    SetLength(Result, ALen); 

    Index := 0; 
    P := PChar(S); 
    while P^ <> #0 do 
    begin 
    F := P; 
    P := AnsiStrPos(P, PChar(Separator)); 
    if (P = nil) or ((Limit > 0) and (Index = Limit - 1)) then P := StrEnd(F); 
    if Index >= ALen then 
    begin 
     Inc(ALen, 5); 
     SetLength(Result, ALen); 
    end; 
    SetString(Result[Index], F, P - F); 
    Inc(Index); 
    if P^ <> #0 then Inc(P, SepLen); 
    end; 
    if Index < ALen then SetLength(Result, Index); 
end; 

使用範例:

var 
    res: TStringDynArray; 
begin 
    res := Explode(':', yourString); 
+2

在這段代碼中有一些奇怪的和潛在的非常低效的選擇來管理/預測結果的長度。通過逐步增加結果數組,增加了內存重新分配和分段的機會。更有效率的做法是設置一個初始長度儘可能大,即假設輸入字符串由50%分隔符字符串=長度(S)div(2 *長度(分隔符)組成),然後將其設置爲實際的數量項目完成後1分配後可能是一個截斷 – Deltics 2010-04-12 23:54:40

+0

你也沒有解釋Limit參數的目的我直觀地期望它設置一個最大數量的子字符串,實際上它似乎是約束檢測將子字符串轉換爲輸入字符串中第一個「Limit」字符數,這似乎毫無意義,因爲如果您需要這樣做,您可以簡單地通過所需子字符串的Copy()操作Explode()。子字符串會更加有用 – Deltics 2010-04-12 23:56:02

+0

@Deltics:沒有人聲稱這是一個高度優化的函數,沒有人要求,所以我有點不理解你的抱怨,但也許你是其中的一員誰優化了一切,無論是否有必要... – Leo 2010-04-14 11:28:57

12

爆炸()功能通過MEF提供的,但有幾個(其中我認爲一個bug修復的)差異類似:

type 
    TArrayOfString = array of String; 


    function SplitString(const aSeparator, aString: String; aMax: Integer = 0): TArrayOfString; 
    var 
    i, strt, cnt: Integer; 
    sepLen: Integer; 

    procedure AddString(aEnd: Integer = -1); 
    var 
     endPos: Integer; 
    begin 
     if (aEnd = -1) then 
     endPos := i 
     else 
     endPos := aEnd + 1; 

     if (strt < endPos) then 
     result[cnt] := Copy(aString, strt, endPos - strt) 
     else 
     result[cnt] := ''; 

     Inc(cnt); 
    end; 

    begin 
    if (aString = '') or (aMax < 0) then 
    begin 
     SetLength(result, 0); 
     EXIT; 
    end; 

    if (aSeparator = '') then 
    begin 
     SetLength(result, 1); 
     result[0] := aString; 
     EXIT; 
    end; 

    sepLen := Length(aSeparator); 
    SetLength(result, (Length(aString) div sepLen) + 1); 

    i  := 1; 
    strt := i; 
    cnt := 0; 
    while (i <= (Length(aString)- sepLen + 1)) do 
    begin 
     if (aString[i] = aSeparator[1]) then 
     if (Copy(aString, i, sepLen) = aSeparator) then 
     begin 
      AddString; 

      if (cnt = aMax) then 
      begin 
      SetLength(result, cnt); 
      EXIT; 
      end; 

      Inc(i, sepLen - 1); 
      strt := i + 1; 
     end; 

     Inc(i); 
    end; 

    AddString(Length(aString)); 

    SetLength(result, cnt); 
    end; 

差異:

  1. AMAX參數限制要返回的字符串的數量
  2. 如果輸入字符串被分隔符終止,則標稱的「空」最終字符串被視爲存在

例子:

SplitString(':', 'abc') returns  : result[0] = abc 

SplitString(':', 'a:b:c:') returns : result[0] = a 
              result[1] = b 
              result[2] = c 
              result[3] = <empty string> 

SplitString(':', 'a:b:c:', 2) returns: result[0] = a 
              result[1] = b 

它是尾部分隔和名義上的「空最後一個要素」,我認爲bug修復。 (我錯誤地建議輸入字符串最多可能包含50%的分隔符,但它當然可以包含100%的分隔符字符串,產生一個空元素數組) !)

31

德爾福2010年StrUtils.SplitString

+3

嗯,不是在我的Delphi 2010版本中(XMLDoc和Indy單元IdStrings中有一個SplitString例程,但這些都不做海報想要的,XMLDoc例程也不會通過單元接口公開)。 – Deltics 2010-04-13 21:06:03

+3

函數SplitString(const S,Delimiters:string):TStringDynArray;在StrUtils.pas中定義 – alex 2010-07-27 10:07:58

+0

我無法包含文件StrUtils.pas(即使存在)。 – truthseeker 2012-02-01 08:26:18

12

我總是用類似這樣的東西:

Uses 
    StrUtils, Classes; 

Var 
    Str, Delimiter : String; 
begin 
    // Str is the input string, Delimiter is the delimiter 
    With TStringList.Create Do 
    try 
    Text := ReplaceText(S,Delim,#13#10); 

    // From here on and until "finally", your desired result strings are 
    // in strings[0].. strings[Count-1) 

    finally 
    Free; //Clean everything up, and liberate your memory ;-) 
    end; 

end; 
+2

適用於較老的Delphi版本的用戶的絕佳解決方案。 – Wolf 2016-11-10 11:37:13

+0

C++ Builder 6用戶:相應的函數是'Strutils :: AnsiReplaceText' – Wolf 2016-11-10 11:59:38

+0

非常簡單。在Delphi 7中使用:'list.Text:= AnsiReplaceStr(source,delimiter,#13#10);'。 – AlainD 2017-11-10 17:57:01

54

無需工程改造Split功能。它已經存在,請參閱:Classes.ExtractStrings

使用它的方式如下:

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    Classes; 

var 
    List: TStrings; 
begin 
    List := TStringList.Create; 
    try 
    ExtractStrings([':'], [], PChar('word:doc,txt,docx'), List); 
    WriteLn(List.Text); 
    ReadLn; 
    finally 
    List.Free; 
    end; 
end. 

而且完全回答這個問題; List表示與元件的所需的陣列:

List[0] = 'word' 
List[1] = 'doc,txt,docx' 
+11

ExtractStrings非常不靈活:「回車符,換行符和引號字符(單或雙)始終被視爲分隔符。」;和「注意:ExtractStrings不會將空字符串添加到列表中。「 – awmross 2013-03-15 03:27:07

+0

問題不是設計一個'split'函數,而是需要一個'TStrings'對象。而由於僵化(@awmross)的提及,我更喜歡[Frank的解決方案](http:// stackoverflow。 com/a/2628241/2932052) – Wolf 2016-11-10 12:16:29

6

爆炸是非常高的速度的功能,源alhoritm從字符串列表組分獲得。 我使用下一個測試爆炸: 爆炸134217733字節的數據,我得到19173962元素,工作時間:2984毫秒。

Implode是非常低速的功能,但我寫它很容易。

{ ****************************************************************************** } 
{ Explode/Implode (String <> String array)          } 
{ ****************************************************************************** } 
function Explode(S: String; Delimiter: Char): Strings; overload; 
var I, C: Integer; P, P1: PChar; 
begin 
    SetLength(Result, 0); 
    if Length(S) = 0 then Exit; 
    P:=PChar(S+Delimiter); C:=0; 
    while P^ <> #0 do begin 
     P1:=P; 
     while (P^ <> Delimiter) do P:=CharNext(P); 
     Inc(C); 
     while P^ in [#1..' '] do P:=CharNext(P); 
     if P^ = Delimiter then begin 
      repeat 
      P:=CharNext(P); 
      until not (P^ in [#1..' ']); 
     end; 
    end; 
    SetLength(Result, C); 
    P:=PChar(S+Delimiter); I:=-1; 
    while P^ <> #0 do begin 
     P1:=P; 
     while (P^ <> Delimiter) do P:=CharNext(P); 
     Inc(I); SetString(Result[I], P1, P-P1); 
     while P^ in [#1..' '] do P:=CharNext(P); 
     if P^ = Delimiter then begin 
      repeat 
      P:=CharNext(P); 
      until not (P^ in [#1..' ']); 
     end; 
    end; 
end; 

function Explode(S: String; Delimiter: Char; Index: Integer): String; overload; 
var I: Integer; P, P1: PChar; 
begin 
    if Length(S) = 0 then Exit; 
    P:=PChar(S+Delimiter); I:=1; 
    while P^ <> #0 do begin 
     P1:=P; 
     while (P^ <> Delimiter) do P:=CharNext(P); 
     SetString(Result, P1, P-P1); 
     if (I <> Index) then Inc(I) else begin 
      SetString(Result, P1, P-P1); Exit; 
     end; 
     while P^ in [#1..' '] do P:=CharNext(P); 
     if P^ = Delimiter then begin 
      repeat 
      P:=CharNext(P); 
      until not (P^ in [#1..' ']); 
     end; 
    end; 
end; 

function Implode(S: Strings; Delimiter: Char): String; 
var iCount: Integer; 
begin 
    Result:=''; 
    if (Length(S) = 0) then Exit; 
    for iCount:=0 to Length(S)-1 do 
    Result:=Result+S[iCount]+Delimiter; 
    System.Delete(Result, Length(Result), 1); 
end; 
+2

這不能編譯:「字符串」不是一個類型 – NGLN 2016-11-10 16:32:57

2

絕代碼庫提供了增強的StringList具有內置分割功能,即能夠既添加和替換現有的文本。它還提供參考計數界面。所以這可以用於舊的Delphi版本,沒有SplitStrings,也沒有仔細的和有點繁瑣的股票TStringList的定製,只使用指定的分隔符。

對於線像Dog 5 4 7一個例子給定的文本文件,可以使用它們解析:

var slF, slR: IJclStringList; ai: TList<integer>; s: string; i: integer; 
    action: procedure(const Name: string; Const Data: array of integer); 

slF := TJclStringList.Create; slF.LoadFromFile('some.txt'); 
slR := TJclStringList.Create; 
for s in slF do begin 
    slR.Split(s, ' ', true); 
    ai := TList<Integer>.Create; 
    try 
     for i := 1 to slR.Count - 1 do 
      ai.Add(StrToInt(slR[i])); 
     action(slR[0], ai.ToArray); 
    finally ai.Free; end; 
end; 

http://wiki.delphi-jedi.org/wiki/JCL_Help:[email protected]@[email protected]

+0

更復雜的例子:http://stackoverflow.com/a/14649862/976391 – 2013-02-26 16:53:36

4

你可以讓自己的函數返回字符串在tarray:

function mySplit(input: string): TArray<string>; 
var 
    delimiterSet: array [0 .. 0] of char; 
    // split works with char array, not a single char 
begin 
    delimiterSet[0] := '&'; // some character 
    result := input.Split(delimiterSet); 
end; 
4

我寫了這個函數,它通過特定的分隔符返回分隔字符串的鏈表。純粹的免費pascal沒有模塊。

Program split_f; 

type 
    PTItem = ^TItem; 
    TItem = record 
     str : string; 
     next : PTItem; 
    end; 

var 
    s : string; 
    strs : PTItem; 

procedure split(str : string;delim : char;var list : PTItem); 
var 
    i : integer; 
    buff : PTItem; 
begin 
    new(list); 
    buff:= list; 
    buff^.str:=''; 
    buff^.next:=nil; 

    for i:=1 to length(str) do begin 
     if (str[i] = delim) then begin 
      new(buff^.next); 
      buff:=buff^.next; 
      buff^.str := ''; 
      buff^.next := nil; 
     end 
     else 
     buff^.str:= buff^.str+str[i]; 
    end; 
end; 

procedure print(var list:PTItem); 
var 
    buff : PTItem; 
begin 
    buff := list; 
    while buff<>nil do begin 
     writeln(buff^.str); 
     buff:= buff^.next; 
    end; 
end; 

begin 

    s := 'Hi;how;are;you?'; 

    split(s, ';', strs); 
    print(strs); 


end. 
2

這將解決你的問題

interface 
    TArrayStr = Array Of string; 

implementation 

function SplitString(Text: String): TArrayStr; 
var 
    intIdx: Integer; 
    intIdxOutput: Integer; 
const 
    Delimiter = ';'; 
begin 
    intIdxOutput := 0; 
    SetLength(Result, 1); 
    Result[0] := ''; 

    for intIdx := 1 to Length(Text) do 
    begin 
     if Text[intIdx] = Delimiter then 
     begin 
     intIdxOutput := intIdxOutput + 1; 
     SetLength(Result, Length(Result) + 1); 
     end 
     else 
     Result[intIdxOutput] := Result[intIdxOutput] + Text[intIdx]; 
    end; 
end; 
+0

你可以給一些解釋什麼代碼呢?謝謝 – Paco 2015-01-30 14:48:02

+0

它通過傳遞的字符串尋找定界符常量,找不到時,與數組上的當前位置連接,找到時,它跳轉到動態數組中的下一個位置 – Dennis 2015-02-02 10:12:47

25

使用SysUtils.TStringHelper.Split功能,在Delphi XE3介紹:

var 
    MyString: String; 
    Splitted: TArray<String>; 
begin 
    MyString := 'word:doc,txt,docx'; 
    Splitted := MyString.Split([':']); 
end. 

這將與給定的分隔符字符串分割成字符串數組。

+0

只適用於utf-8句子 – Alper 2015-10-14 08:02:44

5
var 
    su : string;  // What we want split 
    si : TStringList; // Result of splitting 
    Delimiter : string; 
    ... 
    Delimiter := ';'; 
    si.Text := ReplaceStr(su, Delimiter, #13#10); 

線在SI列表將包含分裂字符串。

0

*

//Basic functionality of a TStringList solves this: 


uses Classes //TStringList 
    ,types //TStringDynArray 
    ,SysUtils //StringReplace() 
    ; 

.... 

//-------------------------------------------------------------------------- 
function _SplitString(const s:string; const delimiter:Char):TStringDynArray; 
    var sl:TStringList; 
     i:integer; 
    begin 
    sl:=TStringList.Create; 

    //separete delimited items by sLineBreak;TStringlist will do the job: 
    sl.Text:=StringReplace(s,delimiter,sLineBreak,[rfReplaceAll]); 

    //return the splitted string as an array: 
    setlength(Result,sl.count); 
    for i:=0 to sl.Count-1 
    do Result[i]:=sl[i]; 

    sl.Free; 
    end; 



//To split a FileName (last item will be the pure filename itselfs): 

function _SplitPath(const fn:TFileName):TStringDynArray; 
    begin 
    result:=_SplitString(fn,'\'); 
    end; 

*

+3

這比接受的答案好嗎? – MartynA 2017-01-20 22:54:24

0

NGLG答案的基礎https://stackoverflow.com/a/8811242/6619626您可以使用以下功能:

type 
OurArrayStr=array of string; 

function SplitString(DelimeterChars:char;Str:string):OurArrayStr; 
var 
seg: TStringList; 
i:integer; 
ret:OurArrayStr; 
begin 
    seg := TStringList.Create; 
    ExtractStrings([DelimeterChars],[], PChar(Str), seg); 
    for i:=0 to seg.Count-1 do 
    begin 
     SetLength(ret,length(ret)+1); 
     ret[length(ret)-1]:=seg.Strings[i]; 
    end; 
    SplitString:=ret; 
    seg.Free; 
end; 

它適用於所有版本的Delphi。