2016-03-07 32 views
9

Ich verwende die StrUtils in, um eine Zeichenfolge in eine TStringDynArray zu teilen, aber die Ausgabe war nicht wie erwartet. Ich werde versuchen, das Problem zu erklären:StrUtils.SplitString funktioniert nicht wie erwartet

ich eine Zeichenfolge haben str: 'a'; 'b'; 'c'
Nun rief ich StrUtils.SplitString(str, '; '); die Zeichenfolge zu spalten und ich erwartete ein Array mit drei Elementen: 'a', 'b', 'c'

Aber was ich get ist ein Array mit fünf Elementen: 'a', '', 'b', '', 'c'.
Wenn ich mit nur ';' statt '; ' geteilt habe, bekomme ich drei Elemente mit einem führenden Leerzeichen.

Also warum bekomme ich leere Saiten in meiner ersten Lösung?

+3

Lesen Sie die Dokumentation. Vielleicht nicht wie erwartet, aber es funktioniert wie dokumentiert. –

+0

Diese Frage enthält einige Vorschläge zum Aufteilen einer Zeichenfolge basierend auf einer Zeichenfolge mit mehreren Zeichen (was Sie erwartet haben), aber die meisten davon funktionieren mit Zeichenfolgenlisten, nicht mit Arrays: http://stackoverflow.com/questions/15424293/How-to-Split-String-by-a-Multi-Zeichen-Delimiter – quasoft

Antwort

15

Diese Funktion wurde entwickelt, um aufeinanderfolgende Separatoren nicht zusammenzuführen. Betrachten wir zum Beispiel die folgende Zeichenfolge auf Kommata Aufspalten:

foo,,bar 

Was würden Sie SplitString('foo,,bar', ',') zurückkehren erwarten? Wären Sie auf der Suche nach ('foo', 'bar') oder sollte die Antwort ('foo', '', 'bar') sein? Es ist nicht a priori klar, was richtig ist, und verschiedene Anwendungsfälle könnten unterschiedliche Ausgaben erfordern.

In Ihrem Fall haben Sie zwei Trennzeichen angegeben: ';' und ' '. Das bedeutet, dass

'a'; 'b' 

Splits bei ';' und wieder bei ' '. Zwischen diesen beiden Begrenzern gibt es nichts, und daher wird eine leere Zeichenfolge zwischen 'a' und 'b' zurückgegeben.

Die Methode Split aus der string helper, die in XE3 eingeführt wurde, hat einen TStringSplitOptions Parameter. Wenn Sie ExcludeEmpty für diesen Parameter übergeben, werden aufeinanderfolgende Trennzeichen als einzelnes Trennzeichen behandelt. Dieses Programm:

{$APPTYPE CONSOLE} 

uses 
    System.SysUtils; 

var 
    S: string; 

begin 
    for S in '''a''; ''b''; ''c'''.Split([';', ' '], ExcludeEmpty) do begin 
    Writeln(S); 
    end; 
end. 

Ausgänge:

 
'a' 
'b' 
'c' 

Aber Sie haben nicht das für Sie in XE2 zur Verfügung, so ich glaube, Sie werden Ihre eigene Split-Funktion haben, rollen. Was wie folgt aussehen könnte:

function IsSeparator(const C: Char; const Separators: string): Boolean; 
var 
    sep: Char; 
begin 
    for sep in Separators do begin 
    if sep=C then begin 
     Result := True; 
     exit; 
    end; 
    end; 
    Result := False; 
end; 

function Split(const Str, Separators: string): TArray<string>; 
var 
    CharIndex, ItemIndex: Integer; 
    len: Integer; 
    SeparatorCount: Integer; 
    Start: Integer; 
begin 
    len := Length(Str); 
    if len=0 then begin 
    Result := nil; 
    exit; 
    end; 

    SeparatorCount := 0; 
    for CharIndex := 1 to len do begin 
    if IsSeparator(Str[CharIndex], Separators) then begin 
     inc(SeparatorCount); 
    end; 
    end; 

    SetLength(Result, SeparatorCount+1); // potentially an over-allocation 
    ItemIndex := 0; 
    Start := 1; 
    CharIndex := 1; 
    for CharIndex := 1 to len do begin 
    if IsSeparator(Str[CharIndex], Separators) then begin 
     if CharIndex>Start then begin 
     Result[ItemIndex] := Copy(Str, Start, CharIndex-Start); 
     inc(ItemIndex); 
     end; 
     Start := CharIndex+1; 
    end; 
    end; 

    if len>Start then begin 
    Result[ItemIndex] := Copy(Str, Start, len-Start+1); 
    inc(ItemIndex); 
    end; 

    SetLength(Result, ItemIndex); 
end; 

Natürlich setzt das alles voraus, dass ein Leerzeichen als Trennzeichen dienen soll. Sie haben im Code danach gefragt, aber vielleicht möchten Sie nur ; als Trennzeichen fungieren. In diesem Fall möchten Sie wahrscheinlich ';' als Trennzeichen übergeben und die zurückgegebenen Zeichenfolgen trimmen.

+0

danke für diese detaillierte Erklärung! –

14

SplitString ist definiert als

function SplitString(const S, Delimiters: string): TStringDynArray; 

Man hätte gedacht, dass Delimiters einzelne Trennzeichenfolge für das Aufspalten Zeichenfolge verwendet bezeichnen, aber es bezeichnet eigentlich einzelner Zeichen zu Split-String gesetzt. Jedes Zeichen in Delimiters Zeichenfolge wird als eines der möglichen Trennzeichen verwendet.

SplitString

Splits eine Zeichenfolge in verschiedene Teile durch die angegebenen delimiter Zeichen begrenzt. SplitString teilt eine Zeichenfolge in verschiedene Teile , die durch das angegebene Trennzeichen Zeichen begrenzt sind. S ist die Zeichenfolge, die geteilt werden soll. Trennzeichen ist eine Zeichenfolge, die Zeichen definiert als Trennzeichen enthält.

+1

Ich nehme an, sie hätten es "Delimiter" (Singular) dann nicht "Delimiters" genannt. FWIW, TStringHelper hat in späteren Versionen eine Version von 'Split', die auch String als Delimiter verwendet, nicht nur Zeichen, sondern leider nicht in XE2. –

+0

@RudyVelthuis Einverstanden. Aber feine Grenze zwischen Delimiter und Delimiters Bedeutung kann verloren gehen, wenn Sie nicht englischer Muttersprachler sind. Abgesehen davon wird bei geteilten Operationen in anderen Sprachen normalerweise ein vollständiges, genaues Trennzeichen angegeben, daher ist diese Delphi-Implementierung auch unter diesem Aspekt ziemlich verwirrend. –

+0

@RudyVelthuis, Aber Split hat auch eine eigene Reihe von Macken: http://StackOverflow.com/Questions/28410901/string-split-works-strange-when-last-value-istempty –

5

Es ist, weil der zweite Parameter von SplitString eine Liste von einzelnen Zeichen Trennzeichen ist, so '; "bedeutet geteilt bei a"; ODER bei '' aufgeteilt. Also ist die Saite bei jedem ';' und in jedem Raum und zwischen dem ';' und das '' da ist nichts, daher die leeren Saiten.

Verwandte Themen